Skip to content

Instantly share code, notes, and snippets.

@ryancdotorg
Created Aug 23, 2021
Embed
What would you like to do?
A simple command line random number generator that can compile on x86_64 without libc
// gcc -Os -DNO_LIBC -fno-builtin -static -nostdlib -fomit-frame-pointer rand.c -o rand
// gcc -O3 rand.c -o rand
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/random.h>
#include <inttypes.h>
#ifdef NO_LIBC
static inline void __attribute__ ((noreturn)) _sys_exit(int n) {
register int rdi asm("rdi") = n;
asm volatile(
"xor %%eax, %%eax\n"
"add %0, %%al\n"
"syscall\n"
:
: "n"(SYS_exit), "r"(rdi)
: "rax", "rcx", "r11", "memory"
);
}
static ssize_t _sys_write(int _fd, const void *_buf, size_t _count) {
register int ret asm("rax") = 0;
register int fd asm("rdi") = _fd;
register const void *buf asm("rsi") = _buf;
register size_t count asm("rdx") = _count;
asm volatile(
"xor %%eax, %%eax\n"
"add %1, %%al\n"
"syscall\n"
: "=r"(ret)
: "n"(SYS_write), "r"(fd), "r"(buf), "r"(count)
: "rcx", "r11", "memory"
);
return ret;
}
static ssize_t _sys_getrandom(void *_buf, size_t _buflen, unsigned int _flags) {
register ssize_t ret asm("rax");
register void *buf asm("rdi") = _buf;
register size_t buflen asm("rsi") = _buflen;
register unsigned int flags asm("rdx") = flags;
asm volatile(
"xor %%eax, %%eax\n"
"add %1, %%ax\n"
"syscall\n"
: "=r"(ret)
: "n"(SYS_getrandom), "r"(buf), "r"(buflen), "r"(flags)
: "rcx", "r11"
);
return ret;
}
#define _write _sys_write
#define _getrandom _sys_getrandom
#else
#define _write write
#define _getrandom getrandom
#endif
static uint64_t randint(uint64_t min, uint64_t max) {
uint64_t result, range, mod, top = 0xffffffffffffffffULL;
range = (max - min) + 1;
mod = top % range;
if (mod != range - 1) top -= mod;
do {
_getrandom(&result, sizeof(result), 0);
} while (result > top);
if (range) result = min + result % range;
return result;
}
static void write_u64(int fd, uint64_t n) {
char buf[32];
char c;
int p = sizeof(buf) - 1;
buf[p--] = '\n';
do {
buf[p--] = '0' + (n % 10);
n /= 10;
} while (n > 0);
int len = (sizeof(buf) - p) - 1;
_write(fd, buf + p + 1, len);
}
static int parse_u64(char *s, uint64_t *n) {
*n = 0;
char d;
int p = 0;
for (;;) {
d = s[p++];
#if 1
if (d == 0) {
return 0;
} else if (d >= '0' && d <= '9') {
d -= '0';
} else {
return -1;
}
#else
switch (s[p]) {
case '0': d = 0; break; case '1': d = 1; break;
case '2': d = 2; break; case '3': d = 3; break;
case '4': d = 4; break; case '5': d = 5; break;
case '6': d = 6; break; case '7': d = 7; break;
case '8': d = 8; break; case '9': d = 9; break;
case '\0': return 0;
default: return -1;
}
#endif
if (*n > 1844674407370955161ULL) {
return -1;
} else if (*n == 1844674407370955161ULL && d > 5) {
return -1;
}
*n *= 10ULL;
*n += d;
}
}
int __attribute__ ((noinline)) main(int argc, char *argv[]) {
uint64_t x, min = 0, max;
if (argc == 1) {
max = 32767;
} else if (argc == 2) {
parse_u64(argv[1], &max);
} else if (argc == 3) {
parse_u64(argv[1], &min);
parse_u64(argv[2], &max);
} else {
return -1;
}
x = randint(min, max);
write_u64(1, x);
return 0;
}
#ifdef NO_LIBC
// minimal entry point
void _start() {
int argc; char **argv;
asm volatile(
"mov 0(%%rsp), %0\n"
"mov %%rsp, %1\n"
"add $8, %1\n"
: "=r"(argc), "=r"(argv) :: "memory"
);
_sys_exit(main(argc, argv));
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment