// Unused primitive identifiers // // 02 07 // 16 // 22 25 // 34 37 // 44 48 49 // 51 57 // 64 65 69 // 70 73 77 79 // 81 83 // 90 94 96 #include <support.h> static int command_argc; static const char **command_argv; #define static_assert(c) _Static_assert(c, #c) typedef _Bool bool; typedef int pid_t; typedef unsigned long rlim_t; struct rlimit { rlim_t rlim_cur; rlim_t rlim_max; }; typedef unsigned int mode_t; typedef long off_t; typedef unsigned long dev_t; typedef unsigned long ino_t; typedef unsigned long nlink_t; typedef unsigned uid_t; typedef unsigned gid_t; typedef long blksize_t; typedef long blkcnt_t; typedef long time_t; struct timespec { time_t tv_sec; long tv_nsec; }; struct stat { dev_t st_dev; ino_t st_ino; nlink_t st_nlink; mode_t st_mode; uid_t st_uid; gid_t st_gid; unsigned pad1; dev_t st_rdev; off_t st_size; blksize_t st_blksize; blkcnt_t st_blocks; struct timespec st_atim; struct timespec st_mtim; struct timespec st_ctim; long pad2[3]; }; #define false 0 #define true 1 #define RLIMIT_STACK 3 #define RLIM_INFINITY -1 #define O_RDONLY 00 #define O_WRONLY 01 #define O_RDWR 02 #define O_CREAT 0100 #define O_TRUNC 01000 #define O_CLOEXEC 02000000 #define PROT_READ 1 #define PROT_WRITE 2 #define PROT_EXEC 4 #define SYS_read 0 #define SYS_write 1 #define SYS_open 2 #define SYS_close 3 #define SYS_stat 4 #define SYS_fstat 5 #define SYS_mmap 9 #define SYS_munmap 11 #define SYS_getpid 39 #define SYS_execve 59 #define SYS_exit 60 #define SYS_getcwd 79 #define SYS_getrlimit 97 #define SYS_setrlimit 160 #define SYS_exit_group 231 #define SYS_epoll_wait 232 #define SYS_epoll_ctl 233 #define SYS_epoll_create1 291 long syscall0(long n); long syscall1(long n, long a1); long syscall2(long n, long a1, long a2); long syscall3(long n, long a1, long a2, long a3); long syscall4(long n, long a1, long a2, long a3, long a4); long syscall5(long n, long a1, long a2, long a3, long a4, long a5); long syscall6(long n, long a1, long a2, long a3, long a4, long a5, long a6); __asm__( ".text\n" "syscall0:\n" " mov %rdi, %rax\n" " syscall\n" " retq\n" "syscall1:\n" " mov %rdi, %rax\n" " mov %rsi, %rdi\n" " syscall\n" " retq\n" "syscall2:\n" " mov %rdi, %rax\n" " mov %rsi, %rdi\n" " mov %rdx, %rsi\n" " syscall\n" " retq\n" "syscall3:\n" " mov %rdi, %rax\n" " mov %rsi, %rdi\n" " mov %rdx, %rsi\n" " mov %rcx, %rdx\n" " syscall\n" " retq\n" "syscall4:\n" " mov %rdi, %rax\n" " mov %rsi, %rdi\n" " mov %rdx, %rsi\n" " mov %rcx, %rdx\n" " mov %r8, %r10\n" " syscall\n" " retq\n" "syscall5:\n" " mov %rdi, %rax\n" " mov %rsi, %rdi\n" " mov %rdx, %rsi\n" " mov %rcx, %rdx\n" " mov %r8, %r10\n" " mov %r9, %r8\n" " syscall\n" " retq\n" "syscall6:\n" " mov %rdi, %rax\n" " mov %rsi, %rdi\n" " mov %rdx, %rsi\n" " mov %rcx, %rdx\n" " mov %r8, %r10\n" " mov %r9, %r8\n" " mov 8(%rsp), %r9\n" " syscall\n" " retq\n" ".global _start\n" "_start:\n" " xor %rbp, %rbp\n" " mov (%rsp), %rdi\n" " lea 8(%rsp), %rsi\n" " call main\n" " mov %rax, %rdi\n" " mov $60, %rax\n" " syscall\n" ); uint16_t htole16(uint16_t x) { return x; } uint32_t htole32(uint32_t x) { return x; } uint64_t htole64(uint64_t x) { return x; } uint16_t le16toh(uint16_t x) { return x; } uint32_t le32toh(uint32_t x) { return x; } uint64_t le64toh(uint64_t x) { return x; } int execve(const char *path, const char **argv, const char **env) { return syscall3(SYS_execve, (long)path, (long)argv, (long)env); } _Noreturn void exit(int status) { syscall1(SYS_exit_group, status); for (;;) syscall1(SYS_exit, status); } int getcwd(char *buf, size_t size) { return syscall2(SYS_getcwd, (long)buf, size); } pid_t getpid(void) { return syscall0(SYS_getpid); } int getrlimit(int resource, struct rlimit *rlim) { int r = syscall2(SYS_getrlimit, resource, (long)rlim); if (r < 0) return -1; return 0; } int setrlimit(int resource, const struct rlimit *rlim) { int r = syscall2(SYS_setrlimit, resource, (long)rlim); if (r < 0) return -1; return 0; } int read(int fd, void *buf, size_t count) { return syscall3(SYS_read, fd, (long)buf, (long)count); } int write(int fd, const void *buf, size_t count) { return syscall3(SYS_write, fd, (long)buf, (long)count); } int close(int fd) { return syscall1(SYS_close, fd); } int open(const char *filename, int flags, mode_t mode) { return syscall3(SYS_open, (long)filename, flags, mode); } int fstat(int fd, struct stat *stat) { return syscall2(SYS_fstat, fd, (long)stat); } long mmap(void *pref, long size, long prot, long flags, long fd, long offset) { return syscall6(SYS_mmap, (long)pref, size, prot, flags, fd, offset); } long munmap(void *bytes, long size) { return syscall2(SYS_munmap, (long)bytes, size); } int epoll_create1(int flags) { return syscall1(SYS_epoll_create1, flags); } int epoll_ctl(int epoll_fd, int op, int fd, void *event) { return syscall4(SYS_epoll_ctl, epoll_fd, op, fd, (long)event); } int epoll_wait(int epoll_fd, void *events, int num_events, int timeout) { return syscall4(SYS_epoll_wait, epoll_fd, (long)events, num_events, timeout); } size_t strlen(const char *s) { size_t size = 0; while (s[size] != '\0') ++size; return size; } void * memcpy(void *dest, const void *src, size_t n) { char *d = dest; const char *s = src; if (n > 0 && d != s) for (size_t i = 0; i < n; i++) d[i] = s[i]; return dest; } void * memmove(void *dest, const void *src, size_t n) { char *d = dest; const char *s = src; if (n > 0 && d != s) { if (s < d && d < s + n) for (size_t i = 1; i <= n; i++) d[n - i] = s[n - i]; else for (size_t i = 0; i < n; i++) d[i] = s[i]; } return dest; } void * memset(void *dest, int c, size_t n) { char *d = dest; for (size_t i = 0; i < n; i++) d[i] = c; return dest; } typedef uint32_t value; static_assert(_Alignof(value) <= 4); struct closure { void *native_code; uint16_t num_params; uint16_t env_size; value env_items[]; }; struct tuple { uint16_t num_items; uint16_t layout; value items[]; }; struct variant { uint16_t label; value item; }; #define CHUNK_NUM_BYTES_MAX 0x3fffffff #define CHUNK_RO 1 #define CHUNK_RW 2 struct chunk { uint32_t header; char bytes[]; }; static_assert(_Alignof(struct closure) <= 8); static_assert(_Alignof(struct tuple) <= 4); static_assert(_Alignof(struct variant) <= 4); static_assert(_Alignof(struct chunk) <= 4); #define TAG_HEAP_CLOSURE 0x1 #define TAG_HEAP_TUPLE 0x3 #define TAG_HEAP_VARIANT 0x5 #define TAG_HEAP_CHUNK 0x7 #define TAG_STATIC_CHUNK_RO 0x9 #define TAG_IMMEDIATE_BOOLEAN 0x0f #define TAG_IMMEDIATE_TUPLE 0x1f #define TAG_IMMEDIATE_VARIANT 0x2f #define TAG_IMMEDIATE_CHUNK 0x3f #define empty_tuple TAG_IMMEDIATE_TUPLE #define value_true TAG_IMMEDIATE_BOOLEAN #define value_false (TAG_IMMEDIATE_BOOLEAN | 0x100) #define INTEGER_MIN (INT32_MIN / 2) #define INTEGER_MAX (INT32_MAX / 2) static_assert(CHUNK_NUM_BYTES_MAX <= INTEGER_MAX); #define NUM_MEMORY_MAPPINGS 16 static struct { void *bytes; size_t size; int prot; } memory_mappings[NUM_MEMORY_MAPPINGS]; static void err_print_line(const char *s) { write(2, s, strlen(s)); write(2, "\n", 1); } static _Noreturn void halt(void) { exit(1); } static _Noreturn void die(const char *s) { err_print_line(s); halt(); } // The Heap // // The heap is a single contiguous memory region in which value // representation data is stored. // // The size of the heap for a given program is fixed at compile time and // is at most pow(2, 30) bytes (1 GiB). // // All objects allocated in the heap have 4-byte, 8-byte, or 16-byte // alignment. Each object allocated in the heap has an associated heap // identifier, established by heap_alloc. The heap_access function provides // the means for obtaining the native address of an object with a given heap // identifier. // // A region is established by saving the value of heap_top into a local // variable. A region is freed by restoring heap_top to the saved value. static _Alignas(16) char heap_bytes[256 * 1024 * 1024]; const uint32_t heap_limit = sizeof(heap_bytes) / 4; uint32_t heap_top = 0; // heap_access // // Parameters: // id - A heap object identifier produced by heap_alloc. static void * heap_access(uint32_t id) { return &heap_bytes[4 * id]; } // heap_alloc // // Parameters: // align - The address alignment constraint. It must be 1, 2, 4, 8, or 16. // size - The number of bytes required, which must be greater than zero. static uint32_t heap_alloc(int align, size_t size) { uint32_t c = (align - 1) / 4; uint32_t id = (heap_top + c) & ~c; if (id > heap_limit || size > 4 * (heap_limit - id)) die("Failed to allocate memory."); heap_top = id + (size + 3) / 4; return id; } static value value_make_box(unsigned int tag, uint32_t id) { return (id << 4) | tag; } static void * value_unbox(value x) { return heap_access(x >> 4); } static bool value_has_tag(value x, unsigned int mask, unsigned int tag) { return (x & mask) == tag; } static struct closure * closure_unbox(value closure) { if (!value_has_tag(closure, 0xf, TAG_HEAP_CLOSURE)) die("Value is not a function."); return value_unbox(closure); } static struct tuple * tuple_unbox(value tuple) { if (!value_has_tag(tuple, 0xf, TAG_HEAP_TUPLE)) die("Value is not a tuple."); return value_unbox(tuple); } static struct variant * variant_unbox(value variant) { if (!value_has_tag(variant, 0xf, TAG_HEAP_VARIANT)) die("Value is not a variant."); return value_unbox(variant); } static bool value_is_number(value v) { return (v & 1) == 0; } static value integer_encode(int32_t n) { if (n < INTEGER_MIN || INTEGER_MAX < n) die("Number is out of range."); return n << 1; } static int32_t integer_decode(value v) { if (!value_is_number(v)) die("Value is not a number."); #ifdef __GNUC__ int32_t n = v; // "implementation-defined" behaviour, according to C11. return n >> 1; // "implementation-defined" behaviour, according to C11. #else #error "Need validation of implementation-defined behaviour." #endif } static value boolean_encode(bool b) { return b ? value_true : value_false; } static int global_chunk_decode(value chunk) { if (!value_has_tag(chunk, 0xff, TAG_IMMEDIATE_CHUNK)) die("Value is not a global chunk."); return chunk >> 8; } static value global_chunk_encode(int i) { return (i << 8) | TAG_IMMEDIATE_CHUNK; } static uint32_t chunk_header(int perm, uint32_t num_bytes) { return ((perm == CHUNK_RW) ? 0x80000000 : 0) | num_bytes; } static int chunk_perm(const struct chunk *chunk) { return (chunk->header & 0x80000000) ? CHUNK_RW : CHUNK_RO; } static uint32_t chunk_num_bytes(const struct chunk *chunk) { return (chunk->header & 0x7fffffff); } static struct chunk * chunk_unbox(value chunk) { if (value_has_tag(chunk, 0xf, TAG_STATIC_CHUNK_RO)) { return (struct chunk *)&constant_bytes[4 * (chunk >> 4)]; } else if (value_has_tag(chunk, 0xf, TAG_HEAP_CHUNK)) { return value_unbox(chunk); } die("Value is not a chunk."); } static void * chunk_access(value chunk, int perm, value i_value, unsigned num_bytes) { int64_t i = integer_decode(i_value); if (value_has_tag(chunk, 0xff, TAG_IMMEDIATE_CHUNK)) { int j = global_chunk_decode(chunk); if (memory_mappings[j].size == 0) die("Global chunk is not mapped."); if (i < 0 || memory_mappings[j].size < i + num_bytes) die("Chunk access is out of bounds."); if (perm == CHUNK_RW && !(memory_mappings[j].prot & PROT_WRITE)) die("Chunk is read-only."); return memory_mappings[j].bytes + i; } else { struct chunk *chunk_rep = chunk_unbox(chunk); if (i < 0 || chunk_num_bytes(chunk_rep) < i + num_bytes) die("Chunk access is out of bounds."); if (perm == CHUNK_RW && chunk_perm(chunk_rep) == CHUNK_RO) die("Chunk is read-only."); return &chunk_rep->bytes[i]; } die("Value is not a chunk."); } static value string_make(uint32_t num_bytes, const char *bytes) { if (CHUNK_NUM_BYTES_MAX < num_bytes) die("String is too big."); size_t align = _Alignof(struct chunk); size_t size = sizeof(struct chunk) + num_bytes; uint32_t id = heap_alloc(align, size); struct chunk *chunk_rep = heap_access(id); chunk_rep->header = chunk_header(CHUNK_RO, num_bytes); if (bytes != NULL) memmove(chunk_rep->bytes, bytes, num_bytes); return value_make_box(TAG_HEAP_CHUNK, id); } static struct chunk * string_unbox(value string) { struct chunk *chunk = chunk_unbox(string); if (chunk_perm(chunk) != CHUNK_RO) die("Value is not a string."); return chunk; } static char * string_bytes(value string) { return string_unbox(string)->bytes; } static uint32_t string_length(value string) { return chunk_num_bytes(string_unbox(string)); } static const char * zero_terminated(value s) { uint32_t len = string_length(s); char *z = heap_access(heap_alloc(1, len + 1)); memmove(z, string_bytes(s), len); z[len] = '\0'; return z; } #ifdef __GNUC__ // Implementation-defined behaviour, according to C11. static int8_t from_uint8(uint8_t u) { return (int8_t)u; } static int16_t from_uint16(uint16_t u) { return (int16_t)u; } static int32_t from_uint32(uint32_t u) { return (int32_t)u; } static int64_t from_uint64(uint64_t u) { return (int64_t)u; } #else #error "Need validation of implementation-defined behaviour." #endif static void store_uint8(void *bytes, uint8_t u) { memmove(bytes, &u, 1); } static void store_uint16_le(void *bytes, uint16_t u) { u = htole16(u); memmove(bytes, &u, 2); } static void store_uint32_le(void *bytes, uint32_t u) { u = htole32(u); memmove(bytes, &u, 4); } static void store_uint64_le(void *bytes, uint64_t u) { u = htole64(u); memmove(bytes, &u, 8); } static void store_int8(void *bytes, int8_t s) { uint8_t u = s; memmove(bytes, &u, 1); } static void store_int16_le(void *bytes, int16_t s) { uint16_t u = s; u = htole16(u); memmove(bytes, &u, 2); } static void store_int32_le(void *bytes, int32_t s) { uint32_t u = s; u = htole32(u); memmove(bytes, &u, 4); } static void store_int64_le(void *bytes, int64_t s) { uint64_t u = s; u = htole64(u); memmove(bytes, &u, 8); } static uint8_t fetch_uint8(const void *bytes) { uint8_t u; memmove(&u, bytes, 1); return u; } static uint16_t fetch_uint16_le(const void *bytes) { uint16_t u; memmove(&u, bytes, 2); return le16toh(u); } static uint32_t fetch_uint32_le(const void *bytes) { uint32_t u; memmove(&u, bytes, 4); return le32toh(u); } static uint64_t fetch_uint64_le(const void *bytes) { uint64_t u; memmove(&u, bytes, 8); return le64toh(u); } static int8_t fetch_int8(const void *bytes) { uint8_t u; memmove(&u, bytes, 1); return from_uint8(u); } static int16_t fetch_int16_le(const void *bytes) { uint16_t u; memmove(&u, bytes, 2); u = le16toh(u); return from_uint16(u); } static int32_t fetch_int32_le(const void *bytes) { uint32_t u; memmove(&u, bytes, 4); u = le32toh(u); return from_uint32(u); } static int64_t fetch_int64_le(const void *bytes) { uint64_t u; memmove(&u, bytes, 8); u = le64toh(u); return from_uint64(u); } // s36: init // // heap_num_bytes must be at most pow(2, 30). // The alignment of heap_bytes must be at least 16. void s36(uint32_t heap_num_bytes, char *heap_bytes, uint32_t stack_limit, int argc, const char **argv) { command_argc = argc; command_argv = argv; } // s40: prim_command_argc value s40(void) { return integer_encode(command_argc); } // s24: prim_command_argv value s24(value i_value) { int32_t i = integer_decode(i_value); if (i < 0 || i >= command_argc) die("Command argument index is out of range."); return string_make(strlen(command_argv[i]), command_argv[i]); } // s87: halt // // Halt execution, returing 1 as the exit code of the process. _Noreturn value s87(void) { exit(1); } // s52: heap_get_top uint32_t s52(void) { return heap_top; } // s15: heap_set_top void s15(uint32_t top) { heap_top = top; } // s75: closure_make // // Construct a fresh closure value. // // Parameters: // native_code - A C function pointer to a function whose return type is // value and which takes one more than num_params arguments, all of // type value. // num_params - The number of parameters associated with the closure (not // the native function). // env_size - The number of values to be stored in the closure // environment. // env_items - If env_size is zero, then env_items may be NULL; // otherwise, env_items must be an array containing env_size values. // The values provided comprise the environment of the closure. value s75(void *native_code, uint16_t num_params, uint16_t env_size, const value *env_items) { size_t align = _Alignof(struct closure); size_t size = sizeof(struct closure) + env_size * sizeof(value); uint32_t id = heap_alloc(align, size); struct closure *closure_rep = heap_access(id); closure_rep->native_code = native_code; closure_rep->num_params = num_params; closure_rep->env_size = env_size; if (env_size > 0) memmove(closure_rep->env_items, env_items, env_size * sizeof(value)); return value_make_box(TAG_HEAP_CLOSURE, id); } // s62: closure_env_items const value * s62(value closure) { const struct closure *closure_rep = closure_unbox(closure); return closure_rep->env_items; } const void *s35(value closure, uint16_t num_args); value s78(uint16_t num_items, const value *items); const value * s33(value tuple, uint16_t num_items); static value adapt_closure_1_0(value closure) { return ((value (*)(value, value))s35(closure, 1))(closure, empty_tuple); } static value adapt_closure_1_2(value closure, value x0, value x1) { value x = s78(2, (const value[]){x0, x1}); return ((value (*)(value, value))s35(closure, 1))(closure, x); } static value adapt_closure_1_3(value closure, value x0, value x1, value x2) { value x = s78(3, (const value[]){x0, x1, x2}); return ((value (*)(value, value))s35(closure, 1))(closure, x); } static value adapt_closure_1_4(value closure, value x0, value x1, value x2, value x3) { value x = s78(4, (const value[]){x0, x1, x2, x3}); return ((value (*)(value, value))s35(closure, 1))(closure, x); } static value adapt_closure_0_1(value closure, value x) { if (x != empty_tuple) die("Ill-formed function application."); return ((value (*)(value))s35(closure, 0))(closure); } static value adapt_closure_2_1(value closure, value x) { const value *items = s33(x, 2); value (*f)(value, value, value) = s35(closure, 2); return f(closure, items[0], items[1]); } static value adapt_closure_3_1(value closure, value x) { const value *items = s33(x, 3); value (*f)(value, value, value, value) = s35(closure, 3); return f(closure, items[0], items[1], items[2]); } static value adapt_closure_4_1(value closure, value x) { const value *items = s33(x, 4); value (*f)(value, value, value, value, value) = s35(closure, 4); return f(closure, items[0], items[1], items[2], items[3]); } // s35: closure_native_code const void * s35(value closure, uint16_t num_args) { const struct closure *closure_rep = closure_unbox(closure); if (closure_rep->num_params == num_args) return closure_rep->native_code; if (closure_rep->num_params == 1) { switch (num_args) { case 0: return adapt_closure_1_0; case 2: return adapt_closure_1_2; case 3: return adapt_closure_1_3; case 4: return adapt_closure_1_4; default: die("Limitation: Missing adaptor for function application."); } } if (num_args == 1) { switch (closure_rep->num_params) { case 0: return adapt_closure_0_1; case 2: return adapt_closure_2_1; case 3: return adapt_closure_3_1; case 4: return adapt_closure_4_1; default: die("Limitation: Missing adaptor for function application."); } } die("Ill-formed function application."); } // s27: variant_make_nonempty // // Construct a fresh variant value where the enclosed value is not {}. // // Parameters: // label - The label! // item - The enclosed value. value s27(uint16_t label, value item) { size_t align = _Alignof(struct variant); size_t size = sizeof(struct variant); uint32_t id = heap_alloc(align, size); struct variant *variant_rep = heap_access(id); variant_rep->label = label; variant_rep->item = item; return value_make_box(TAG_HEAP_VARIANT, id); } // s09: variant_label // // The label of a variant value. // // Parameters: // variant - The variant! uint16_t s09(value variant) { if (value_has_tag(variant, 0xff, TAG_IMMEDIATE_VARIANT)) return variant >> 8; struct variant *variant_rep = variant_unbox(variant); return variant_rep->label; } // s06: variant_item // // The value embedded within a variant. // // Parameters: // variant - The variant! value s06(value variant) { if (value_has_tag(variant, 0xff, TAG_IMMEDIATE_VARIANT)) return empty_tuple; struct variant *variant_rep = variant_unbox(variant); return variant_rep->item; } // s30: tuple_make_with_layout value s30(uint16_t num_items, const value *items, uint16_t layout) { size_t align = _Alignof(struct tuple); size_t size = sizeof(struct tuple) + num_items * sizeof(value); uint32_t id = heap_alloc(align, size); struct tuple *tuple_rep = heap_access(id); tuple_rep->num_items = num_items; tuple_rep->layout = layout; if (num_items > 0) memmove(tuple_rep->items, items, num_items * sizeof(value)); return value_make_box(TAG_HEAP_TUPLE, id); } // s78: tuple_make_with_no_layout value s78(uint16_t num_items, const value *items) { return s30(num_items, items, UINT16_MAX); } // s68: tuple_fetch_at_offset value s68(value tuple, uint16_t offset) { const char *error_message = "Ill-formed tuple access."; if (tuple == empty_tuple) die(error_message); struct tuple *tuple_rep = tuple_unbox(tuple); if (offset >= tuple_rep->num_items) die(error_message); return tuple_rep->items[offset]; } // s31: tuple_fetch_at_label value s31(value tuple, uint16_t label) { const char *error_message = "Ill-formed record access."; if (tuple == empty_tuple) die(error_message); struct tuple *tuple_rep = tuple_unbox(tuple); unsigned int layout = tuple_rep->layout; if (layout == UINT16_MAX) die(error_message); for (unsigned int i = layout; record_layouts[i] != UINT16_MAX; i++) { if (record_layouts[i] == label) return tuple_rep->items[i - layout]; } die(error_message); } // s33: tuple_items const value * s33(value tuple, uint16_t num_items) { if (tuple == empty_tuple) die("Invalid use of empty tuple."); struct tuple *tuple_rep = tuple_unbox(tuple); if (tuple_rep->num_items != num_items) die("Tuple mismatch."); return tuple_rep->items; } // s86: string_make value s86(size_t num_bytes, const char *bytes) { return string_make(num_bytes, bytes); } // s89: stuck_cond _Noreturn value s89(void) { die("Cond expression has no applicable clause."); } // s88: stuck_switch _Noreturn value s88(void) { die("Switch expression has no applicable clause."); } // s53: stuck_match _Noreturn value s53(void) { die("Match expression has no applicable clause."); } // s45: prim_heap_top value s45(void) { return integer_encode(heap_top); } // s26: prim_die value s26(value string) { die(zero_terminated(string)); } // s00: prim_exec static const char * verified_exec_string(const struct chunk *table, size_t offset) { uint32_t table_size = chunk_num_bytes(table); bool found_zero = false; for (size_t i = offset; i < table_size; i++) { if (table->bytes[i] == 0) { found_zero = true; break; } } if (!found_zero) die("Invalid argument for exec."); return &table->bytes[offset]; } value s00(value path_offset_value, value arg_offsets_value, value var_offsets_value, value table_value) { int32_t path_offset = integer_decode(path_offset_value); if (path_offset < 0) die("Invalid argument for exec."); const struct chunk *arg_offsets = chunk_unbox(arg_offsets_value); const struct chunk *var_offsets = chunk_unbox(var_offsets_value); const struct chunk *table = chunk_unbox(table_value); uint32_t num_args = chunk_num_bytes(arg_offsets) / 4; uint32_t num_vars = chunk_num_bytes(var_offsets) / 4; size_t args_align = _Alignof(const char *); size_t args_size = sizeof(const char *) * (num_args + 1); size_t vars_align = _Alignof(const char *); size_t vars_size = sizeof(const char *) * (num_vars + 1); int r; { uint32_t top = heap_top; const char *path = verified_exec_string(table, path_offset); const char **args = heap_access(heap_alloc(args_align, args_size)); const char **vars = heap_access(heap_alloc(vars_align, vars_size)); for (uint32_t i = 0; i < num_args; i++) { void *offset_bytes = chunk_access(arg_offsets_value, CHUNK_RO, integer_encode(4 * i), 4); uint32_t offset = fetch_uint32_le(offset_bytes); args[i] = verified_exec_string(table, offset); } args[num_args] = NULL; for (uint32_t i = 0; i < num_vars; i++) { void *offset_bytes = chunk_access(var_offsets_value, CHUNK_RO, integer_encode(4 * i), 4); uint32_t offset = fetch_uint32_le(offset_bytes); vars[i] = verified_exec_string(table, offset); } vars[num_vars] = NULL; r = execve(path, args, vars); heap_top = top; } return r; } // s05: prim_exit value s05(value status_value) { int32_t status = integer_decode(status_value); exit(status); } // s85: prim_getcwd value s85(void) { // TODO How to better handle the heap allocation trick used here? (We are // writing into the unallocated region of the heap and subsequently // incorporating the written bytes into the most recently allocated // chunk.) size_t align = _Alignof(struct chunk); size_t size = sizeof(struct chunk); uint32_t id = heap_alloc(align, size); struct chunk *chunk_rep = heap_access(id); int r = getcwd(chunk_rep->bytes, heap_limit - heap_top); if (r < 0) die("Failed to get current working directory."); size_t pathlen = strlen(chunk_rep->bytes); if (pathlen > CHUNK_NUM_BYTES_MAX) die("Current working directory path is too long."); heap_top += pathlen; chunk_rep->header = chunk_header(CHUNK_RO, pathlen); return value_make_box(TAG_HEAP_CHUNK, id); } // s66: prim_getpid value s66(void) { return integer_encode(getpid()); } // s03: prim_open value s03(value name, value flags_value, value mode_value) { int32_t flags = integer_decode(flags_value); int32_t mode = integer_decode(mode_value); int r; { uint32_t top = heap_top; const char *zero_name = zero_terminated(name); r = open(zero_name, flags, mode); heap_top = top; } return integer_encode(r); } // s72: prim_close value s72(value fd_value) { int32_t fd = integer_decode(fd_value); int r = close(fd); return integer_encode(r); } // s67: prim_read value s67(value fd_value, value chunk, value start_value, value count_value) { int32_t fd = integer_decode(fd_value); int32_t count = integer_decode(count_value); void *buf = chunk_access(chunk, CHUNK_RW, start_value, count); long r = read(fd, buf, count); return integer_encode(r); } // s41: prim_write value s41(value fd_value, value chunk, value start_value, value count_value) { int32_t fd = integer_decode(fd_value); int32_t count = integer_decode(count_value); const void *buf = chunk_access(chunk, CHUNK_RO, start_value, count); long r = write(fd, buf, count); return integer_encode(r); } // s18: prim_print value s18(value string) { const char *s = string_bytes(string); int r = write(1, s, string_length(string)); if (r < 0) die("Failed to print string."); return empty_tuple; } // s20: prim_file_create value s20(value name) { int fd; { uint32_t top = heap_top; const char *zero_name = zero_terminated(name); int r = open(zero_name, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0644); if (r < 0) die("Failed to create file."); fd = r; heap_top = top; } return integer_encode(fd); } // s23: prim_file_open value s23(value name) { int fd; { uint32_t top = heap_top; const char *zero_name = zero_terminated(name); int r = open(zero_name, O_RDONLY|O_CLOEXEC, 0); if (r < 0) die("Failed to open file."); fd = r; heap_top = top; } return integer_encode(fd); } // s92: prim_file_close value s92(value fd_value) { int32_t fd = integer_decode(fd_value); if (fd <= 2) die("Attempted to close stdin, stdout, or stderr."); int r = close(fd); if (r < 0) die("Failed to close file."); return empty_tuple; } // s28: prim_file_read_all value s28(value fd_value) { int fd = integer_decode(fd_value); struct stat statbuf; int r = fstat(fd, &statbuf); if (r < 0) die("Failed to determine file size."); off_t file_size = statbuf.st_size; if (file_size < 0 || file_size > UINT32_MAX - 1) die("Failed to read file."); value string = string_make(file_size, NULL); char *bytes = string_bytes(string); r = read(fd, bytes, file_size); if (r != file_size) die("Failed to read file."); return string; } // s12: prim_show_integer value s12(value integer) { int32_t n = integer_decode(integer); bool is_negative = (n < 0); int32_t m = is_negative ? -n : n; char text[16]; int i = 16; if (n == 0) { --i; text[i] = '0'; } else { while (m > 0) { --i; text[i] = '0' + (m % 10); m /= 10; } if (is_negative) { --i; text[i] = '-'; } } size_t len = 16 - i; return string_make(len, &text[i]); } // s93: prim_multiply value s93(value a, value b) { int32_t n; if (__builtin_mul_overflow(integer_decode(a), integer_decode(b), &n)) die("Integer is out of range."); return integer_encode(n); } // s19: prim_add value s19(value a, value b) { int32_t n; if (__builtin_add_overflow(integer_decode(a), integer_decode(b), &n)) die("Integer is out of range."); return integer_encode(n); } // s47: prim_subtract value s47(value a, value b) { int32_t n; if (__builtin_sub_overflow(integer_decode(a), integer_decode(b), &n)) die("Integer is out of range."); return integer_encode(n); } // s84: prim_negate value s84(value n) { if (n == (1 << 31)) die("Integer is out of range."); return integer_encode(-integer_decode(n)); } struct division_result { int32_t q; int32_t r; }; static struct division_result divide(int32_t b, int32_t a) { if (a == 0) die("Division by zero."); int32_t b_abs = (b < 0) ? -b : b; int32_t a_abs = (a < 0) ? -a : a; int32_t q = b_abs / a_abs; int32_t r = b_abs % a_abs; // Invariant: b_abs = q * a_abs + r // Invariant: 0 <= r < |a| if (b < 0) { if (r > 0) { q++; r -= a_abs; } q = -q; r = -r; } // Invariant: b = q * a_abs + r // Invariant: 0 <= r < |a| if (a < 0) q = -q; // Invariant: b = q * a + r // Invariant: 0 <= r < |a| return (struct division_result){.q = q, .r = r}; } // s91: prim_quotient value s91(value b_value, value a_value) { int32_t b = integer_decode(b_value); int32_t a = integer_decode(a_value); struct division_result d = divide(b, a); return integer_encode(d.q); } // s43: prim_remainder value s43(value b_value, value a_value) { int32_t b = integer_decode(b_value); int32_t a = integer_decode(a_value); struct division_result d = divide(b, a); return integer_encode(d.r); } // s50: prim_equal value s50(value a, value b) { return boolean_encode(integer_decode(a) == integer_decode(b)); } // s10: prim_less value s10(value a, value b) { return boolean_encode(integer_decode(a) < integer_decode(b)); } // s63: prim_less_or_equal value s63(value a, value b) { return boolean_encode(integer_decode(a) <= integer_decode(b)); } // s61: prim_greater value s61(value a, value b) { return boolean_encode(integer_decode(a) > integer_decode(b)); } // s55: prim_greater_or_equal value s55(value a, value b) { return boolean_encode(integer_decode(a) >= integer_decode(b)); } // s95: prim_memory_map value s95(value i_value, value size_value, value prot_value, value flags_value, value fd_value, value offset_value) { int32_t i = integer_decode(i_value); int32_t size = integer_decode(size_value); int32_t prot = integer_decode(prot_value); int32_t flags = integer_decode(flags_value); int32_t fd = integer_decode(fd_value); int32_t offset = integer_decode(offset_value); if (i < 0 || NUM_MEMORY_MAPPINGS <= i) die("Memory mapping index is out of range."); if (size == 0) die("Zero-sized memory mappings are not allowed."); if (memory_mappings[i].size > 0) die("Memory mapping is already in use."); long r = mmap(NULL, size, prot, flags, fd, offset); if (r < 0) die("Failed to create memory mapping."); memory_mappings[i].bytes = (void *)r; memory_mappings[i].size = size; memory_mappings[i].prot = prot; return empty_tuple; } // s56: prim_memory_unmap value s56(value i_value) { int32_t i = integer_decode(i_value); if (i < 0 || NUM_MEMORY_MAPPINGS <= i) die("Memory mapping index is out of range."); if (memory_mappings[i].size == 0) return empty_tuple; long r = munmap(memory_mappings[i].bytes, memory_mappings[i].size); if (r < 0) die("Failed to unmap memory mapping."); memory_mappings[i].bytes = NULL; memory_mappings[i].size = 0; memory_mappings[i].prot = 0; return empty_tuple; } // s71: prim_epoll_create1 value s71(value flags_value) { int64_t flags = integer_decode(flags_value); int r = epoll_create1(flags); return integer_encode(r); } // s60: prim_epoll_ctl value s60(value epoll_fd_value, value op_value, value fd_value, value event_chunk) { int64_t epoll_fd = integer_decode(epoll_fd_value); int64_t op = integer_decode(op_value); int64_t fd = integer_decode(fd_value); value start_value = integer_encode(0); void *event = chunk_access(event_chunk, CHUNK_RW, start_value, 12); int r = epoll_ctl(epoll_fd, op, fd, event); return integer_encode(r); } // s97: prim_epoll_wait value s97(value epoll_fd_value, value events_chunk, value max_events_value, value timeout_value) { int64_t epoll_fd = integer_decode(epoll_fd_value); int64_t max_events = integer_decode(max_events_value); int64_t timeout = integer_decode(timeout_value); value start_value = integer_encode(0); unsigned n; if (__builtin_mul_overflow(max_events, 12, &n)) die("Integer is out of range."); unsigned events_size = n; void *events = chunk_access(events_chunk, CHUNK_RW, start_value, events_size); int r = epoll_wait(epoll_fd, events, max_events, timeout); return integer_encode(r); } // s04: prim_chunk_global value s04(value i_value) { int32_t i = integer_decode(i_value); if (i < 0 || NUM_MEMORY_MAPPINGS <= i) die("Memory mapping index is out of range."); return global_chunk_encode(i); } // s38: prim_chunk_new value s38(value num_bytes_value) { int32_t num_bytes = integer_decode(num_bytes_value); if (num_bytes > CHUNK_NUM_BYTES_MAX) die("Chunk size is too big."); size_t align = _Alignof(struct chunk); size_t size = sizeof(struct chunk) + num_bytes; uint32_t id = heap_alloc(align, size); struct chunk *chunk_rep = heap_access(id); chunk_rep->header = chunk_header(CHUNK_RW, num_bytes); memset(chunk_rep->bytes, 0, num_bytes); return value_make_box(TAG_HEAP_CHUNK, id); } // s32: prim_chunk_new_ro value s32(value num_bytes_value, value init_command_value) { int32_t num_bytes = integer_decode(num_bytes_value); if (num_bytes > CHUNK_NUM_BYTES_MAX) die("Chunk size is too big."); size_t align = _Alignof(struct chunk); size_t size = sizeof(struct chunk) + num_bytes; uint32_t id = heap_alloc(align, size); struct chunk *chunk_rep = heap_access(id); chunk_rep->header = chunk_header(CHUNK_RW, num_bytes); memset(chunk_rep->bytes, 0, num_bytes); value chunk_value = value_make_box(TAG_HEAP_CHUNK, id); { uint32_t top = heap_top; value (*init_command)(value, value) = s35(init_command_value, 1); (void)init_command(init_command_value, chunk_value); heap_top = top; } chunk_rep->header = chunk_header(CHUNK_RO, num_bytes); return chunk_value; } // s14: prim_chunk_size value s14(value chunk) { if (value_has_tag(chunk, 0xff, TAG_IMMEDIATE_CHUNK)) { int i = global_chunk_decode(chunk); return integer_encode(memory_mappings[i].size); } else { const struct chunk *chunk_rep = chunk_unbox(chunk); return integer_encode(chunk_num_bytes(chunk_rep)); } die("Value is not a chunk."); } // s82: prim_chunk_store_bytes value s82(value d, value d_start_value, value s, value s_start_value, value count_value) { int32_t count = integer_decode(count_value); if (count < 0) die("Chunk index range is invalid."); void *d_bytes = chunk_access(d, CHUNK_RW, d_start_value, count); const void *s_bytes = chunk_access(s, CHUNK_RO, s_start_value, count); memmove(d_bytes, s_bytes, count); return empty_tuple; } value chunk_fetch_bytes(value chunk, value start_value, value count_value, int perm) { int32_t count = integer_decode(count_value); if (count < 0) die("Chunk index range is invalid."); const void *bytes = chunk_access(chunk, CHUNK_RO, start_value, count); size_t align = _Alignof(struct chunk); size_t size = sizeof(struct chunk) + count; uint32_t id = heap_alloc(align, size); struct chunk *chunk_rep = heap_access(id); chunk_rep->header = chunk_header(perm, count); memmove(chunk_rep->bytes, bytes, count); return value_make_box(TAG_HEAP_CHUNK, id); } // s11: prim_chunk_fetch_bytes_ro value s11(value chunk, value start_value, value count_value) { return chunk_fetch_bytes(chunk, start_value, count_value, CHUNK_RO); } // s39: prim_chunk_fetch_bytes_rw value s39(value chunk, value start_value, value count_value) { return chunk_fetch_bytes(chunk, start_value, count_value, CHUNK_RW); } // s42: prim_chunk_store_uint8 value s42(value chunk, value i_value, value n_value) { void *bytes = chunk_access(chunk, CHUNK_RW, i_value, 1); int64_t n = integer_decode(n_value); if (n < 0 || UINT8_MAX < n) die("Chunk value is out of range."); store_uint8(bytes, n); return empty_tuple; } // s13: prim_chunk_fetch_uint8 value s13(value chunk, value i_value) { const void *bytes = chunk_access(chunk, CHUNK_RO, i_value, 1); uint8_t u = fetch_uint8(bytes); static_assert(UINT8_MAX <= INTEGER_MAX); return integer_encode(u); } // s76: prim_chunk_store_int8_le value s76(value chunk, value i_value, value n_value) { void *bytes = chunk_access(chunk, CHUNK_RW, i_value, 1); int64_t s = integer_decode(n_value); if (s < INT8_MIN || INT8_MAX < s) die("Number is out of range."); store_int8(bytes, s); return empty_tuple; } // s46: prim_chunk_fetch_int8_le value s46(value chunk, value i_value) { const void *bytes = chunk_access(chunk, CHUNK_RO, i_value, 1); int64_t s = fetch_int8(bytes); if (s < INTEGER_MIN || INTEGER_MAX < s) die("Number is out of range."); return integer_encode(s); } // s54: prim_chunk_store_uint16_le value s54(value chunk, value i_value, value n_value) { void *bytes = chunk_access(chunk, CHUNK_RW, i_value, 2); int64_t n = integer_decode(n_value); if (n < 0 || UINT16_MAX < n) die("Chunk value is out of range."); store_uint16_le(bytes, n); return empty_tuple; } // s29: prim_chunk_fetch_uint16_le value s29(value chunk, value i_value) { const void *bytes = chunk_access(chunk, CHUNK_RO, i_value, 2); uint16_t u = fetch_uint16_le(bytes); static_assert(UINT16_MAX <= INTEGER_MAX); return integer_encode(u); } // s08: prim_chunk_store_int16_le value s08(value chunk, value i_value, value n_value) { void *bytes = chunk_access(chunk, CHUNK_RW, i_value, 2); int64_t s = integer_decode(n_value); if (s < INT16_MIN || INT16_MAX < s) die("Number is out of range."); store_int16_le(bytes, s); return empty_tuple; } // s98: prim_chunk_fetch_int16_le value s98(value chunk, value i_value) { const void *bytes = chunk_access(chunk, CHUNK_RO, i_value, 2); int64_t s = fetch_int16_le(bytes); if (s < INTEGER_MIN || INTEGER_MAX < s) die("Number is out of range."); return integer_encode(s); } // s74: prim_chunk_store_uint32_le value s74(value chunk, value i_value, value n_value) { void *bytes = chunk_access(chunk, CHUNK_RW, i_value, 4); int64_t n = integer_decode(n_value); if (n < 0 || UINT32_MAX < n) die("Chunk value is out of range."); store_uint32_le(bytes, n); return empty_tuple; } // s01: prim_chunk_fetch_uint32_le value s01(value chunk, value i_value) { const void *bytes = chunk_access(chunk, CHUNK_RO, i_value, 4); uint32_t u = fetch_uint32_le(bytes); if (u > INTEGER_MAX) die("Number is out of range."); return integer_encode(u); } // s17: prim_chunk_store_int32_le value s17(value chunk, value i_value, value n_value) { void *bytes = chunk_access(chunk, CHUNK_RW, i_value, 4); int64_t s = integer_decode(n_value); if (s < INT32_MIN || INT32_MAX < s) die("Number is out of range."); store_int32_le(bytes, s); return empty_tuple; } // s59: prim_chunk_fetch_int32_le value s59(value chunk, value i_value) { const void *bytes = chunk_access(chunk, CHUNK_RO, i_value, 4); int64_t s = fetch_int32_le(bytes); if (s < INTEGER_MIN || INTEGER_MAX < s) die("Number is out of range."); return integer_encode(s); } // s21: prim_chunk_store_uint64_le value s21(value chunk, value i_value, value n_value) { void *bytes = chunk_access(chunk, CHUNK_RW, i_value, 8); int64_t n = integer_decode(n_value); if (n < 0 || UINT64_MAX < n) die("Chunk value is out of range."); store_uint64_le(bytes, n); return empty_tuple; } // s99: prim_chunk_fetch_uint64_le value s99(value chunk, value i_value) { const void *bytes = chunk_access(chunk, CHUNK_RO, i_value, 8); uint64_t u = fetch_uint64_le(bytes); if (u > INTEGER_MAX) die("Number is out of range."); return integer_encode(u); } // s58: prim_chunk_store_int64_le value s58(value chunk, value i_value, value n_value) { void *bytes = chunk_access(chunk, CHUNK_RW, i_value, 8); int64_t s = integer_decode(n_value); if (s < INT64_MIN || INT64_MAX < s) die("Number is out of range."); store_int64_le(bytes, s); return empty_tuple; } // s80: prim_chunk_fetch_int64_le value s80(value chunk, value i_value) { const void *bytes = chunk_access(chunk, CHUNK_RO, i_value, 8); int64_t s = fetch_int64_le(bytes); if (s < INTEGER_MIN || INTEGER_MAX < s) die("Number is out of range."); return integer_encode(s); } int main(int argc, const char *argv[]) { command_argc = argc; command_argv = argv; P(); return 0; }