sm64

A Super Mario 64 decompilation
Log | Files | Refs | README | LICENSE

libc_impl.c (84451B)


      1 #define _GNU_SOURCE // for sigset
      2 #include <stdbool.h>
      3 #include <stdio.h>
      4 #include <stdint.h>
      5 #include <stdlib.h>
      6 #include <string.h>
      7 #include <math.h>
      8 #include <assert.h>
      9 #include <errno.h>
     10 #include <time.h>
     11 #include <limits.h>
     12 #include <ctype.h>
     13 #include <locale.h>
     14 #include <libgen.h>
     15 
     16 #ifdef __CYGWIN__
     17 #include <windows.h>
     18 #endif
     19 
     20 #ifdef __APPLE__
     21 #include <mach-o/dyld.h>
     22 #include <mach/vm_page_size.h>
     23 #endif
     24 
     25 #include <sys/mman.h>
     26 #include <sys/types.h>
     27 #include <sys/stat.h>
     28 #include <sys/times.h>
     29 #include <sys/file.h>
     30 #include <sys/wait.h>
     31 #include <fcntl.h>
     32 #include <utime.h>
     33 #include <unistd.h>
     34 #include <signal.h>
     35 
     36 #include "libc_impl.h"
     37 #include "helpers.h"
     38 #include "header.h"
     39 
     40 #define MIN(a, b) ((a) < (b) ? (a) : (b))
     41 #define MAX(a, b) ((a) > (b) ? (a) : (b))
     42 
     43 #define STRING(param)                                       \
     44     size_t param##_len = wrapper_strlen(mem, param##_addr); \
     45     char param[param##_len + 1];                            \
     46     for (size_t i = 0; i <= param##_len; i++) {             \
     47         param[i] = MEM_S8(param##_addr + i);                \
     48     }
     49 
     50 #if !defined(IDO53) && !defined(IDO71) && !defined(IDO72)
     51 #define IDO71
     52 #endif
     53 
     54 #define MEM_REGION_START 0xfb00000
     55 #define MEM_REGION_SIZE (512 * 1024 * 1024)
     56 
     57 #ifdef IDO53
     58 // IDO 5.3
     59 #define IOB_ADDR 0x0fb528e4
     60 #define ERRNO_ADDR 0x0fb52720
     61 #define CTYPE_ADDR 0x0fb504f0
     62 #define LIBC_ADDR 0x0fb50000
     63 #define LIBC_SIZE 0x3000
     64 #endif
     65 
     66 #ifdef IDO71
     67 // IDO 7.1
     68 #define IOB_ADDR 0x0fb4ee44
     69 #define ERRNO_ADDR 0x0fb4ec80
     70 #define CTYPE_ADDR 0x0fb4cba0
     71 #define LIBC_ADDR 0x0fb4c000
     72 #define LIBC_SIZE 0x3000
     73 #endif
     74 
     75 #ifdef IDO72
     76 // IDO 7.2
     77 #define IOB_ADDR 0x0fb49454
     78 #define ERRNO_ADDR 0x0fb49290
     79 #define CTYPE_ADDR 0x0fb46db0
     80 #define LIBC_ADDR 0x0fb46000
     81 #define LIBC_SIZE 0x4000
     82 #endif
     83 
     84 #define STDIN_ADDR IOB_ADDR
     85 #define STDOUT_ADDR (IOB_ADDR + 0x10)
     86 #define STDERR_ADDR (IOB_ADDR + 0x20)
     87 #define STDIN ((struct FILE_irix*)&MEM_U32(STDIN_ADDR))
     88 #define STDOUT ((struct FILE_irix*)&MEM_U32(STDOUT_ADDR))
     89 #define STDERR ((struct FILE_irix*)&MEM_U32(STDERR_ADDR))
     90 
     91 #define MALLOC_BINS_ADDR custom_libc_data_addr
     92 #define STRTOK_DATA_ADDR (MALLOC_BINS_ADDR + (30 - 3) * 4)
     93 #define INTBUF_ADDR (STRTOK_DATA_ADDR + 4)
     94 
     95 #define SIGNAL_HANDLER_STACK_START LIBC_ADDR
     96 
     97 #define NFILE 100
     98 
     99 #define IOFBF 0000 /* full buffered */
    100 #define IOLBF 0100 /* line buffered */
    101 #define IONBF 0004 /* not buffered */
    102 #define IOEOF 0020 /* EOF reached on read */
    103 #define IOERR 0040 /* I/O error from system */
    104 
    105 #define IOREAD 0001  /* currently reading */
    106 #define IOWRT 0002   /* currently writing */
    107 #define IORW 0200    /* opened for reading and writing */
    108 #define IOMYBUF 0010 /* stdio malloc()'d buffer */
    109 
    110 #define STDIO_BUFSIZE 16384
    111 
    112 struct timespec_t_irix {
    113     int tv_sec;
    114     int tv_nsec;
    115 };
    116 
    117 struct FILE_irix {
    118     int _cnt;
    119     uint32_t _ptr_addr;
    120     uint32_t _base_addr;
    121     uint8_t pad[2];
    122     uint8_t _file;
    123     uint8_t _flag;
    124 };
    125 
    126 typedef uint64_t (*fptr_trampoline)(uint8_t* mem, uint32_t sp, uint32_t a0, uint32_t a1, uint32_t a2, uint32_t a3,
    127                                     uint32_t fp_dest);
    128 
    129 static struct {
    130     struct {
    131         fptr_trampoline trampoline;
    132         uint8_t* mem;
    133         uint32_t fp_dest;
    134     } handlers[65];
    135     volatile uint32_t recursion_level;
    136 } signal_context;
    137 
    138 static uint32_t cur_sbrk;
    139 static uint32_t bufendtab[NFILE]; // this version contains the size and not the end ptr
    140 static uint32_t custom_libc_data_addr;
    141 
    142 #define _U 01   /* Upper case */
    143 #define _L 02   /* Lower case */
    144 #define _N 04   /* Numeral (digit) */
    145 #define _S 010  /* Spacing character */
    146 #define _P 020  /* Punctuation */
    147 #define _C 040  /* Control character */
    148 #define _B 0100 /* Blank */
    149 #define _X 0200 /* heXadecimal digit */
    150 
    151 static char ctype[] = {
    152     0,
    153     // clang-format off
    154 /*         00      01      02      03      04      05      06      07  */
    155 /*    0 */ _C,     _C,     _C,     _C,     _C,     _C,     _C,     _C,
    156 /*  010 */ _C,     _S|_C,  _S|_C,  _S|_C,  _S|_C,  _S|_C,  _C,     _C,
    157 /*  020 */ _C,     _C,     _C,     _C,     _C,     _C,     _C,     _C,
    158 /*  030 */ _C,     _C,     _C,     _C,     _C,     _C,     _C,     _C,
    159 /*  040 */ _S|_B,  _P,     _P,     _P,     _P,     _P,     _P,     _P,
    160 /*  050 */ _P,     _P,     _P,     _P,     _P,     _P,     _P,     _P,
    161 /*  060 */ _N|_X,  _N|_X,  _N|_X,  _N|_X,  _N|_X,  _N|_X,  _N|_X,  _N|_X,
    162 /*  070 */ _N|_X,  _N|_X,  _P,     _P,     _P,     _P,     _P,     _P,
    163 /* 0100 */ _P,     _U|_X,  _U|_X,  _U|_X,  _U|_X,  _U|_X,  _U|_X,  _U,
    164 /* 0110 */ _U,     _U,     _U,     _U,     _U,     _U,     _U,     _U,
    165 /* 0120 */ _U,     _U,     _U,     _U,     _U,     _U,     _U,     _U,
    166 /* 0130 */ _U,     _U,     _U,     _P,     _P,     _P,     _P,     _P,
    167 /* 0140 */ _P,     _L|_X,  _L|_X,  _L|_X,  _L|_X,  _L|_X,  _L|_X,  _L,
    168 /* 0150 */ _L,     _L,     _L,     _L,     _L,     _L,     _L,     _L,
    169 /* 0160 */ _L,     _L,     _L,     _L,     _L,     _L,     _L,     _L,
    170 /* 0170 */ _L,     _L,     _L,     _P,     _P,     _P,     _P,     _C,
    171 /* 0200 */ 0,      0,      0,      0,      0,      0,      0,      0,
    172 /* 0210 */ 0,      0,      0,      0,      0,      0,      0,      0,
    173 /* 0220 */ 0,      0,      0,      0,      0,      0,      0,      0,
    174 /* 0230 */ 0,      0,      0,      0,      0,      0,      0,      0,
    175 /* 0240 */ 0,      0,      0,      0,      0,      0,      0,      0,
    176 /* 0250 */ 0,      0,      0,      0,      0,      0,      0,      0,
    177 /* 0260 */ 0,      0,      0,      0,      0,      0,      0,      0,
    178 /* 0270 */ 0,      0,      0,      0,      0,      0,      0,      0,
    179 /* 0300 */ 0,      0,      0,      0,      0,      0,      0,      0,
    180 /* 0310 */ 0,      0,      0,      0,      0,      0,      0,      0,
    181 /* 0320 */ 0,      0,      0,      0,      0,      0,      0,      0,
    182 /* 0330 */ 0,      0,      0,      0,      0,      0,      0,      0,
    183 /* 0340 */ 0,      0,      0,      0,      0,      0,      0,      0,
    184 /* 0350 */ 0,      0,      0,      0,      0,      0,      0,      0,
    185 /* 0360 */ 0,      0,      0,      0,      0,      0,      0,      0,
    186 /* 0370 */ 0,      0,      0,      0,      0,      0,      0,      0,
    187     // clang-format on
    188 };
    189 
    190 
    191 static char usr_lib_redirect[PATH_MAX + 1];
    192 static char usr_include_redirect[PATH_MAX + 1];
    193 
    194 static int g_file_max = 3;
    195 
    196 
    197 /* Compilation Target/Emulation Host Page Size Determination */
    198 #if defined(__CYGWIN__) || (defined(__linux__) && defined(__aarch64__))
    199 #define RUNTIME_PAGESIZE
    200 /* ARM64 linux can have page sizes of 4kb, 16kb, or 64kb */
    201 /* Set in main before running the translated code */
    202 static size_t g_Pagesize;
    203 
    204 #define TRUNC_PAGE(x) ((x) & ~(g_Pagesize - 1))
    205 #define ROUND_PAGE(x) (TRUNC_PAGE((x) + (g_Pagesize - 1)))
    206 
    207 #elif defined(__APPLE__)
    208 /* https://developer.apple.com/documentation/apple-silicon/addressing-architectural-differences-in-your-macos-code */
    209 #define TRUNC_PAGE(x) (trunc_page((x)))
    210 #define ROUND_PAGE(x) (round_page((x)))
    211 
    212 #else
    213 /* A fixed 4KB page size for x64 linux (is there anything else?) */
    214 #define TRUNC_PAGE(x) ((x) & ~(0x1000 - 1))
    215 #define ROUND_PAGE(x) (TRUNC_PAGE((x) + (0x1000 - 1)))
    216 #endif /* PageSize Macros */
    217 
    218 static uint8_t* memory_map(size_t length) {
    219 #ifdef __CYGWIN__
    220     uint8_t* mem = mmap(0, length, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
    221 #else
    222     uint8_t* mem = mmap(0, length, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    223 #endif
    224 
    225     assert(TRUNC_PAGE((uintptr_t)mem) == (uintptr_t)mem &&
    226            "Page size too small, try increasing `page_size` in recomp.cpp");
    227     if (mem == MAP_FAILED) {
    228         perror("mmap (memory_map)");
    229         exit(1);
    230     }
    231     return mem;
    232 }
    233 
    234 static void memory_allocate(uint8_t* mem, uint32_t start, uint32_t end) {
    235     assert(start >= MEM_REGION_START);
    236     assert(end <= MEM_REGION_START + MEM_REGION_SIZE);
    237     // `start` will be passed to mmap,
    238     // so it has to be host aligned in order to keep the guest's pages valid
    239     assert(start == TRUNC_PAGE(start) && "Page size too small, try increasing `page_size` in recomp.cpp");
    240 #ifdef __CYGWIN__
    241     uintptr_t _start = TRUNC_PAGE((uintptr_t)mem + start);
    242     uintptr_t _end = ROUND_PAGE((uintptr_t)mem + end);
    243 
    244     if (mprotect((void*)_start, _end - _start, PROT_READ | PROT_WRITE) < 0) {
    245         perror("mprotect (memory_allocate)");
    246         exit(1);
    247     }
    248 #else
    249     void* addr = (void*)TRUNC_PAGE((uintptr_t)mem + start);
    250     size_t len = end - start;
    251 
    252     if (mmap(addr, len, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) == MAP_FAILED) {
    253         perror("mmap (memory_allocate)");
    254         exit(1);
    255     }
    256 #endif /* __CYGWIN__ */
    257 }
    258 
    259 static void memory_unmap(uint8_t* mem, size_t length) {
    260     if (munmap(mem, length)) {
    261         perror("munmap");
    262         exit(1);
    263     }
    264 }
    265 
    266 static void free_all_file_bufs(uint8_t* mem) {
    267     struct FILE_irix* f = (struct FILE_irix*)&MEM_U32(IOB_ADDR);
    268     for (int i = 0; i < g_file_max; i++) {
    269         if (f[i]._flag & IOMYBUF) {
    270             wrapper_free(mem, f[i]._base_addr);
    271         }
    272     }
    273 }
    274 
    275 void get_env_var(char* out, char* name) {
    276     char* env = getenv(name);
    277 
    278     if (env == NULL) { // No environment variable found, so just return empty string
    279         out[0] = '\0';
    280         return;
    281     }
    282 
    283     if (snprintf(out, PATH_MAX, "%s", env) >= PATH_MAX) {
    284         fprintf(stderr, "Error: Environment variable %s is too large\n", name);
    285         exit(1);
    286     }
    287 
    288 }
    289 
    290 static void init_usr_lib_redirect(void) {
    291     char path[PATH_MAX + 1] = { 0 };
    292 
    293     get_env_var(path, "USR_LIB");
    294 
    295     if (path[0] != '\0') {
    296         strcpy(usr_lib_redirect, path);
    297         return;
    298     }
    299 
    300     // gets the current executable's path
    301 #ifdef __CYGWIN__
    302     uint32_t size = GetModuleFileName(NULL, path, PATH_MAX);
    303     if (size == 0 || size == PATH_MAX) {
    304         return;
    305     }
    306 #elif defined __APPLE__
    307     uint32_t size = PATH_MAX;
    308     if (_NSGetExecutablePath(path, &size) < 0) {
    309         return;
    310     }
    311 #else
    312     ssize_t size = readlink("/proc/self/exe", path, PATH_MAX);
    313     if (size < 0 || size == PATH_MAX) {
    314         return;
    315     }
    316 #endif
    317 
    318     strcpy(usr_lib_redirect, dirname(path));
    319 }
    320 
    321 static void init_usr_include_redirect(void) {
    322     char path[PATH_MAX + 1] = {0};
    323 
    324     get_env_var(path, "USR_INCLUDE");
    325 
    326     strcpy(usr_include_redirect, path);
    327 }
    328 
    329 static void init_redirect_paths(void) {
    330     init_usr_lib_redirect();
    331     init_usr_include_redirect();
    332 }
    333 
    334 /**
    335  * Redirects `path` by replacing the initial segment `from` by `to`. The result is placed in `out`.
    336  * If `path` does not have `from` as its initial segment, or there is no `to`, the original path is used.
    337  * If an error occurs, an error message will be printed, and the program exited.
    338 */
    339 void redirect_path(char* out, const char* path, const char* from, const char* to) {
    340     int from_len = strlen(from);
    341 
    342     if(!strncmp(path, from, from_len) && (to[0] != '\0')) {
    343         char redirected_path[PATH_MAX + 1] = {0};
    344         int n;
    345 
    346         n = snprintf(redirected_path, sizeof(redirected_path), "%s%s", to, path + from_len);
    347 
    348         if (n >= 0 && n < sizeof(redirected_path)) {
    349             strcpy(out, redirected_path);
    350         } else {
    351             fprintf(stderr, "Error: Unable to redirect %s->%s for %s\n", from, to, path);
    352             exit(1);
    353         }
    354     } else {
    355         strcpy(out, path);
    356     }
    357 }
    358 
    359 void final_cleanup(uint8_t* mem) {
    360     wrapper_fflush(mem, 0);
    361     free_all_file_bufs(mem);
    362     mem += MEM_REGION_START;
    363     memory_unmap(mem, MEM_REGION_SIZE);
    364 }
    365 
    366 int main(int argc, char* argv[]) {
    367     int ret;
    368 
    369     init_redirect_paths();
    370 #ifdef RUNTIME_PAGESIZE
    371     g_Pagesize = sysconf(_SC_PAGESIZE);
    372 #endif /* RUNTIME_PAGESIZE */
    373 
    374     uint8_t* mem = memory_map(MEM_REGION_SIZE);
    375     mem -= MEM_REGION_START;
    376     int run(uint8_t * mem, int argc, char* argv[]);
    377     ret = run(mem, argc, argv);
    378     final_cleanup(mem);
    379     return ret;
    380 }
    381 
    382 void mmap_initial_data_range(uint8_t* mem, uint32_t start, uint32_t end) {
    383     custom_libc_data_addr = end;
    384 #ifdef __APPLE__
    385     end += vm_page_size;
    386 #else
    387     end += 4096;
    388 #endif /* __APPLE__ */
    389     memory_allocate(mem, start, end);
    390     cur_sbrk = end;
    391 }
    392 
    393 void setup_libc_data(uint8_t* mem) {
    394     memory_allocate(mem, LIBC_ADDR, (LIBC_ADDR + LIBC_SIZE));
    395     for (size_t i = 0; i < sizeof(ctype); i++) {
    396         MEM_S8(CTYPE_ADDR + i) = ctype[i];
    397     }
    398     STDIN->_flag = IOREAD;
    399     STDIN->_file = 0;
    400     STDOUT->_flag = IOWRT;
    401     STDOUT->_file = 1;
    402     STDERR->_flag = IOWRT | IONBF;
    403     STDERR->_file = 2;
    404 }
    405 
    406 static uint32_t strcpy1(uint8_t* mem, uint32_t dest_addr, const char* str) {
    407     for (;;) {
    408         char c = *str;
    409         ++str;
    410         MEM_S8(dest_addr) = c;
    411         ++dest_addr;
    412         if (c == '\0') {
    413             return dest_addr - 1;
    414         }
    415     }
    416 }
    417 
    418 static uint32_t strcpy2(uint8_t* mem, uint32_t dest_addr, uint32_t src_addr) {
    419     for (;;) {
    420         char c = MEM_S8(src_addr);
    421         ++src_addr;
    422         MEM_S8(dest_addr) = c;
    423         ++dest_addr;
    424         if (c == '\0') {
    425             return dest_addr - 1;
    426         }
    427     }
    428 }
    429 
    430 uint32_t wrapper_sbrk(uint8_t* mem, int increment) {
    431     uint32_t old = cur_sbrk;
    432     size_t alignedInc = ROUND_PAGE(old + increment) - old;
    433     memory_allocate(mem, old, old + alignedInc);
    434     cur_sbrk += alignedInc;
    435     return old;
    436 }
    437 
    438 /*
    439 Simple bin-based malloc algorithm
    440 
    441 The memory is divided into bins of item sizes 8, 16, 32, 64, 128, ..., 2^30.
    442 Size requests are divided into these bin sizes and each bin is handled
    443 completely separate from other bins.
    444 
    445 For each bin there is a linked list of free'd items.
    446 Linked list node:
    447 struct FreeListNode {
    448     struct Node *next;
    449     size_t free_space_after;
    450     uint8_t data[bin_item_size];
    451 };
    452 At most one value of next and space_after is non-zero.
    453 If a node exists in the linked list, it is the memory node to return.
    454 struct AllocatedNode {
    455     int bin;
    456     uint32_t current_size;
    457     uint8_t data[bin_item_size];
    458 };
    459 The returned address is the data element.
    460 When the last list node is returned, and free_space_after is big enough
    461 for a new node, a new node is created having free_space_after set to
    462 (free_space_after - (8 + bin_item_size)), and is appended to the list.
    463 
    464 If the list was empty, a new memory chunk is requested from the system
    465 of 65536 bytes, or at least (8 + bin_item_size), rounded up to nearest
    466 page size boundary. It can also be smaller if it leaves holes bigger than
    467 4096 bytes that can never be used. This chunk is then inserted to the list,
    468 and the algorithm restarts.
    469 
    470 This algorithm, for each bin, never uses more than twice as much as is
    471 maximally in use (plus 65536 bytes).
    472 The malloc/free calls run in O(1) and calloc/realloc calls run in O(size).
    473 */
    474 
    475 size_t mem_used;
    476 size_t mem_allocated;
    477 size_t max_mem_used;
    478 size_t num_sbrks;
    479 size_t num_allocs;
    480 uint32_t wrapper_malloc(uint8_t* mem, uint32_t size) {
    481     int bin = -1;
    482     for (int i = 3; i < 30; i++) {
    483         if (size <= (1 << i)) {
    484             bin = i;
    485             break;
    486         }
    487     }
    488     if (bin == -1) {
    489         return 0;
    490     }
    491     ++num_allocs;
    492     mem_used += size;
    493     max_mem_used = MAX(mem_used, max_mem_used);
    494     uint32_t item_size = 1 << bin;
    495     uint32_t list_ptr = MALLOC_BINS_ADDR + (bin - 3) * 4;
    496     uint32_t node_ptr = MEM_U32(list_ptr);
    497     if (node_ptr == 0) {
    498         uint32_t sbrk_request = 0x10000;
    499         if (8 + item_size > sbrk_request) {
    500             sbrk_request = 8 + item_size;
    501             sbrk_request = ROUND_PAGE(sbrk_request);
    502         }
    503         uint32_t left_over = sbrk_request % (8 + item_size);
    504         sbrk_request -= left_over & ~(4096 - 1);
    505         mem_allocated += sbrk_request;
    506         ++num_sbrks;
    507         node_ptr = wrapper_sbrk(mem, sbrk_request);
    508         MEM_U32(node_ptr + 4) = sbrk_request - (8 + item_size);
    509     }
    510     uint32_t next = MEM_U32(node_ptr);
    511     if (next == 0) {
    512         uint32_t free_space_after = MEM_U32(node_ptr + 4);
    513         if (free_space_after >= 8 + item_size) {
    514             next = node_ptr + 8 + item_size;
    515             MEM_U32(next + 4) = free_space_after - (8 + item_size);
    516         }
    517     } else {
    518         assert(MEM_U32(node_ptr + 4) == 0);
    519     }
    520     MEM_U32(list_ptr) = next;
    521     MEM_U32(node_ptr) = bin;
    522     MEM_U32(node_ptr + 4) = size;
    523     return node_ptr + 8;
    524 }
    525 
    526 uint32_t wrapper_calloc(uint8_t* mem, uint32_t num, uint32_t size) {
    527     uint64_t new_size = (uint64_t)num * size;
    528     assert(new_size == (uint32_t)new_size);
    529     uint32_t ret = wrapper_malloc(mem, new_size);
    530     return wrapper_memset(mem, ret, 0, new_size);
    531 }
    532 
    533 uint32_t wrapper_realloc(uint8_t* mem, uint32_t data_addr, uint32_t size) {
    534     if (data_addr == 0) {
    535         return wrapper_malloc(mem, size);
    536     } else {
    537         uint32_t node_ptr = data_addr - 8;
    538         int bin = MEM_U32(node_ptr);
    539         uint32_t old_size = MEM_U32(node_ptr + 4);
    540         uint32_t max_size = 1 << bin;
    541         assert(bin >= 3 && bin < 30);
    542         assert(old_size <= max_size);
    543         if (size <= max_size) {
    544             mem_used = mem_used - old_size + size;
    545             MEM_U32(node_ptr + 4) = size;
    546             return data_addr;
    547         } else {
    548             uint32_t new_addr = wrapper_malloc(mem, size);
    549             wrapper_memcpy(mem, new_addr, data_addr, old_size);
    550             wrapper_free(mem, data_addr);
    551             return new_addr;
    552         }
    553     }
    554 }
    555 
    556 void wrapper_free(uint8_t* mem, uint32_t data_addr) {
    557     if (data_addr == 0) {
    558         return;
    559     }
    560     uint32_t node_ptr = data_addr - 8;
    561     int bin = MEM_U32(node_ptr);
    562     uint32_t size = MEM_U32(node_ptr + 4);
    563     if (size == 0) {
    564         // Double free. IDO 5.3 strip relies on this.
    565         fprintf(stderr, "warning: double free: 0x%x\n", data_addr);
    566         return;
    567     }
    568     uint32_t list_ptr = MALLOC_BINS_ADDR + (bin - 3) * 4;
    569     assert(bin >= 3 && bin < 30);
    570     assert(size <= (1 << bin));
    571     MEM_U32(node_ptr) = MEM_U32(list_ptr);
    572     MEM_U32(node_ptr + 4) = 0;
    573     MEM_U32(list_ptr) = node_ptr;
    574     mem_used -= size;
    575 }
    576 
    577 int wrapper_fscanf(uint8_t* mem, uint32_t fp_addr, uint32_t format_addr, uint32_t sp) {
    578     struct FILE_irix* f = (struct FILE_irix*)&MEM_U32(fp_addr);
    579     STRING(format) // for debug
    580 
    581     int ret = 0;
    582     char c;
    583     int ch;
    584     sp += 2 * 4;
    585     for (;;) {
    586         c = MEM_S8(format_addr);
    587         ++format_addr;
    588         if (c == '%') {
    589             c = MEM_S8(format_addr);
    590             ++format_addr;
    591             if (c == '%') {
    592                 goto percent;
    593             }
    594             for (;;) {
    595                 ch = wrapper_fgetc(mem, fp_addr);
    596                 if (ch == -1) {
    597                     return ret;
    598                 }
    599                 if (!isspace(ch)) {
    600                     break;
    601                 }
    602             }
    603             bool l = false;
    604         continue_format:
    605             switch (c) {
    606                 case 'l':
    607                     assert(!l && "ll not implemented in fscanf");
    608                     l = true;
    609                     c = MEM_S8(format_addr);
    610                     ++format_addr;
    611                     goto continue_format;
    612                 case 'd': {
    613                     int64_t num = 0;
    614                     int sign = 1;
    615                     bool found_first = false;
    616                     if (ch == '-') {
    617                         sign = -1;
    618                         ch = wrapper_fgetc(mem, fp_addr);
    619                         if (ch == -1) {
    620                             return ret;
    621                         }
    622                     }
    623                     for (;;) {
    624                         if (isdigit(ch)) {
    625                             num *= 10;
    626                             num += ch - '0';
    627                             found_first = true;
    628                             ch = wrapper_fgetc(mem, fp_addr);
    629                             if (ch == -1) {
    630                                 break;
    631                             }
    632                         } else {
    633                             wrapper_ungetc(mem, ch, fp_addr);
    634                             break;
    635                         }
    636                     }
    637                     if (found_first) {
    638                         uint32_t int_addr = MEM_U32(sp);
    639                         sp += 4;
    640                         MEM_S32(int_addr) = (int)(num * sign);
    641                         ++ret;
    642                     } else {
    643                         return ret;
    644                     }
    645                     break;
    646                 }
    647                 default:
    648                     assert(0 && "fscanf format not implemented");
    649             }
    650         } else if (c == '\0') {
    651             break;
    652         } else {
    653         percent:
    654             ch = wrapper_fgetc(mem, fp_addr);
    655             if (ch == -1) {
    656                 break;
    657             }
    658             if ((char)ch != c) {
    659                 break;
    660             }
    661         }
    662     }
    663 
    664     return ret;
    665 }
    666 
    667 int wrapper_printf(uint8_t* mem, uint32_t format_addr, uint32_t sp) {
    668     STRING(format)
    669     if (!strcmp(format, " child died due to signal %d.\n")) {
    670         printf(format, MEM_U32(sp + 4));
    671         return 1;
    672     }
    673     assert(0 && "printf not implemented");
    674     return 0;
    675 }
    676 
    677 int wrapper_sprintf(uint8_t* mem, uint32_t str_addr, uint32_t format_addr, uint32_t sp) {
    678     STRING(format) // for debug
    679     char temp[32];
    680 
    681     if (!strcmp(format, "%.16e")) {
    682         union {
    683             uint32_t w[2];
    684             double d;
    685         } d;
    686         d.w[1] = MEM_U32(sp + 2 * 4);
    687         d.w[0] = MEM_U32(sp + 3 * 4);
    688         sprintf(temp, "%.16e", d.d);
    689         strcpy1(mem, str_addr, temp);
    690         return 1;
    691     }
    692     if (!strcmp(format, "\\%03o")) {
    693         sprintf(temp, "\\%03o", MEM_U32(sp + 2 * 4));
    694         strcpy1(mem, str_addr, temp);
    695         return 1;
    696     }
    697     if (!strcmp(format, "%*ld=")) {
    698         sprintf(temp, "%*d=", MEM_U32(sp + 2 * 4), MEM_U32(sp + 3 * 4));
    699         strcpy1(mem, str_addr, temp);
    700         return 1;
    701     }
    702 
    703     uint32_t orig_str_addr = str_addr;
    704     uint32_t pos = 0;
    705     int ret = 0;
    706     char c;
    707     sp += 2 * 4;
    708     for (;;) {
    709         c = MEM_S8(format_addr + pos);
    710         ++pos;
    711         if (c == '%') {
    712             bool l = false;
    713             c = MEM_S8(format_addr + pos);
    714             ++pos;
    715             uint32_t zeros = 0;
    716             bool zero_prefix = false;
    717         continue_format:
    718             switch (c) {
    719                 case '\0':
    720                     goto finish_str;
    721 
    722                 case '0':
    723                     do {
    724                         c = MEM_S8(format_addr + pos);
    725                         ++pos;
    726                         if (c >= '0' && c <= '9') {
    727                             zeros *= 10;
    728                             zeros += c - '0';
    729                         }
    730                     } while (c >= '0' && c <= '9');
    731                     goto continue_format;
    732                 case '#':
    733                     c = MEM_S8(format_addr + pos);
    734                     ++pos;
    735                     zero_prefix = true;
    736                     goto continue_format;
    737                     break;
    738                 case 'l':
    739                     assert(!l && "ll not implemented in fscanf");
    740                     c = MEM_S8(format_addr + pos);
    741                     ++pos;
    742                     l = true;
    743                     goto continue_format;
    744                     break;
    745                 case 'd':
    746                     if (zeros != 0) {
    747                         char temp1[32];
    748                         sprintf(temp1, "%%0%dd", zeros);
    749                         sprintf(temp, temp1, MEM_S32(sp));
    750                     } else {
    751                         sprintf(temp, "%d", MEM_S32(sp));
    752                     }
    753                     sp += 4;
    754                     str_addr = strcpy1(mem, str_addr, temp);
    755                     ++ret;
    756                     break;
    757                 case 'o':
    758                     if (zero_prefix) {
    759                         sprintf(temp, "%#o", MEM_S32(sp));
    760                     } else {
    761                         sprintf(temp, "%o", MEM_S32(sp));
    762                     }
    763                     sp += 4;
    764                     str_addr = strcpy1(mem, str_addr, temp);
    765                     ++ret;
    766                     break;
    767                 case 'x':
    768                     if (zero_prefix) {
    769                         sprintf(temp, "%#x", MEM_S32(sp));
    770                     } else {
    771                         sprintf(temp, "%x", MEM_S32(sp));
    772                     }
    773                     sp += 4;
    774                     str_addr = strcpy1(mem, str_addr, temp);
    775                     ++ret;
    776                     break;
    777                 case 'u':
    778                     sprintf(temp, "%u", MEM_S32(sp));
    779                     sp += 4;
    780                     str_addr = strcpy1(mem, str_addr, temp);
    781                     ++ret;
    782                     break;
    783                 case 's':
    784                     str_addr = strcpy2(mem, str_addr, MEM_U32(sp));
    785                     sp += 4;
    786                     ++ret;
    787                     break;
    788                 case 'c':
    789                     MEM_S8(str_addr) = (char)MEM_U32(sp);
    790                     ++str_addr;
    791                     sp += 4;
    792                     ++ret;
    793                     break;
    794                 case '%':
    795                     MEM_S8(str_addr) = '%';
    796                     ++str_addr;
    797                     break;
    798                 default:
    799                     fprintf(stderr, "%s\n", format);
    800                     assert(0 && "non-implemented sprintf format");
    801             }
    802         } else if (c == '\0') {
    803             break;
    804         } else {
    805             MEM_S8(str_addr) = c;
    806             ++str_addr;
    807         }
    808     }
    809 
    810 finish_str:
    811     MEM_S8(str_addr) = '\0';
    812     return ret;
    813 }
    814 
    815 int wrapper_fprintf(uint8_t* mem, uint32_t fp_addr, uint32_t format_addr, uint32_t sp) {
    816     struct FILE_irix* f = (struct FILE_irix*)&MEM_U32(fp_addr);
    817     STRING(format)
    818     sp += 8;
    819 
    820     // Special-case this one format string. This seems to be the only one that uses `%f` or width specifiers.
    821     if (!strcmp(format, "%.2fu %.2fs %u:%04.1f %.0f%%\n") && fp_addr == STDERR_ADDR) {
    822         double arg0 = MEM_F64(sp + 0);
    823         double arg1 = MEM_F64(sp + 8);
    824         uint32_t arg2 = MEM_U32(sp + 16);
    825         double arg3 = MEM_F64(sp + 24);
    826         double arg4 = MEM_F64(sp + 32);
    827         fprintf(stderr, format, arg0, arg1, arg2, arg3, arg4);
    828         fflush(stderr);
    829         return 1;
    830     }
    831     if (strcmp(format, "%s phase time: %.2fu %.2fs %u:%04.1f %.0f%%\n") == 0 && fp_addr == STDERR_ADDR) {
    832         if (wrapper_fputs(mem, MEM_U32(sp), fp_addr) == -1) {
    833             return 0;
    834         }
    835         sp += 4;
    836         // align
    837         sp += 4;
    838 
    839         double arg0 = MEM_F64(sp + 0);
    840         double arg1 = MEM_F64(sp + 8);
    841         uint32_t arg2 = MEM_U32(sp + 16);
    842         double arg3 = MEM_F64(sp + 24);
    843         double arg4 = MEM_F64(sp + 32);
    844         fprintf(stderr, " phase time: %.2fu %.2fs %u:%04.1f %.0f%%\n", arg0, arg1, arg2, arg3, arg4);
    845         fflush(stderr);
    846         return 1;
    847     }
    848     int ret = 0;
    849     for (;;) {
    850         int width = 1;
    851         uint32_t pos = format_addr;
    852         char ch = MEM_S8(pos);
    853 
    854         while (ch != '%' && ch != '\0') {
    855             ++pos;
    856             ch = MEM_S8(pos);
    857         }
    858         if (format_addr != pos) {
    859             if (wrapper_fwrite(mem, format_addr, 1, pos - format_addr, fp_addr) != pos - format_addr) {
    860                 break;
    861             }
    862         }
    863         if (ch == '\0') {
    864             break;
    865         }
    866         ++pos;
    867         ch = MEM_S8(pos);
    868         if (ch >= '1' && ch <= '9') {
    869             ++pos;
    870             width = ch - '0';
    871             ch = MEM_S8(pos);
    872         }
    873 
    874         switch (ch) {
    875             case 'd':
    876             case 'x':
    877             case 'X':
    878             case 'c':
    879             case 'u': {
    880                 char buf[32];
    881 
    882                 char formatSpecifier[0x100] = { 0 };
    883 
    884                 formatSpecifier[0] = '%';
    885                 formatSpecifier[1] = width + '0';
    886                 formatSpecifier[2] = ch;
    887 
    888                 sprintf(buf, formatSpecifier, MEM_U32(sp));
    889 
    890                 strcpy1(mem, INTBUF_ADDR, buf);
    891                 if (wrapper_fputs(mem, INTBUF_ADDR, fp_addr) == -1) {
    892                     return ret;
    893                 }
    894                 sp += 4;
    895                 ++ret;
    896                 break;
    897             }
    898             case 's': {
    899                 if (wrapper_fputs(mem, MEM_U32(sp), fp_addr) == -1) {
    900                     return ret;
    901                 }
    902                 sp += 4;
    903                 ++ret;
    904                 break;
    905             }
    906             default:
    907                 fprintf(stderr, "missing format: '%s'\n", format);
    908                 assert(0 && "non-implemented fprintf format");
    909         }
    910         format_addr = ++pos;
    911     }
    912     return ret;
    913 }
    914 
    915 int wrapper__doprnt(uint8_t* mem, uint32_t format_addr, uint32_t params_addr, uint32_t fp_addr) {
    916     assert(0 && "_doprnt not implemented");
    917     return 0;
    918 }
    919 
    920 uint32_t wrapper_strlen(uint8_t* mem, uint32_t str_addr) {
    921     uint32_t len = 0;
    922     while (MEM_S8(str_addr) != '\0') {
    923         ++str_addr;
    924         ++len;
    925     }
    926     return len;
    927 }
    928 
    929 int wrapper_open(uint8_t* mem, uint32_t pathname_addr, int flags, int mode) {
    930     STRING(pathname)
    931 
    932     char rpathname[PATH_MAX + 1];
    933     redirect_path(rpathname, pathname, "/usr/include", usr_include_redirect);
    934 
    935     int f = flags & O_ACCMODE;
    936     if (flags & 0x100) {
    937         f |= O_CREAT;
    938     }
    939     if (flags & 0x200) {
    940         f |= O_TRUNC;
    941     }
    942     if (flags & 0x400) {
    943         f |= O_EXCL;
    944     }
    945     if (flags & 0x800) {
    946         f |= O_NOCTTY;
    947     }
    948     if (flags & 0x08) {
    949         f |= O_APPEND;
    950     }
    951 
    952     int fd = open(rpathname, f, mode);
    953     MEM_U32(ERRNO_ADDR) = errno;
    954     return fd;
    955 }
    956 
    957 int wrapper_creat(uint8_t* mem, uint32_t pathname_addr, int mode) {
    958     STRING(pathname)
    959     int ret = creat(pathname, mode);
    960     if (ret < 0) {
    961         MEM_U32(ERRNO_ADDR) = errno;
    962     }
    963     return ret;
    964 }
    965 
    966 int wrapper_access(uint8_t* mem, uint32_t pathname_addr, int mode) {
    967     STRING(pathname)
    968     int ret = access(pathname, mode);
    969     if (ret != 0) {
    970         MEM_U32(ERRNO_ADDR) = errno;
    971     }
    972     return ret;
    973 }
    974 
    975 int wrapper_rename(uint8_t* mem, uint32_t oldpath_addr, uint32_t newpath_addr) {
    976     STRING(oldpath)
    977     STRING(newpath)
    978     int ret = rename(oldpath, newpath);
    979     if (ret != 0) {
    980         MEM_U32(ERRNO_ADDR) = errno;
    981     }
    982     return ret;
    983 }
    984 
    985 int wrapper_utime(uint8_t* mem, uint32_t filename_addr, uint32_t times_addr) {
    986     STRING(filename)
    987     struct utimbuf buf = { 0, 0 };
    988     int ret = utime(filename, times_addr == 0 ? NULL : &buf);
    989     if (ret == 0) {
    990         if (times_addr != 0) {
    991             MEM_U32(times_addr + 0) = buf.actime;
    992             MEM_U32(times_addr + 4) = buf.modtime;
    993         }
    994     } else {
    995         MEM_U32(ERRNO_ADDR) = errno;
    996     }
    997     return ret;
    998 }
    999 
   1000 int wrapper_flock(uint8_t* mem, int fd, int operation) {
   1001     int ret = flock(fd, operation);
   1002     if (ret != 0) {
   1003         MEM_U32(ERRNO_ADDR) = errno;
   1004     }
   1005     return ret;
   1006 }
   1007 
   1008 int wrapper_chmod(uint8_t* mem, uint32_t path_addr, uint32_t mode) {
   1009     STRING(path)
   1010     int ret = chmod(path, mode);
   1011     if (ret < 0) {
   1012         MEM_U32(ERRNO_ADDR) = errno;
   1013     }
   1014     return ret;
   1015 }
   1016 
   1017 int wrapper_umask(int mode) {
   1018     return umask(mode);
   1019 }
   1020 
   1021 uint32_t wrapper_ecvt(uint8_t* mem, double number, int ndigits, uint32_t decpt_addr, uint32_t sign_addr) {
   1022     assert(0);
   1023 }
   1024 
   1025 uint32_t wrapper_fcvt(uint8_t* mem, double number, int ndigits, uint32_t decpt_addr, uint32_t sign_addr) {
   1026     assert(0);
   1027 }
   1028 
   1029 double wrapper_sqrt(double v) {
   1030     return sqrt(v);
   1031 }
   1032 
   1033 float wrapper_sqrtf(float v) {
   1034     return sqrtf(v);
   1035 }
   1036 
   1037 int wrapper_atoi(uint8_t* mem, uint32_t nptr_addr) {
   1038     STRING(nptr)
   1039     return atoi(nptr);
   1040 }
   1041 
   1042 int wrapper_atol(uint8_t* mem, uint32_t nptr_addr) {
   1043     return wrapper_atoi(mem, nptr_addr);
   1044 }
   1045 
   1046 double wrapper_atof(uint8_t* mem, uint32_t nptr_addr) {
   1047     STRING(nptr);
   1048     return atof(nptr);
   1049 }
   1050 
   1051 int wrapper_strtol(uint8_t* mem, uint32_t nptr_addr, uint32_t endptr_addr, int base) {
   1052     STRING(nptr)
   1053     char* endptr = NULL;
   1054     int64_t res = strtoll(nptr, endptr_addr != 0 ? &endptr : NULL, base);
   1055     if (res > INT_MAX) {
   1056         MEM_U32(ERRNO_ADDR) = ERANGE;
   1057         res = INT_MAX;
   1058     }
   1059     if (res < INT_MIN) {
   1060         MEM_U32(ERRNO_ADDR) = ERANGE;
   1061         res = INT_MIN;
   1062     }
   1063     if (endptr != NULL) {
   1064         MEM_U32(endptr_addr) = nptr_addr + (uint32_t)(endptr - nptr);
   1065     }
   1066     return res;
   1067 }
   1068 
   1069 uint32_t wrapper_strtoul(uint8_t* mem, uint32_t nptr_addr, uint32_t endptr_addr, int base) {
   1070     STRING(nptr)
   1071     char* endptr = NULL;
   1072     uint64_t res = strtoull(nptr, endptr_addr != 0 ? &endptr : NULL, base);
   1073     if (res > INT_MAX) {
   1074         MEM_U32(ERRNO_ADDR) = ERANGE;
   1075         res = INT_MAX;
   1076     }
   1077     if (endptr != NULL) {
   1078         MEM_U32(endptr_addr) = nptr_addr + (uint32_t)(endptr - nptr);
   1079     }
   1080     return res;
   1081 }
   1082 
   1083 int64_t wrapper_strtoll(uint8_t* mem, uint32_t nptr_addr, uint32_t endptr_addr, int base) {
   1084     STRING(nptr)
   1085     char* endptr = NULL;
   1086     errno = 0;
   1087     int64_t res = strtoll(nptr, endptr_addr != 0 ? &endptr : NULL, base);
   1088     if (errno != 0) {
   1089         MEM_U32(ERRNO_ADDR) = errno;
   1090     }
   1091     if (endptr != NULL) {
   1092         MEM_U32(endptr_addr) = nptr_addr + (uint32_t)(endptr - nptr);
   1093     }
   1094     return res;
   1095 }
   1096 
   1097 uint64_t wrapper_strtoull(uint8_t* mem, uint32_t nptr_addr, uint32_t endptr_addr, int base) {
   1098     STRING(nptr)
   1099     char* endptr = NULL;
   1100     errno = 0;
   1101     uint64_t res = strtoull(nptr, endptr_addr != 0 ? &endptr : NULL, base);
   1102     if (errno != 0) {
   1103         MEM_U32(ERRNO_ADDR) = errno;
   1104     }
   1105     if (endptr != NULL) {
   1106         MEM_U32(endptr_addr) = nptr_addr + (uint32_t)(endptr - nptr);
   1107     }
   1108     return res;
   1109 }
   1110 
   1111 double wrapper_strtod(uint8_t* mem, uint32_t nptr_addr, uint32_t endptr_addr) {
   1112     STRING(nptr)
   1113     char* endptr = NULL;
   1114     errno = 0;
   1115     double res = strtod(nptr, endptr_addr != 0 ? &endptr : NULL);
   1116     if (errno != 0) {
   1117         MEM_U32(ERRNO_ADDR) = errno;
   1118     }
   1119     if (endptr != NULL) {
   1120         MEM_U32(endptr_addr) = nptr_addr + (uint32_t)(endptr - nptr);
   1121     }
   1122     return res;
   1123 }
   1124 
   1125 uint32_t wrapper_strchr(uint8_t* mem, uint32_t str_addr, int c) {
   1126     c = c & 0xff;
   1127     for (;;) {
   1128         unsigned char ch = MEM_U8(str_addr);
   1129         if (ch == c) {
   1130             return str_addr;
   1131         }
   1132         if (ch == '\0') {
   1133             return 0;
   1134         }
   1135         ++str_addr;
   1136     }
   1137 }
   1138 
   1139 uint32_t wrapper_strrchr(uint8_t* mem, uint32_t str_addr, int c) {
   1140     c = c & 0xff;
   1141     uint32_t ret = 0;
   1142     for (;;) {
   1143         unsigned char ch = MEM_U8(str_addr);
   1144         if (ch == c) {
   1145             ret = str_addr;
   1146         }
   1147         if (ch == '\0') {
   1148             return ret;
   1149         }
   1150         ++str_addr;
   1151     }
   1152 }
   1153 
   1154 uint32_t wrapper_strcspn(uint8_t* mem, uint32_t str_addr, uint32_t invalid_addr) {
   1155     STRING(invalid)
   1156     uint32_t n = strlen(invalid);
   1157     uint32_t pos = 0;
   1158     char c;
   1159     while ((c = MEM_S8(str_addr)) != 0) {
   1160         for (int i = 0; i < n; i++) {
   1161             if (c == invalid[i]) {
   1162                 return pos;
   1163             }
   1164         }
   1165         ++pos;
   1166         ++str_addr;
   1167     }
   1168     return pos;
   1169 }
   1170 
   1171 uint32_t wrapper_strpbrk(uint8_t* mem, uint32_t str_addr, uint32_t accept_addr) {
   1172     STRING(accept)
   1173     uint32_t n = strlen(accept);
   1174     char c;
   1175     while ((c = MEM_S8(str_addr)) != 0) {
   1176         for (int i = 0; i < n; i++) {
   1177             if (c == accept[i]) {
   1178                 return str_addr;
   1179             }
   1180         }
   1181         ++str_addr;
   1182     }
   1183     return 0;
   1184 }
   1185 
   1186 static void stat_common(uint8_t* mem, uint32_t buf_addr, struct stat* statbuf) {
   1187     struct irix_stat {
   1188         int st_dev;
   1189         int pad1[3];
   1190         int st_ino;
   1191         int st_mode;
   1192         int st_nlink;
   1193         int st_uid;
   1194         int st_gid;
   1195         int st_rdev;
   1196         int pad2[2];
   1197         int st_size;
   1198         int pad3;
   1199         struct timespec_t_irix st_atim;
   1200         struct timespec_t_irix st_mtim;
   1201         struct timespec_t_irix st_ctim;
   1202         int st_blksize;
   1203         int st_blocks;
   1204     } s;
   1205     s.st_dev = statbuf->st_dev;
   1206     s.st_ino = statbuf->st_ino;
   1207     s.st_mode = statbuf->st_mode;
   1208     s.st_nlink = statbuf->st_nlink;
   1209     s.st_uid = statbuf->st_uid;
   1210     s.st_gid = statbuf->st_gid;
   1211     s.st_rdev = statbuf->st_rdev;
   1212     s.st_size = statbuf->st_size;
   1213 #ifdef __APPLE__
   1214     s.st_atim.tv_sec = statbuf->st_atimespec.tv_sec;
   1215     s.st_atim.tv_nsec = statbuf->st_atimespec.tv_nsec;
   1216     s.st_mtim.tv_sec = statbuf->st_mtimespec.tv_sec;
   1217     s.st_mtim.tv_nsec = statbuf->st_mtimespec.tv_nsec;
   1218     s.st_ctim.tv_sec = statbuf->st_ctimespec.tv_sec;
   1219     s.st_ctim.tv_nsec = statbuf->st_ctimespec.tv_nsec;
   1220 #else
   1221     s.st_atim.tv_sec = statbuf->st_atim.tv_sec;
   1222     s.st_atim.tv_nsec = statbuf->st_atim.tv_nsec;
   1223     s.st_mtim.tv_sec = statbuf->st_mtim.tv_sec;
   1224     s.st_mtim.tv_nsec = statbuf->st_mtim.tv_nsec;
   1225     s.st_ctim.tv_sec = statbuf->st_ctim.tv_sec;
   1226     s.st_ctim.tv_nsec = statbuf->st_ctim.tv_nsec;
   1227 #endif
   1228     memcpy(&MEM_U32(buf_addr), &s, sizeof(s));
   1229 }
   1230 
   1231 int wrapper_fstat(uint8_t* mem, int fildes, uint32_t buf_addr) {
   1232     struct stat statbuf;
   1233     if (fstat(fildes, &statbuf) < 0) {
   1234         MEM_U32(ERRNO_ADDR) = errno;
   1235         return -1;
   1236     } else {
   1237         stat_common(mem, buf_addr, &statbuf);
   1238         return 0;
   1239     }
   1240 }
   1241 
   1242 int wrapper_stat(uint8_t* mem, uint32_t pathname_addr, uint32_t buf_addr) {
   1243     STRING(pathname)
   1244     struct stat statbuf;
   1245     if (stat(pathname, &statbuf) < 0) {
   1246         MEM_U32(ERRNO_ADDR) = errno;
   1247         return -1;
   1248     } else {
   1249         stat_common(mem, buf_addr, &statbuf);
   1250         return 0;
   1251     }
   1252 }
   1253 
   1254 int wrapper_ftruncate(uint8_t* mem, int fd, int length) {
   1255     int ret = ftruncate(fd, length);
   1256     if (ret != 0) {
   1257         MEM_U32(ERRNO_ADDR) = errno;
   1258     }
   1259     return ret;
   1260 }
   1261 
   1262 int wrapper_truncate(uint8_t* mem, uint32_t pathname_addr, int length) {
   1263     STRING(pathname)
   1264     int ret = truncate(pathname, length);
   1265     if (ret != 0) {
   1266         MEM_U32(ERRNO_ADDR) = errno;
   1267     }
   1268     return ret;
   1269 }
   1270 
   1271 void wrapper_bcopy(uint8_t* mem, uint32_t src_addr, uint32_t dst_addr, uint32_t len) {
   1272     if (dst_addr % 4 == 0 && src_addr % 4 == 0 && len % 4 == 0) {
   1273         // Use memmove to copy regions that are 4-byte aligned.
   1274         // This prevents the byte-swapped mem from causing issues when copying normally.
   1275         // Memmove handles overlapping copies correctly, so overlap does not need to be checked.
   1276         memmove(&MEM_U32(dst_addr), &MEM_U32(src_addr), len);
   1277     } else if (dst_addr > src_addr) {
   1278         // Perform a reverse byte-swapped copy when the destination is ahead of the source.
   1279         // This prevents overwriting the source contents before they're read.
   1280         dst_addr += len - 1;
   1281         src_addr += len - 1;
   1282         while (len--) {
   1283             MEM_U8(dst_addr) = MEM_U8(src_addr);
   1284             --dst_addr;
   1285             --src_addr;
   1286         }
   1287     } else {
   1288         // Otherwise, perform a normal byte-swapped copy.
   1289         while (len--) {
   1290             MEM_U8(dst_addr) = MEM_U8(src_addr);
   1291             ++dst_addr;
   1292             ++src_addr;
   1293         }
   1294     }
   1295 }
   1296 
   1297 /**
   1298  * IRIX's memcpy seems to allow overlapping destination and source pointers, while the C standard dictates
   1299  * both pointer should not overlap, (UB otherwise).
   1300  * Because of this, we only use host bcopy since it can handle overlapping regions
   1301  */
   1302 uint32_t wrapper_memcpy(uint8_t* mem, uint32_t dst_addr, uint32_t src_addr, uint32_t len) {
   1303     wrapper_bcopy(mem, src_addr, dst_addr, len);
   1304     return dst_addr;
   1305 }
   1306 
   1307 uint32_t wrapper_memccpy(uint8_t* mem, uint32_t dst_addr, uint32_t src_addr, int c, uint32_t len) {
   1308     while (len--) {
   1309         uint8_t ch = MEM_U8(src_addr);
   1310         MEM_U8(dst_addr) = ch;
   1311         ++dst_addr;
   1312         ++src_addr;
   1313         if (ch == c) {
   1314             return dst_addr;
   1315         }
   1316     }
   1317     return 0;
   1318 }
   1319 
   1320 int wrapper_read(uint8_t* mem, int fd, uint32_t buf_addr, uint32_t nbytes) {
   1321     uint8_t* buf = (uint8_t*)malloc(nbytes);
   1322     ssize_t ret = read(fd, buf, nbytes);
   1323     if (ret < 0) {
   1324         MEM_U32(ERRNO_ADDR) = errno;
   1325     } else {
   1326         for (ssize_t i = 0; i < ret; i++) {
   1327             MEM_U8(buf_addr + i) = buf[i];
   1328         }
   1329     }
   1330     free(buf);
   1331     return (int)ret;
   1332 }
   1333 
   1334 int wrapper_write(uint8_t* mem, int fd, uint32_t buf_addr, uint32_t nbytes) {
   1335     uint8_t* buf = (uint8_t*)malloc(nbytes);
   1336     for (size_t i = 0; i < nbytes; i++) {
   1337         buf[i] = MEM_U8(buf_addr + i);
   1338     }
   1339     ssize_t ret = write(fd, buf, nbytes);
   1340     if (ret < 0) {
   1341         MEM_U32(ERRNO_ADDR) = errno;
   1342     }
   1343     free(buf);
   1344     return (int)ret;
   1345 }
   1346 
   1347 static uint32_t init_file(uint8_t* mem, int fd, int i, const char* path, const char* mode) {
   1348     int flags = O_RDONLY;
   1349     if (strcmp(mode, "r") == 0 || strcmp(mode, "rb") == 0) {
   1350         flags = O_RDONLY;
   1351     } else if (strcmp(mode, "w") == 0 || strcmp(mode, "wb") == 0) {
   1352         flags = O_WRONLY | O_CREAT | O_TRUNC;
   1353     } else if (strcmp(mode, "a") == 0 || strcmp(mode, "ab") == 0) {
   1354         flags = O_WRONLY | O_CREAT | O_APPEND;
   1355     } else if (strcmp(mode, "r+") == 0 || strcmp(mode, "r+b") == 0) {
   1356         flags = O_RDWR;
   1357     } else if (strcmp(mode, "w+") == 0 || strcmp(mode, "w+b") == 0) {
   1358         flags = O_RDWR | O_CREAT | O_TRUNC;
   1359     } else if (strcmp(mode, "a+") == 0 || strcmp(mode, "a+b") == 0) {
   1360         flags = O_RDWR | O_CREAT | O_APPEND;
   1361     }
   1362     if (fd == -1) {
   1363         char rpathname[PATH_MAX + 1];
   1364         redirect_path(rpathname, path, "/usr/lib", usr_lib_redirect);
   1365 
   1366         fd = open(rpathname, flags, 0666);
   1367 
   1368         if (fd < 0) {
   1369             MEM_U32(ERRNO_ADDR) = errno;
   1370             return 0;
   1371         }
   1372     }
   1373     struct FILE_irix* f = (struct FILE_irix*)&MEM_U32(IOB_ADDR);
   1374     uint32_t ret = 0;
   1375     if (i == -1) {
   1376         for (i = 3; i < NFILE; i++) {
   1377             if (f[i]._flag == 0) {
   1378                 break;
   1379             }
   1380         }
   1381     }
   1382     assert(i < NFILE);
   1383     g_file_max = i + 1;
   1384     ret = IOB_ADDR + i * sizeof(struct FILE_irix);
   1385     f[i]._cnt = 0;
   1386     f[i]._ptr_addr = 0;
   1387     f[i]._base_addr = 0;
   1388     f[i]._file = fd;
   1389     f[i]._flag = (flags & O_ACCMODE) == O_RDONLY ? IOREAD : 0;
   1390     f[i]._flag |= (flags & O_ACCMODE) == O_WRONLY ? IOWRT : 0;
   1391     f[i]._flag |= (flags & O_ACCMODE) == O_RDWR ? IORW : 0;
   1392     bufendtab[i] = 0;
   1393     return ret;
   1394 }
   1395 
   1396 uint32_t wrapper_fopen(uint8_t* mem, uint32_t path_addr, uint32_t mode_addr) {
   1397     assert(path_addr != 0);
   1398     assert(mode_addr != 0);
   1399 
   1400     STRING(path)
   1401     STRING(mode)
   1402     return init_file(mem, -1, -1, path, mode);
   1403 }
   1404 
   1405 uint32_t wrapper_freopen(uint8_t* mem, uint32_t path_addr, uint32_t mode_addr, uint32_t fp_addr) {
   1406     STRING(path)
   1407     STRING(mode)
   1408     struct FILE_irix* f = (struct FILE_irix*)&MEM_U32(fp_addr);
   1409     wrapper_fclose(mem, fp_addr);
   1410     return init_file(mem, -1, f - (struct FILE_irix*)&MEM_U32(IOB_ADDR), path, mode);
   1411 }
   1412 
   1413 int wrapper_fclose(uint8_t* mem, uint32_t fp_addr) {
   1414     struct FILE_irix* f = (struct FILE_irix*)&MEM_U32(fp_addr);
   1415     wrapper_fflush(mem, fp_addr);
   1416     if (f->_flag & IOMYBUF) {
   1417         wrapper_free(mem, f->_base_addr);
   1418     }
   1419     f->_flag = 0;
   1420     close(f->_file);
   1421     return 0;
   1422 }
   1423 
   1424 static int flush_all(uint8_t* mem) {
   1425     struct FILE_irix* f = (struct FILE_irix*)&MEM_U32(IOB_ADDR);
   1426     int ret = 0;
   1427     for (int i = 0; i < g_file_max; i++) {
   1428         if (f[i]._flag & IOWRT) {
   1429             ret |= wrapper_fflush(mem, IOB_ADDR + i * sizeof(struct FILE_irix));
   1430         }
   1431     }
   1432     return ret;
   1433 }
   1434 
   1435 int wrapper_fflush(uint8_t* mem, uint32_t fp_addr) {
   1436     if (fp_addr == 0) {
   1437         // Flush all
   1438         return flush_all(mem);
   1439     }
   1440     struct FILE_irix* f = (struct FILE_irix*)&MEM_U32(fp_addr);
   1441     if (f->_flag & IOWRT) {
   1442         int p = 0;
   1443         int to_flush = f->_ptr_addr - f->_base_addr;
   1444         int c = to_flush;
   1445         while (c > 0) {
   1446             int r = wrapper_write(mem, f->_file, f->_base_addr + p, c);
   1447             if (r < 0) {
   1448                 f->_file |= IOERR;
   1449                 return -1;
   1450             }
   1451             p += r;
   1452             c -= r;
   1453         }
   1454         f->_ptr_addr = f->_base_addr;
   1455         f->_cnt += to_flush;
   1456     }
   1457     return 0;
   1458 }
   1459 
   1460 int wrapper_fchown(uint8_t* mem, int fd, int owner, int group) {
   1461     int ret = fchown(fd, owner, group);
   1462     if (ret != 0) {
   1463         MEM_U32(ERRNO_ADDR) = errno;
   1464     }
   1465     return ret;
   1466 }
   1467 
   1468 int wrapper_ftell(uint8_t* mem, uint32_t fp_addr) {
   1469     struct FILE_irix* f = (struct FILE_irix*)&MEM_U32(fp_addr);
   1470     int adjust;
   1471     if (f->_cnt < 0) {
   1472         f->_cnt = 0;
   1473     }
   1474     if (f->_flag & IOREAD) {
   1475         adjust = -f->_cnt;
   1476     } else if (f->_flag & (IOWRT | IORW)) {
   1477         adjust = 0;
   1478         if ((f->_flag & IOWRT) && f->_base_addr != 0 && (f->_flag & IONBF) == 0) {
   1479             adjust = f->_ptr_addr - f->_base_addr;
   1480         }
   1481     } else {
   1482         return -1;
   1483     }
   1484     int res = wrapper_lseek(mem, f->_file, 0, 1);
   1485     if (res >= 0) {
   1486         res += adjust;
   1487     }
   1488     return res;
   1489 }
   1490 
   1491 void wrapper_rewind(uint8_t* mem, uint32_t fp_addr) {
   1492     (void)wrapper_fseek(mem, fp_addr, 0, SEEK_SET);
   1493     struct FILE_irix* f = (struct FILE_irix*)&MEM_U32(fp_addr);
   1494     f->_flag &= ~IOERR;
   1495 }
   1496 
   1497 int wrapper_fseek(uint8_t* mem, uint32_t fp_addr, int offset, int origin) {
   1498     struct FILE_irix* f = (struct FILE_irix*)&MEM_U32(fp_addr);
   1499     int c, p;
   1500     f->_flag &= ~IOEOF;
   1501     if (f->_flag & IOREAD) {
   1502         if (origin < SEEK_END && f->_base_addr && !(f->_flag & IONBF)) {
   1503             c = f->_cnt;
   1504             p = offset;
   1505             if (origin == SEEK_SET) {
   1506                 p += c - lseek(f->_file, 0L, SEEK_CUR);
   1507             } else {
   1508                 offset -= c;
   1509             }
   1510             if (!(f->_flag & IORW) && c > 0 && p <= c && p >= f->_base_addr - f->_ptr_addr) {
   1511                 f->_ptr_addr += p;
   1512                 f->_cnt -= p;
   1513                 return 0;
   1514             }
   1515         }
   1516         if (f->_flag & IORW) {
   1517             f->_ptr_addr = f->_base_addr;
   1518             f->_flag &= ~IOREAD;
   1519         }
   1520         p = lseek(f->_file, offset, origin);
   1521         f->_cnt = 0;
   1522     } else if (f->_flag & (IOWRT | IORW)) {
   1523         wrapper_fflush(mem, fp_addr);
   1524         if (f->_flag & IORW) {
   1525             f->_cnt = 0;
   1526             f->_flag &= ~IOWRT;
   1527             f->_ptr_addr = f->_base_addr;
   1528         }
   1529         p = lseek(f->_file, offset, origin);
   1530     }
   1531     if (p < 0) {
   1532         MEM_U32(ERRNO_ADDR) = errno;
   1533         return p;
   1534     }
   1535     return 0;
   1536 }
   1537 
   1538 int wrapper_lseek(uint8_t* mem, int fd, int offset, int whence) {
   1539     int ret = (int)lseek(fd, offset, whence);
   1540     if (ret == -1) {
   1541         MEM_U32(ERRNO_ADDR) = errno;
   1542     }
   1543     return ret;
   1544 }
   1545 
   1546 int wrapper_dup(uint8_t* mem, int fd) {
   1547     fd = dup(fd);
   1548     if (fd < 0) {
   1549         MEM_U32(ERRNO_ADDR) = errno;
   1550     }
   1551     return fd;
   1552 }
   1553 
   1554 int wrapper_dup2(uint8_t* mem, int oldfd, int newfd) {
   1555     int fd = dup2(oldfd, newfd);
   1556     if (fd < 0) {
   1557         MEM_U32(ERRNO_ADDR) = errno;
   1558     }
   1559     return fd;
   1560 }
   1561 
   1562 int wrapper_pipe(uint8_t* mem, uint32_t pipefd_addr) {
   1563     int pipefd[2];
   1564     int ret = pipe(pipefd);
   1565     if (ret == 0) {
   1566         MEM_U32(pipefd_addr + 0) = pipefd[0];
   1567         MEM_U32(pipefd_addr + 4) = pipefd[1];
   1568     } else {
   1569         MEM_U32(ERRNO_ADDR) = errno;
   1570     }
   1571     return ret;
   1572 }
   1573 
   1574 void wrapper_perror(uint8_t* mem, uint32_t str_addr) {
   1575     STRING(str)
   1576     perror(str);
   1577 }
   1578 
   1579 int wrapper_fdopen(uint8_t* mem, int fd, uint32_t mode_addr) {
   1580     STRING(mode)
   1581     return init_file(mem, fd, -1, NULL, mode);
   1582 }
   1583 
   1584 uint32_t wrapper_memset(uint8_t* mem, uint32_t dest_addr, int byte, uint32_t n) {
   1585     uint32_t saved = dest_addr;
   1586     if (dest_addr % 4 == 0 && n % 4 == 0) {
   1587         memset(&MEM_U32(dest_addr), byte, n);
   1588     } else {
   1589         while (n--) {
   1590             MEM_U8(dest_addr) = (uint8_t)byte;
   1591             ++dest_addr;
   1592         }
   1593     }
   1594     return saved;
   1595 }
   1596 
   1597 int wrapper_bcmp(uint8_t* mem, uint32_t s1_addr, uint32_t s2_addr, uint32_t n) {
   1598     while (n--) {
   1599         if (MEM_U8(s1_addr) != MEM_U8(s2_addr)) {
   1600             return 1;
   1601         }
   1602         ++s1_addr;
   1603         ++s2_addr;
   1604     }
   1605     return 0;
   1606 }
   1607 
   1608 int wrapper_memcmp(uint8_t* mem, uint32_t s1_addr, uint32_t s2_addr, uint32_t n) {
   1609     while (n--) {
   1610         unsigned char c1 = MEM_U8(s1_addr);
   1611         unsigned char c2 = MEM_U8(s2_addr);
   1612         if (c1 < c2) {
   1613             return -1;
   1614         }
   1615         if (c1 > c2) {
   1616             return 1;
   1617         }
   1618         ++s1_addr;
   1619         ++s2_addr;
   1620     }
   1621     return 0;
   1622 }
   1623 
   1624 int wrapper_getpid(void) {
   1625     return getpid();
   1626 }
   1627 
   1628 int wrapper_getpgrp(uint8_t* mem) {
   1629     int ret = getpgrp();
   1630     if (ret == -1) {
   1631         MEM_U32(ERRNO_ADDR) = errno;
   1632     }
   1633     return ret;
   1634 }
   1635 
   1636 int wrapper_remove(uint8_t* mem, uint32_t path_addr) {
   1637     STRING(path)
   1638     int ret = remove(path);
   1639     if (ret < 0) {
   1640         MEM_U32(ERRNO_ADDR) = errno;
   1641     }
   1642     return ret;
   1643 }
   1644 
   1645 int wrapper_unlink(uint8_t* mem, uint32_t path_addr) {
   1646     if (path_addr == 0) {
   1647         fprintf(stderr, "Warning: unlink with NULL as arguement\n");
   1648         MEM_U32(ERRNO_ADDR) = EFAULT;
   1649         return -1;
   1650     }
   1651     STRING(path)
   1652     int ret = unlink(path);
   1653     if (ret < 0) {
   1654         MEM_U32(ERRNO_ADDR) = errno;
   1655     }
   1656     return ret;
   1657 }
   1658 
   1659 int wrapper_close(uint8_t* mem, int fd) {
   1660     int ret = close(fd);
   1661     if (ret < 0) {
   1662         MEM_U32(ERRNO_ADDR) = errno;
   1663     }
   1664     return ret;
   1665 }
   1666 
   1667 int wrapper_strcmp(uint8_t* mem, uint32_t s1_addr, uint32_t s2_addr) {
   1668     for (;;) {
   1669         char c1 = MEM_S8(s1_addr);
   1670         char c2 = MEM_S8(s2_addr);
   1671         if (c1 != c2) {
   1672             return c1 < c2 ? -1 : 1;
   1673         }
   1674         if (c1 == '\0') {
   1675             return 0;
   1676         }
   1677         ++s1_addr;
   1678         ++s2_addr;
   1679     }
   1680 }
   1681 
   1682 int wrapper_strncmp(uint8_t* mem, uint32_t s1_addr, uint32_t s2_addr, uint32_t n) {
   1683     if (n == 0) {
   1684         return 0;
   1685     }
   1686     for (;;) {
   1687         char c1 = MEM_S8(s1_addr);
   1688         char c2 = MEM_S8(s2_addr);
   1689         if (c1 != c2) {
   1690             return c1 < c2 ? -1 : 1;
   1691         }
   1692         if (--n == 0 || c1 == '\0') {
   1693             return 0;
   1694         }
   1695         ++s1_addr;
   1696         ++s2_addr;
   1697     }
   1698 }
   1699 
   1700 uint32_t wrapper_strcpy(uint8_t* mem, uint32_t dest_addr, uint32_t src_addr) {
   1701     uint32_t saved = dest_addr;
   1702     for (;;) {
   1703         char c = MEM_S8(src_addr);
   1704         ++src_addr;
   1705         MEM_S8(dest_addr) = c;
   1706         ++dest_addr;
   1707         if (c == '\0') {
   1708             return saved;
   1709         }
   1710     }
   1711 }
   1712 
   1713 uint32_t wrapper_strncpy(uint8_t* mem, uint32_t dest_addr, uint32_t src_addr, uint32_t n) {
   1714     uint32_t i;
   1715     for (i = 0; i < n && MEM_S8(src_addr) != '\0'; i++) {
   1716         MEM_S8(dest_addr + i) = MEM_S8(src_addr + i);
   1717     }
   1718     for (; i < n; i++) {
   1719         MEM_S8(dest_addr + i) = '\0';
   1720     }
   1721     return dest_addr;
   1722 }
   1723 
   1724 uint32_t wrapper_strcat(uint8_t* mem, uint32_t dest_addr, uint32_t src_addr) {
   1725     uint32_t saved = dest_addr;
   1726     while (MEM_S8(dest_addr) != '\0') {
   1727         ++dest_addr;
   1728     }
   1729     while (MEM_S8(src_addr) != '\0') {
   1730         MEM_S8(dest_addr) = MEM_S8(src_addr);
   1731         ++src_addr;
   1732         ++dest_addr;
   1733     }
   1734     MEM_S8(dest_addr) = '\0';
   1735     return saved;
   1736 }
   1737 
   1738 uint32_t wrapper_strncat(uint8_t* mem, uint32_t dest_addr, uint32_t src_addr, uint32_t n) {
   1739     uint32_t saved = dest_addr;
   1740     while (MEM_S8(dest_addr) != '\0') {
   1741         ++dest_addr;
   1742     }
   1743     while (n-- && MEM_S8(src_addr) != '\0') {
   1744         MEM_S8(dest_addr) = MEM_S8(src_addr);
   1745         ++src_addr;
   1746         ++dest_addr;
   1747     }
   1748     MEM_S8(dest_addr) = '\0';
   1749     return saved;
   1750 }
   1751 
   1752 uint32_t wrapper_strtok(uint8_t* mem, uint32_t str_addr, uint32_t delimiters_addr) {
   1753     if (str_addr == 0) {
   1754         str_addr = MEM_U32(STRTOK_DATA_ADDR);
   1755     }
   1756     if (str_addr == 0) {
   1757         // nothing remaining
   1758         return 0;
   1759     }
   1760     uint32_t p;
   1761     for (p = str_addr; MEM_S8(p) != '\0'; p++) {
   1762         uint32_t q;
   1763         for (q = delimiters_addr; MEM_S8(q) != '\0' && MEM_S8(q) != MEM_S8(p); q++) {}
   1764         if (MEM_S8(q) == '\0') {
   1765             break;
   1766         }
   1767     }
   1768     if (MEM_S8(p) == '\0') {
   1769         return 0;
   1770     }
   1771     uint32_t ret = p;
   1772     for (;;) {
   1773         uint32_t q;
   1774         for (q = delimiters_addr; MEM_S8(q) != '\0' && MEM_S8(q) != MEM_S8(p); q++) {}
   1775         if (MEM_S8(q) != '\0') {
   1776             MEM_S8(p) = '\0';
   1777             MEM_U32(STRTOK_DATA_ADDR) = ++p;
   1778             return ret;
   1779         }
   1780         char next = MEM_S8(p);
   1781         ++p;
   1782         if (next == '\0') {
   1783             MEM_U32(STRTOK_DATA_ADDR) = 0;
   1784             return ret;
   1785         }
   1786     }
   1787 }
   1788 
   1789 uint32_t wrapper_strstr(uint8_t* mem, uint32_t str1_addr, uint32_t str2_addr) {
   1790     for (;;) {
   1791         if (MEM_S8(str1_addr) == '\0') {
   1792             return 0;
   1793         }
   1794         uint32_t s1 = str1_addr;
   1795         uint32_t s2 = str2_addr;
   1796         for (;;) {
   1797             char c2 = MEM_S8(s2);
   1798             if (c2 == '\0') {
   1799                 return str1_addr;
   1800             }
   1801             if (MEM_S8(s1) == c2) {
   1802                 ++s1;
   1803                 ++s2;
   1804             } else {
   1805                 break;
   1806             }
   1807         }
   1808         ++str1_addr;
   1809     }
   1810 }
   1811 
   1812 uint32_t wrapper_strdup(uint8_t* mem, uint32_t str_addr) {
   1813     uint32_t len = wrapper_strlen(mem, str_addr) + 1;
   1814     uint32_t ret = wrapper_malloc(mem, len);
   1815     if (ret == 0) {
   1816         MEM_U32(ERRNO_ADDR) = ENOMEM;
   1817         return 0;
   1818     }
   1819     return wrapper_memcpy(mem, ret, str_addr, len);
   1820 }
   1821 
   1822 int wrapper_toupper(int c) {
   1823     return toupper(c);
   1824 }
   1825 
   1826 int wrapper_tolower(int c) {
   1827     return tolower(c);
   1828 }
   1829 
   1830 int wrapper_gethostname(uint8_t* mem, uint32_t name_addr, uint32_t len) {
   1831     char buf[256] = { 0 };
   1832     if (len > 256) {
   1833         len = 256;
   1834     }
   1835     int ret = gethostname(buf, len);
   1836     if (ret < 0) {
   1837         MEM_U32(ERRNO_ADDR) = errno;
   1838     } else {
   1839         for (uint32_t i = 0; i < len; i++) {
   1840             MEM_S8(name_addr + i) = buf[i];
   1841         }
   1842     }
   1843     return ret;
   1844 }
   1845 
   1846 int wrapper_isatty(uint8_t* mem, int fd) {
   1847     int ret = isatty(fd);
   1848     if (ret == 0) {
   1849         MEM_U32(ERRNO_ADDR) = errno;
   1850     }
   1851     return ret;
   1852 }
   1853 
   1854 uint32_t wrapper_strftime(uint8_t* mem, uint32_t ptr_addr, uint32_t maxsize, uint32_t format_addr,
   1855                           uint32_t timeptr_addr) {
   1856     MEM_S8(ptr_addr) = 0;
   1857     return 0;
   1858 }
   1859 
   1860 int wrapper_times(uint8_t* mem, uint32_t buffer_addr) {
   1861     struct tms_irix {
   1862         int tms_utime;
   1863         int tms_stime;
   1864         int tms_cutime;
   1865         int tms_cstime;
   1866     } r;
   1867     struct tms t;
   1868     clock_t ret = times(&t);
   1869     if (ret == (clock_t)-1) {
   1870         MEM_U32(ERRNO_ADDR) = errno;
   1871     } else {
   1872         r.tms_utime = t.tms_utime;
   1873         r.tms_stime = t.tms_stime;
   1874         r.tms_cutime = t.tms_cutime;
   1875         r.tms_cstime = t.tms_cstime;
   1876         MEM_U32(buffer_addr + 0x0) = t.tms_utime;
   1877         MEM_U32(buffer_addr + 0x4) = t.tms_stime;
   1878         MEM_U32(buffer_addr + 0x8) = t.tms_cutime;
   1879         MEM_U32(buffer_addr + 0xC) = t.tms_cstime;
   1880     }
   1881     return (int)ret;
   1882 }
   1883 
   1884 int wrapper_clock(void) {
   1885     return (int)clock();
   1886 }
   1887 
   1888 uint32_t wrapper_ctime(uint8_t* mem, uint32_t timep_addr) {
   1889     time_t t = MEM_S32(timep_addr);
   1890     char* res = ctime(&t);
   1891     size_t len = strlen(res) + 1;
   1892     uint32_t ret_addr = wrapper_malloc(mem, len);
   1893     uint32_t pos = ret_addr;
   1894     while (len--) {
   1895         MEM_S8(pos) = *res;
   1896         ++pos;
   1897         ++res;
   1898     }
   1899     return ret_addr;
   1900 }
   1901 
   1902 uint32_t wrapper_localtime(uint8_t* mem, uint32_t timep_addr) {
   1903     time_t t = MEM_S32(timep_addr);
   1904     struct irix_tm {
   1905         int tm_sec;
   1906         int tm_min;
   1907         int tm_hour;
   1908         int tm_mday;
   1909         int tm_mon;
   1910         int tm_year;
   1911         int tm_wday;
   1912         int tm_yday;
   1913         int tm_isdst;
   1914     };
   1915     uint32_t ret = wrapper_malloc(mem, sizeof(struct irix_tm));
   1916     struct irix_tm* r = (struct irix_tm*)&MEM_U32(ret);
   1917     struct tm* l = localtime(&t);
   1918     r->tm_sec = l->tm_sec;
   1919     r->tm_min = l->tm_min;
   1920     r->tm_hour = l->tm_hour;
   1921     r->tm_mday = l->tm_mday;
   1922     r->tm_mon = l->tm_mon;
   1923     r->tm_year = l->tm_year;
   1924     r->tm_wday = l->tm_wday;
   1925     r->tm_yday = l->tm_yday;
   1926     r->tm_isdst = l->tm_isdst;
   1927     return ret;
   1928 }
   1929 
   1930 int wrapper_setvbuf(uint8_t* mem, uint32_t fp_addr, uint32_t buf_addr, int mode, uint32_t size) {
   1931     struct FILE_irix* f = (struct FILE_irix*)&MEM_U32(fp_addr);
   1932     wrapper_fflush(mem, fp_addr);
   1933     if ((f->_flag & IOMYBUF) && f->_base_addr != 0) {
   1934         wrapper_free(mem, f->_base_addr);
   1935     }
   1936     size &= ~0xf;
   1937     f->_flag &= ~IOMYBUF;
   1938 
   1939     if (buf_addr == 0) {
   1940         assert(size > 0);
   1941         buf_addr = wrapper_malloc(mem, size);
   1942         f->_flag |= IOMYBUF;
   1943     }
   1944 
   1945     f->_base_addr = buf_addr;
   1946     f->_ptr_addr = buf_addr;
   1947     f->_cnt = 0;
   1948     bufendtab[(fp_addr - IOB_ADDR) / sizeof(struct FILE_irix)] = size;
   1949     return 0;
   1950 }
   1951 
   1952 int wrapper___semgetc(uint8_t* mem, uint32_t fp_addr) {
   1953     assert(0);
   1954 }
   1955 
   1956 int wrapper___semputc(uint8_t* mem, int c, uint32_t fp_addr) {
   1957     assert(0);
   1958 }
   1959 
   1960 int wrapper_fgetc(uint8_t* mem, uint32_t fp_addr) {
   1961     struct FILE_irix* f = (struct FILE_irix*)&MEM_U32(fp_addr);
   1962     if (--f->_cnt < 0) {
   1963         return wrapper___filbuf(mem, fp_addr);
   1964     } else {
   1965         int ret = MEM_U8(f->_ptr_addr);
   1966         ++f->_ptr_addr;
   1967         return ret;
   1968     }
   1969 }
   1970 
   1971 int wrapper_fgets(uint8_t* mem, uint32_t str_addr, int count, uint32_t fp_addr) {
   1972     bool modified = false;
   1973     uint32_t saved = str_addr;
   1974     for (count--; count > 0; count--) {
   1975         int ch = wrapper_fgetc(mem, fp_addr);
   1976         if (ch == -1) {
   1977             MEM_S8(str_addr) = '\0';
   1978             return modified ? saved : 0;
   1979         }
   1980         modified = true;
   1981         MEM_S8(str_addr) = (char)ch;
   1982         ++str_addr;
   1983         if (ch == '\n') {
   1984             break;
   1985         }
   1986     }
   1987     MEM_S8(str_addr) = '\0';
   1988     return saved;
   1989 }
   1990 
   1991 static void file_assign_buffer(uint8_t* mem, struct FILE_irix* f) {
   1992     f->_base_addr = wrapper_malloc(mem, STDIO_BUFSIZE);
   1993     f->_ptr_addr = f->_base_addr;
   1994     f->_flag |= IOMYBUF;
   1995     f->_cnt = 0;
   1996     bufendtab[f - (struct FILE_irix*)&MEM_U32(IOB_ADDR)] = STDIO_BUFSIZE;
   1997 }
   1998 
   1999 int wrapper___filbuf(uint8_t* mem, uint32_t fp_addr) {
   2000     struct FILE_irix* f = (struct FILE_irix*)&MEM_U32(fp_addr);
   2001     if (!(f->_flag & IOREAD)) {
   2002         if (f->_flag & IORW) {
   2003             f->_flag |= IOREAD;
   2004         } else {
   2005             MEM_U32(ERRNO_ADDR) = 9; // EBADF
   2006             return -1;
   2007         }
   2008     }
   2009     if (f->_base_addr == 0) {
   2010         file_assign_buffer(mem, f);
   2011     }
   2012     uint32_t size = bufendtab[(fp_addr - IOB_ADDR) / sizeof(struct FILE_irix)];
   2013     int nread = wrapper_read(mem, f->_file, f->_base_addr, size);
   2014     int ret = -1;
   2015     if (nread > 0) {
   2016         f->_ptr_addr = f->_base_addr;
   2017         f->_cnt = nread;
   2018         ret = MEM_U8(f->_ptr_addr);
   2019         ++f->_ptr_addr;
   2020         --f->_cnt;
   2021     } else if (nread == 0) {
   2022         f->_flag |= IOEOF;
   2023     } else {
   2024         f->_flag |= IOERR;
   2025     }
   2026     return ret;
   2027 }
   2028 
   2029 int wrapper___flsbuf(uint8_t* mem, int ch, uint32_t fp_addr) {
   2030     struct FILE_irix* f = (struct FILE_irix*)&MEM_U32(fp_addr);
   2031     if (wrapper_fflush(mem, fp_addr) != 0) {
   2032         return -1;
   2033     }
   2034     if (f->_base_addr == 0) {
   2035         file_assign_buffer(mem, f);
   2036         f->_cnt = bufendtab[f - (struct FILE_irix*)&MEM_U32(IOB_ADDR)];
   2037     }
   2038     MEM_U8(f->_ptr_addr) = ch;
   2039     ++f->_ptr_addr;
   2040     --f->_cnt;
   2041     if (f->_flag & IONBF) {
   2042         if (wrapper_fflush(mem, fp_addr) != 0) {
   2043             return -1;
   2044         }
   2045         f->_cnt = 0;
   2046     }
   2047     return ch;
   2048 }
   2049 
   2050 int wrapper_ungetc(uint8_t* mem, int ch, uint32_t fp_addr) {
   2051     struct FILE_irix* f = (struct FILE_irix*)&MEM_U32(fp_addr);
   2052     if (ch == -1 || f->_ptr_addr == f->_base_addr) {
   2053         return -1;
   2054     }
   2055     --f->_ptr_addr;
   2056     MEM_U8(f->_ptr_addr) = (uint8_t)ch;
   2057     ++f->_cnt;
   2058     f->_flag &= ~IOEOF;
   2059     return ch;
   2060 }
   2061 
   2062 uint32_t wrapper_gets(uint8_t* mem, uint32_t str_addr) {
   2063     uint32_t p, str0 = str_addr;
   2064     int n;
   2065 
   2066     for (;;) {
   2067         if (STDIN->_cnt <= 0) {
   2068             if (wrapper___filbuf(mem, STDIN_ADDR) == -1) {
   2069                 if (str0 == str_addr) {
   2070                     return 0;
   2071                 }
   2072                 break;
   2073             }
   2074             --STDIN->_ptr_addr;
   2075             ++STDIN->_cnt;
   2076         }
   2077         n = STDIN->_cnt;
   2078         if ((p = wrapper_memccpy(mem, str_addr, STDIN->_ptr_addr, '\n', n)) != 0) {
   2079             n = p - str_addr;
   2080         }
   2081         str_addr += n;
   2082         STDIN->_cnt -= n;
   2083         STDIN->_ptr_addr += n;
   2084         // bufsync
   2085         if (p != 0) {
   2086             // found '\n' in buffer
   2087             --str_addr;
   2088             break;
   2089         }
   2090     }
   2091     MEM_S8(str_addr) = '\0';
   2092     return str0;
   2093 }
   2094 
   2095 uint32_t wrapper_fread(uint8_t* mem, uint32_t data_addr, uint32_t size, uint32_t count, uint32_t fp_addr) {
   2096     struct FILE_irix* f = (struct FILE_irix*)&MEM_U32(fp_addr);
   2097     int nleft = count * size;
   2098     int n;
   2099     for (;;) {
   2100         if (f->_cnt <= 0) {
   2101             if (wrapper___filbuf(mem, fp_addr) == -1) {
   2102                 return count - (nleft + size - 1) / size;
   2103             }
   2104             --f->_ptr_addr;
   2105             ++f->_cnt;
   2106         }
   2107         n = MIN(nleft, f->_cnt);
   2108         data_addr = wrapper_memcpy(mem, data_addr, f->_ptr_addr, n) + n;
   2109         f->_cnt -= n;
   2110         f->_ptr_addr += n;
   2111         if ((nleft -= n) <= 0) {
   2112             return count;
   2113         }
   2114     }
   2115 }
   2116 
   2117 uint32_t wrapper_fwrite(uint8_t* mem, uint32_t data_addr, uint32_t size, uint32_t count, uint32_t fp_addr) {
   2118     struct FILE_irix* f = (struct FILE_irix*)&MEM_U32(fp_addr);
   2119     if (size > 0 && count > 0 && f->_base_addr == 0) {
   2120         file_assign_buffer(mem, f);
   2121         f->_cnt = bufendtab[f - (struct FILE_irix*)&MEM_U32(IOB_ADDR)];
   2122         f->_flag |= IOWRT;
   2123     }
   2124     uint32_t num_written = 0;
   2125     while (count--) {
   2126         uint32_t s = size;
   2127         while (s > 0) {
   2128             uint32_t to_write = f->_cnt;
   2129             if (s < to_write) {
   2130                 to_write = s;
   2131             }
   2132             if (f->_cnt == 0) {
   2133                 if (wrapper_fflush(mem, fp_addr) != 0) {
   2134                     return num_written;
   2135                 }
   2136             }
   2137             wrapper_memcpy(mem, f->_ptr_addr, data_addr, to_write);
   2138             data_addr += to_write;
   2139             f->_ptr_addr += to_write;
   2140             f->_cnt -= to_write;
   2141             s -= to_write;
   2142         }
   2143         num_written++;
   2144     }
   2145     if (f->_flag & IONBF) {
   2146         wrapper_fflush(mem, fp_addr); // TODO check error return value
   2147     }
   2148     return num_written;
   2149 }
   2150 
   2151 int wrapper_fputs(uint8_t* mem, uint32_t str_addr, uint32_t fp_addr) {
   2152     assert(str_addr != 0);
   2153 
   2154     uint32_t len = wrapper_strlen(mem, str_addr);
   2155     uint32_t ret = wrapper_fwrite(mem, str_addr, 1, len, fp_addr);
   2156     return ret == 0 && len != 0 ? -1 : 0;
   2157 }
   2158 
   2159 int wrapper_puts(uint8_t* mem, uint32_t str_addr) {
   2160     int ret = wrapper_fputs(mem, str_addr, STDOUT_ADDR);
   2161     if (ret != 0) {
   2162         return ret;
   2163     }
   2164     struct FILE_irix* f = STDOUT;
   2165     if (--f->_cnt < 0) {
   2166         if (wrapper___flsbuf(mem, '\n', STDOUT_ADDR) != '\n') {
   2167             return -1;
   2168         }
   2169     } else {
   2170         MEM_S8(f->_ptr_addr) = '\n';
   2171         ++f->_ptr_addr;
   2172     }
   2173     return 0;
   2174 }
   2175 
   2176 uint32_t wrapper_getcwd(uint8_t* mem, uint32_t buf_addr, uint32_t size) {
   2177     char buf[size];
   2178     if (getcwd(buf, size) == NULL) {
   2179         MEM_U32(ERRNO_ADDR) = errno;
   2180         return 0;
   2181     } else {
   2182         if (buf_addr == 0) {
   2183             buf_addr = wrapper_malloc(mem, size);
   2184         }
   2185         strcpy1(mem, buf_addr, buf);
   2186         return buf_addr;
   2187     }
   2188 }
   2189 
   2190 int wrapper_time(uint8_t* mem, uint32_t tloc_addr) {
   2191     time_t ret = time(NULL);
   2192     if (ret == (time_t)-1) {
   2193         MEM_U32(ERRNO_ADDR) = errno;
   2194     } else if (tloc_addr != 0) {
   2195         MEM_S32(tloc_addr) = ret;
   2196     }
   2197     return ret;
   2198 }
   2199 
   2200 void wrapper_bzero(uint8_t* mem, uint32_t str_addr, uint32_t n) {
   2201     while (n--) {
   2202         MEM_U8(str_addr) = 0;
   2203         ++str_addr;
   2204     }
   2205 }
   2206 
   2207 int wrapper_fp_class_d(double d) {
   2208     union {
   2209         uint32_t w[2];
   2210         double d;
   2211     } bits;
   2212     bits.d = d;
   2213     uint32_t a2 = bits.w[1];
   2214     uint32_t a1 = a2 >> 20;
   2215     uint32_t a0 = a1;
   2216     a2 &= 0xfffff;
   2217     uint32_t a3 = bits.w[0];
   2218     a1 &= 0x7ff;
   2219     a0 &= 0x800;
   2220     if (a1 == 0x7ff) {
   2221         if (a2 == 0 && a3 == 0) {
   2222             return a0 == 0 ? 2 : 3;
   2223         }
   2224         a0 = a2 & 0x80000;
   2225         return a0 == 0 ? 1 : 0;
   2226     }
   2227     if (a1 == 0) {
   2228         if (a2 == 0 && a3 == 0) {
   2229             return a0 == 0 ? 8 : 9;
   2230         }
   2231         return a0 == 0 ? 6 : 7;
   2232     }
   2233     return a0 == 0 ? 4 : 5;
   2234 }
   2235 
   2236 double wrapper_ldexp(double d, int i) {
   2237     return ldexp(d, i);
   2238 }
   2239 
   2240 uint64_t wrapper___ll_mul(uint64_t a0, uint64_t a1) {
   2241     return a0 * a1;
   2242 }
   2243 
   2244 int64_t wrapper___ll_div(int64_t a0, int64_t a1) {
   2245     return a0 / a1;
   2246 }
   2247 
   2248 int64_t wrapper___ll_rem(uint64_t a0, int64_t a1) {
   2249     return a0 % a1;
   2250 }
   2251 
   2252 uint64_t wrapper___ll_lshift(uint64_t a0, uint64_t shift) {
   2253     return a0 << (shift & 0x3F);
   2254 }
   2255 
   2256 int64_t wrapper___ll_rshift(int64_t a0, uint64_t shift) {
   2257     return a0 >> (shift & 0x3F);
   2258 }
   2259 
   2260 uint64_t wrapper___ull_div(uint64_t a0, uint64_t a1) {
   2261     return a0 / a1;
   2262 }
   2263 
   2264 uint64_t wrapper___ull_rem(uint64_t a0, uint64_t a1) {
   2265     return a0 % a1;
   2266 }
   2267 
   2268 uint64_t wrapper___ull_rshift(uint64_t a0, uint64_t shift) {
   2269     return a0 >> (shift & 0x3f);
   2270 }
   2271 
   2272 uint64_t wrapper___d_to_ull(double d) {
   2273     return d;
   2274 }
   2275 
   2276 int64_t wrapper___d_to_ll(double d) {
   2277     return d;
   2278 }
   2279 
   2280 uint64_t wrapper___f_to_ull(float f) {
   2281     return f;
   2282 }
   2283 
   2284 int64_t wrapper___f_to_ll(float f) {
   2285     return f;
   2286 }
   2287 
   2288 float wrapper___ull_to_f(uint64_t v) {
   2289     return v;
   2290 }
   2291 
   2292 float wrapper___ll_to_f(int64_t v) {
   2293     return v;
   2294 }
   2295 
   2296 double wrapper___ull_to_d(uint64_t v) {
   2297     return v;
   2298 }
   2299 
   2300 double wrapper___ll_to_d(int64_t v) {
   2301     return v;
   2302 }
   2303 
   2304 void wrapper_abort(uint8_t* mem) {
   2305     abort();
   2306 }
   2307 
   2308 void wrapper_exit(uint8_t* mem, int status) {
   2309     final_cleanup(mem);
   2310     exit(status);
   2311 }
   2312 
   2313 void wrapper__exit(uint8_t* mem, int status) {
   2314     assert(0 && "_exit not implemented"); // exit() is already overridden
   2315 }
   2316 
   2317 void wrapper__cleanup(uint8_t* mem) {
   2318 }
   2319 
   2320 uint32_t wrapper__rld_new_interface(uint8_t* mem, uint32_t operation, uint32_t sp) {
   2321     assert(0 && "_rld_new_interface not implemented");
   2322     return 0;
   2323 }
   2324 
   2325 void wrapper__exithandle(uint8_t* mem) {
   2326     assert(0 && "_exithandle not implemented");
   2327 }
   2328 
   2329 int wrapper__prctl(uint8_t* mem, int operation, uint32_t sp) {
   2330     assert(0 && "_prctl not implemented");
   2331     return 0;
   2332 }
   2333 
   2334 double wrapper__atod(uint8_t* mem, uint32_t buffer_addr, int ndigits, int dexp) {
   2335     // ftp://atoum.hst.nerim.net/irix/src/irix-6.5.5-src/6.5.5/m/irix/lib/libc/src/math/atod.c
   2336     assert(0 && "_atod not implemented");
   2337     return 0.0;
   2338 }
   2339 
   2340 int wrapper_pathconf(uint8_t* mem, uint32_t path_addr, int name) {
   2341     STRING(path)
   2342     if (name == 5) {
   2343         errno = 0;
   2344         int ret = pathconf(path, _PC_PATH_MAX);
   2345         if (errno != 0) {
   2346             MEM_U32(ERRNO_ADDR) = errno;
   2347         }
   2348         return ret;
   2349     }
   2350     assert(0 && "pathconf not implemented for the specific 'name'");
   2351     return 0;
   2352 }
   2353 
   2354 uint32_t wrapper_getenv(uint8_t* mem, uint32_t name_addr) {
   2355     STRING(name);
   2356     const char* value = getenv(name);
   2357     if (value == NULL) {
   2358         return 0;
   2359     }
   2360     size_t value_size = strlen(value) + 1;
   2361     uint32_t buf_addr = wrapper_malloc(mem, value_size);
   2362     strcpy1(mem, buf_addr, value);
   2363     return buf_addr;
   2364 }
   2365 
   2366 uint32_t wrapper_gettxt(uint8_t* mem, uint32_t msgid_addr, uint32_t default_str_addr) {
   2367     // Return default for now
   2368     return default_str_addr;
   2369 }
   2370 
   2371 uint32_t wrapper_setlocale(uint8_t* mem, int category, uint32_t locale_addr) {
   2372     assert(locale_addr != 0);
   2373     STRING(locale)
   2374     assert(category == 6); // LC_ALL
   2375     char* ret = setlocale(LC_ALL, locale);
   2376     // Let's hope the caller doesn't use the return value
   2377     return 0;
   2378 }
   2379 
   2380 uint32_t wrapper_mmap(uint8_t* mem, uint32_t addr, uint32_t length, int prot, int flags, int fd, int offset) {
   2381     if (addr == 0 && prot == (prot & 3) && flags == 2) {
   2382         // Read/write, map private. Just make a copy.
   2383         uint8_t* ptr = mmap(0, length, PROT_READ, MAP_PRIVATE, fd, offset);
   2384         if (ptr == MAP_FAILED) {
   2385             MEM_U32(ERRNO_ADDR) = errno;
   2386             return -1;
   2387         }
   2388         uint32_t out = wrapper_malloc(mem, length);
   2389         for (uint32_t i = 0; i < length; i++) {
   2390             MEM_S8(out + i) = ptr[i];
   2391         }
   2392         munmap(ptr, length);
   2393         return out;
   2394     }
   2395     assert(0 && "mmap not implemented");
   2396     return 0;
   2397 }
   2398 
   2399 int wrapper_munmap(uint8_t* mem, uint32_t addr, uint32_t length) {
   2400     return 0;
   2401 }
   2402 
   2403 int wrapper_mprotect(uint8_t* mem, uint32_t addr, uint32_t length, int prot) {
   2404     assert(0 && "mprotect not implemented");
   2405     return 0;
   2406 }
   2407 
   2408 int wrapper_sysconf(uint8_t* mem, int name) {
   2409     assert(0 && "sysconf not implemented");
   2410     return 0;
   2411 }
   2412 
   2413 int wrapper_getpagesize(uint8_t* mem) {
   2414     return 4096;
   2415 }
   2416 
   2417 int wrapper_strerror(uint8_t* mem, int errnum) {
   2418     errno = errnum;
   2419     perror("strerror");
   2420     assert(0 && "strerror not implemented");
   2421     return 0;
   2422 }
   2423 
   2424 int wrapper_ioctl(uint8_t* mem, int fd, uint32_t request, uint32_t sp) {
   2425     assert(0 && "ioctl not implemented");
   2426     return 0;
   2427 }
   2428 
   2429 int wrapper_fcntl(uint8_t* mem, int fd, int cmd, uint32_t sp) {
   2430     assert(0 && "fcntl not implemented");
   2431     return 0;
   2432 }
   2433 
   2434 static void signal_handler(int signum) {
   2435     uint32_t level = signal_context.recursion_level++;
   2436     uint8_t* mem = signal_context.handlers[signum].mem;
   2437     uint32_t fp_dest = signal_context.handlers[signum].fp_dest;
   2438     uint32_t sp = SIGNAL_HANDLER_STACK_START - 16 - level * 0x1000;
   2439     signal_context.handlers[signum].trampoline(mem, sp, signum, 0, 0, 0, fp_dest);
   2440     signal_context.recursion_level--;
   2441 }
   2442 
   2443 uint32_t wrapper_signal(uint8_t* mem, int signum,
   2444                         uint64_t (*trampoline)(uint8_t* mem, uint32_t sp, uint32_t a0, uint32_t a1, uint32_t a2,
   2445                                                uint32_t a3, uint32_t fp_dest),
   2446                         uint32_t handler_addr, uint32_t sp) {
   2447     return 0;
   2448 }
   2449 
   2450 uint32_t wrapper_sigset(uint8_t* mem, int signum,
   2451                         uint64_t (*trampoline)(uint8_t* mem, uint32_t sp, uint32_t a0, uint32_t a1, uint32_t a2,
   2452                                                uint32_t a3, uint32_t fp_dest),
   2453                         uint32_t disp_addr, uint32_t sp) {
   2454     void (*handler)(int) = signal_handler;
   2455 
   2456     if ((int)disp_addr >= -1 && (int)disp_addr <= 1) {
   2457         // SIG_DFL etc.
   2458         handler = (void (*)(int))(intptr_t)(int)disp_addr;
   2459     }
   2460 
   2461     switch (signum) {
   2462         case 2:
   2463             signum = SIGINT;
   2464             break;
   2465         case 13:
   2466             signum = SIGPIPE;
   2467             break;
   2468         case 15:
   2469             signum = SIGTERM;
   2470             break;
   2471         default:
   2472             assert(0 && "sigset with this signum not implemented");
   2473             break;
   2474     }
   2475 
   2476     signal_context.handlers[signum].trampoline = trampoline;
   2477     signal_context.handlers[signum].mem = mem;
   2478     signal_context.handlers[signum].fp_dest = disp_addr;
   2479 
   2480     return (uint32_t)(uintptr_t)sigset(signum, handler); // for now only support SIG_DFL etc. as return value
   2481 }
   2482 
   2483 int wrapper_get_fpc_csr(uint8_t* mem) {
   2484     return 0;
   2485 }
   2486 
   2487 int wrapper_set_fpc_csr(uint8_t* mem, int csr) {
   2488     return 0;
   2489 }
   2490 
   2491 int wrapper_setjmp(uint8_t* mem, uint32_t addr) {
   2492     return 0;
   2493 }
   2494 
   2495 void wrapper_longjmp(uint8_t* mem, uint32_t addr, int status) {
   2496     assert(0 && "longjmp not implemented");
   2497 }
   2498 
   2499 uint32_t wrapper_tempnam(uint8_t *mem, uint32_t dir_addr, uint32_t pfx_addr) {
   2500     STRING(dir)
   2501     STRING(pfx)
   2502     char *ret = tempnam(dir, pfx);
   2503     char *ret_saved = ret;
   2504     if (ret == NULL) {
   2505         MEM_U32(ERRNO_ADDR) = errno;
   2506         return 0;
   2507     }
   2508     size_t len = strlen(ret) + 1;
   2509     uint32_t ret_addr = wrapper_malloc(mem, len);
   2510     uint32_t pos = ret_addr;
   2511     while (len--) {
   2512         MEM_S8(pos) = *ret;
   2513         ++pos;
   2514         ++ret;
   2515     }
   2516     free(ret_saved);
   2517     return ret_addr;
   2518 }
   2519 
   2520 uint32_t wrapper_tmpnam(uint8_t *mem, uint32_t str_addr) {
   2521     char buf[1024];
   2522     assert(str_addr != 0 && "s NULL not implemented for tmpnam");
   2523     char *ret = tmpnam(buf);
   2524     if (ret == NULL) {
   2525         return 0;
   2526     } else {
   2527         strcpy1(mem, str_addr, ret);
   2528         return str_addr;
   2529     }
   2530 }
   2531 
   2532 uint32_t wrapper_mktemp(uint8_t *mem, uint32_t template_addr) {
   2533     STRING(template)
   2534     mktemp(template);
   2535     strcpy1(mem, template_addr, template);
   2536     return template_addr;
   2537 }
   2538 
   2539 int wrapper_mkstemp(uint8_t* mem, uint32_t name_addr) {
   2540     STRING(name)
   2541     int fd = mkstemp(name);
   2542     if (fd < 0) {
   2543         MEM_U32(ERRNO_ADDR) = errno;
   2544     } else {
   2545         strcpy1(mem, name_addr, name);
   2546     }
   2547     return fd;
   2548 }
   2549 
   2550 uint32_t wrapper_tmpfile(uint8_t* mem) {
   2551     // create and fopen a temporary file that is removed when the program exits
   2552     const char* tmpdir = getenv("TMPDIR");
   2553     if (tmpdir == NULL) {
   2554         tmpdir = "/tmp";
   2555     }
   2556 
   2557     char name[PATH_MAX + 1] = { 0 };
   2558     int n = snprintf(name, sizeof(name), "%s/copt_temp_XXXXXX", tmpdir);
   2559     if (n < 0 || n >= sizeof(name)) {
   2560         // This isn't the best errno code, but it is one that can be returned by tmpfile
   2561         MEM_U32(ERRNO_ADDR) = EACCES;
   2562         return 0;
   2563     }
   2564 
   2565     int fd = mkstemp(name);
   2566     if (fd < 0) {
   2567         MEM_U32(ERRNO_ADDR) = errno;
   2568         return 0;
   2569     }
   2570 
   2571     // the file will be removed from disk when it's closed later
   2572     unlink(name);
   2573 
   2574     // fdopen:
   2575     uint32_t ret = init_file(mem, fd, -1, NULL, "w+");
   2576     if (ret == 0) {
   2577         close(fd);
   2578     }
   2579     return ret;
   2580 }
   2581 
   2582 int wrapper_wait(uint8_t* mem, uint32_t wstatus_addr) {
   2583     int wstatus;
   2584     pid_t ret = wait(&wstatus);
   2585     MEM_S32(wstatus_addr) = wstatus;
   2586     return ret;
   2587 }
   2588 
   2589 int wrapper_kill(uint8_t* mem, int pid, int sig) {
   2590     int ret = kill(pid, sig);
   2591     if (ret != 0) {
   2592         MEM_U32(ERRNO_ADDR) = errno;
   2593     }
   2594     return ret;
   2595 }
   2596 
   2597 int wrapper_execlp(uint8_t* mem, uint32_t file_addr, uint32_t sp) {
   2598     uint32_t argv_addr = sp + 4;
   2599     return wrapper_execvp(mem, file_addr, argv_addr);
   2600 }
   2601 
   2602 int wrapper_execv(uint8_t* mem, uint32_t pathname_addr, uint32_t argv_addr) {
   2603     STRING(pathname)
   2604     uint32_t argc = 0;
   2605     while (MEM_U32(argv_addr + argc * 4) != 0) {
   2606         ++argc;
   2607     }
   2608     char* argv[argc + 1];
   2609     for (uint32_t i = 0; i < argc; i++) {
   2610         uint32_t str_addr = MEM_U32(argv_addr + i * 4);
   2611         uint32_t len = wrapper_strlen(mem, str_addr) + 1;
   2612         argv[i] = (char*)malloc(len);
   2613         char* pos = argv[i];
   2614         while (len--) {
   2615             *pos++ = MEM_S8(str_addr);
   2616             ++str_addr;
   2617         }
   2618     }
   2619     argv[argc] = NULL;
   2620     execv(pathname, argv);
   2621     MEM_U32(ERRNO_ADDR) = errno;
   2622     for (uint32_t i = 0; i < argc; i++) {
   2623         free(argv[i]);
   2624     }
   2625     return -1;
   2626 }
   2627 
   2628 int wrapper_execvp(uint8_t* mem, uint32_t file_addr, uint32_t argv_addr) {
   2629     STRING(file)
   2630     uint32_t argc = 0;
   2631     while (MEM_U32(argv_addr + argc * 4) != 0) {
   2632         ++argc;
   2633     }
   2634     char* argv[argc + 1];
   2635     for (uint32_t i = 0; i < argc; i++) {
   2636         uint32_t str_addr = MEM_U32(argv_addr + i * 4);
   2637         uint32_t len = wrapper_strlen(mem, str_addr) + 1;
   2638         argv[i] = (char*)malloc(len);
   2639         char* pos = argv[i];
   2640         while (len--) {
   2641             *pos++ = MEM_S8(str_addr);
   2642             ++str_addr;
   2643         }
   2644     }
   2645     argv[argc] = NULL;
   2646 
   2647     char rfile[PATH_MAX + 1];
   2648     redirect_path(rfile, file, "/usr/lib", usr_lib_redirect);
   2649 
   2650     execvp(rfile, argv);
   2651 
   2652     MEM_U32(ERRNO_ADDR) = errno;
   2653     for (uint32_t i = 0; i < argc; i++) {
   2654         free(argv[i]);
   2655     }
   2656     return -1;
   2657 }
   2658 
   2659 int wrapper_fork(uint8_t* mem) {
   2660     int ret = fork();
   2661     if (ret == -1) {
   2662         MEM_U32(ERRNO_ADDR) = errno;
   2663     }
   2664     return ret;
   2665 }
   2666 
   2667 int wrapper_system(uint8_t* mem, uint32_t command_addr) {
   2668     STRING(command)
   2669     return system(command); // no errno
   2670 }
   2671 
   2672 static int name_compare(uint8_t* mem, uint32_t a_addr, uint32_t b_addr) {
   2673     return wrapper_strcmp(mem, MEM_U32(a_addr), MEM_U32(b_addr));
   2674 }
   2675 
   2676 static uint32_t tsearch_tfind(uint8_t* mem, uint32_t key_addr, uint32_t rootp_addr, uint32_t compar_addr, bool insert) {
   2677     if (rootp_addr == 0) {
   2678         return 0;
   2679     }
   2680     while (MEM_U32(rootp_addr) != 0) {
   2681         uint32_t node_addr = MEM_U32(rootp_addr);
   2682         int r = name_compare(mem, key_addr, MEM_U32(node_addr));
   2683         if (r == 0) {
   2684             return node_addr;
   2685         }
   2686         rootp_addr = r < 0 ? node_addr + 4 : node_addr + 8;
   2687     }
   2688     if (insert) {
   2689         uint32_t node_addr = wrapper_malloc(mem, 12);
   2690         if (node_addr != 0) {
   2691             MEM_U32(rootp_addr) = node_addr;
   2692             MEM_U32(node_addr) = key_addr;
   2693             MEM_U32(node_addr + 4) = 0;
   2694             MEM_U32(node_addr + 8) = 0;
   2695             return node_addr;
   2696         }
   2697     }
   2698     return 0;
   2699 }
   2700 
   2701 uint32_t wrapper_tsearch(uint8_t* mem, uint32_t key_addr, uint32_t rootp_addr, uint32_t compar_addr) {
   2702     return tsearch_tfind(mem, key_addr, rootp_addr, compar_addr, true);
   2703 }
   2704 
   2705 uint32_t wrapper_tfind(uint8_t* mem, uint32_t key_addr, uint32_t rootp_addr, uint32_t compar_addr) {
   2706     return tsearch_tfind(mem, key_addr, rootp_addr, compar_addr, false);
   2707 }
   2708 
   2709 // qsort implementation from SGI libc, originally derived from
   2710 // https://people.ece.ubc.ca/~eddieh/glu_dox/d7/da4/qsort_8c_source.html (public domain)
   2711 
   2712 #define CMP(x, y) (int32_t)(trampoline(mem, sp, (x), (y), 0, 0, compare_addr) >> 32)
   2713 
   2714 static void qst(uint8_t* mem, uint32_t start, uint32_t end, fptr_trampoline trampoline, uint32_t compare_addr,
   2715                 uint32_t sp, uint32_t size, uint32_t minSortSize, uint32_t medianOfThreeThreshold);
   2716 
   2717 uint32_t wrapper_qsort(uint8_t* mem, uint32_t base_addr, uint32_t count, uint32_t size, fptr_trampoline trampoline,
   2718                        uint32_t compare_addr, uint32_t sp) {
   2719     uint32_t end;
   2720     uint32_t it;
   2721     uint32_t prevIt;
   2722     uint32_t byteIt;
   2723     uint32_t hi;
   2724     uint32_t insPos;
   2725     uint32_t cur;
   2726     uint32_t smallest;
   2727     uint8_t temp;
   2728 
   2729     if (count < 2) {
   2730         return 0;
   2731     }
   2732 
   2733     end = base_addr + (count * size);
   2734 
   2735     if (count >= 4) {
   2736         // run a rough quicksort
   2737         qst(mem, base_addr, end, trampoline, compare_addr, sp, size, size * 4, size * 6);
   2738         // the smallest element will be one of the first 4
   2739         hi = base_addr + size * 4;
   2740     } else {
   2741         hi = end;
   2742     }
   2743 
   2744     // Find the smallest element and swap it to the front
   2745     smallest = base_addr;
   2746     for (it = base_addr + size; it < hi; it += size) {
   2747         if (CMP(smallest, it) > 0) {
   2748             smallest = it;
   2749         }
   2750     }
   2751 
   2752     if (smallest != base_addr) {
   2753         for (it = base_addr; it < base_addr + size; smallest++, it++) {
   2754             temp = MEM_U8(smallest);
   2755             MEM_U8(smallest) = MEM_U8(it);
   2756             MEM_U8(it) = temp;
   2757         }
   2758     }
   2759 
   2760     // Do insertion sort on the rest of the elements
   2761     for (cur = base_addr + size; cur < end; cur += size) {
   2762 
   2763         // Find where cur should go
   2764         insPos = cur - size;
   2765         while (CMP(insPos, cur) > 0) {
   2766             if (base_addr == insPos) {
   2767                 // This isn't logically possible, because we've put the smallest element first.
   2768                 // But it can happen if the comparator function is faulty, and it's best not to
   2769                 // write out of bounds in that situation.
   2770                 break;
   2771             }
   2772             insPos -= size;
   2773         }
   2774         insPos += size;
   2775 
   2776         if (insPos == cur) {
   2777             continue;
   2778         }
   2779 
   2780         for (byteIt = cur + size; --byteIt >= cur;) {
   2781             temp = MEM_U8(byteIt);
   2782             prevIt = byteIt;
   2783             for (it = byteIt - size; it >= insPos; it -= size) {
   2784                 MEM_U8(prevIt) = MEM_U8(it);
   2785                 prevIt = it;
   2786             }
   2787             MEM_U8(prevIt) = temp;
   2788         }
   2789     }
   2790 
   2791     return 0;
   2792 }
   2793 
   2794 static void qst(uint8_t* mem, uint32_t start, uint32_t end, fptr_trampoline trampoline, uint32_t compare_addr,
   2795                 uint32_t sp, uint32_t size, uint32_t minSortSize, uint32_t medianOfThreeThreshold) {
   2796     uint32_t sizeAfterPivot;
   2797     uint32_t sizeBeforePivot;
   2798     uint32_t totalSize;
   2799     int32_t i;
   2800     uint32_t afterPivot;
   2801     uint32_t last;
   2802     uint32_t newPartitionFirst;
   2803     uint32_t median;
   2804     uint32_t partitionFirst;
   2805     uint32_t partitionLast;
   2806     uint32_t pivot;
   2807     uint32_t swapWith;
   2808     uint8_t temp;
   2809 
   2810     totalSize = end - start;
   2811     do {
   2812         last = end - size;
   2813         pivot = partitionFirst = (((totalSize / size) >> 1) * size) + start;
   2814         if (totalSize >= medianOfThreeThreshold) {
   2815             // compute median of three
   2816             median = CMP(start, pivot) > 0 ? start : pivot;
   2817             if (CMP(median, last) > 0) {
   2818                 median = median == start ? pivot : start;
   2819                 median = CMP(median, last) < 0 ? last : median;
   2820             }
   2821 
   2822             // swap the median so it ends up in the middle
   2823             if (median != pivot) {
   2824                 // Fake-match: use partitionFirst here instead of e.g. swapWith.
   2825                 i = size;
   2826                 do {
   2827                     temp = MEM_U8(partitionFirst);
   2828                     MEM_U8(partitionFirst) = MEM_U8(median);
   2829                     MEM_U8(median) = temp;
   2830                     partitionFirst++;
   2831                     median++;
   2832                     i--;
   2833                 } while (i != 0);
   2834             }
   2835         }
   2836 
   2837         // Partition the elements start, ..., pivot, ..., last, such that values smaller than the
   2838         // pivot are on the left, and values greater than the pivot are on the right (equal ones can
   2839         // go wherever). The pivot may end up getting swapped into another position in the process.
   2840 
   2841         partitionFirst = start;
   2842         partitionLast = last;
   2843 
   2844         // Loop invariant: Elements partitionFirst, ..., partitionLast remain to be partitioned,
   2845         // and pivot is in that range.
   2846         for (;;) {
   2847             while (partitionFirst < pivot && CMP(partitionFirst, pivot) < 0) {
   2848                 // Skip over smaller values on the left.
   2849                 partitionFirst += size;
   2850             }
   2851 
   2852             while (pivot < partitionLast) {
   2853                 if (CMP(pivot, partitionLast) < 0) {
   2854                     // Skip over greater values on the right.
   2855                     partitionLast -= size;
   2856                 } else {
   2857                     // We have found a value we cannot skip over. Put it at the front.
   2858                     // If the pivot was at the front, it gets swapped to the last position,
   2859                     // otherwise, the value at the front is something we know isn't smaller
   2860                     // than the pivot, so we can skip partitioning it.
   2861                     newPartitionFirst = partitionFirst + size;
   2862                     if (partitionFirst == pivot) {
   2863                         swapWith = partitionLast;
   2864                         pivot = partitionLast;
   2865                     } else {
   2866                         swapWith = partitionLast;
   2867                         partitionLast -= size;
   2868                     }
   2869                     goto swapFront;
   2870                 }
   2871             }
   2872 
   2873             // We have hit up against the pivot at the end. Swap it to the front to we can
   2874             // skip over it. The front element is known to not be smaller than the pivot,
   2875             // except if the pivot is at the front also, i.e. if the range has been reduced
   2876             // down to size 1 -- in that case it's time to break out of the loop.
   2877             partitionLast -= size;
   2878             if (partitionFirst == pivot) {
   2879                 break;
   2880             }
   2881             swapWith = pivot;
   2882             pivot = partitionFirst;
   2883             newPartitionFirst = partitionFirst;
   2884 
   2885         swapFront:
   2886             i = size;
   2887             do {
   2888                 temp = MEM_U8(partitionFirst);
   2889                 MEM_U8(partitionFirst) = MEM_U8(swapWith);
   2890                 MEM_U8(swapWith) = temp;
   2891                 partitionFirst++;
   2892                 swapWith++;
   2893                 i--;
   2894             } while (i != 0);
   2895             partitionFirst = newPartitionFirst;
   2896         }
   2897 
   2898         afterPivot = pivot + size;
   2899         sizeBeforePivot = pivot - start;
   2900         sizeAfterPivot = end - afterPivot;
   2901         totalSize = sizeBeforePivot;
   2902         if (sizeAfterPivot >= sizeBeforePivot) {
   2903             if (sizeBeforePivot >= minSortSize) {
   2904                 qst(mem, start, pivot, trampoline, compare_addr, sp, size, minSortSize, medianOfThreeThreshold);
   2905             }
   2906             start = afterPivot;
   2907             totalSize = sizeAfterPivot;
   2908         } else {
   2909             if (sizeAfterPivot >= minSortSize) {
   2910                 qst(mem, afterPivot, end, trampoline, compare_addr, sp, size, minSortSize, medianOfThreeThreshold);
   2911             }
   2912             end = pivot;
   2913         }
   2914     } while (totalSize >= minSortSize);
   2915 }
   2916 
   2917 #undef CMP
   2918 
   2919 uint32_t wrapper_regcmp(uint8_t* mem, uint32_t string1_addr, uint32_t sp) {
   2920     STRING(string1);
   2921     fprintf(stderr, "regex string: %s\n", string1);
   2922     assert(0 && "regcmp not implemented");
   2923     return 0;
   2924 }
   2925 
   2926 uint32_t wrapper_regex(uint8_t* mem, uint32_t re_addr, uint32_t subject_addr, uint32_t sp) {
   2927     STRING(subject);
   2928     assert(0 && "regex not implemented");
   2929     return 0;
   2930 }
   2931 
   2932 void wrapper___assert(uint8_t* mem, uint32_t assertion_addr, uint32_t file_addr, int line) {
   2933     STRING(assertion)
   2934     STRING(file)
   2935     __assert(assertion, file, line);
   2936 }
   2937 
   2938 union host_doubleword {
   2939     uint64_t ww;
   2940     double d;
   2941 };
   2942 
   2943 union FloatReg FloatReg_from_double(double d) {
   2944     union host_doubleword val;
   2945     union FloatReg floatreg;
   2946 
   2947     val.d = d;
   2948 
   2949     floatreg.w[0] = val.ww & 0xFFFFFFFF;
   2950     floatreg.w[1] = (val.ww >> 32) & 0xFFFFFFFF;
   2951 
   2952     return floatreg;
   2953 }
   2954 
   2955 double double_from_FloatReg(union FloatReg floatreg) {
   2956     union host_doubleword val;
   2957 
   2958     val.ww = floatreg.w[1];
   2959     val.ww <<= 32;
   2960     val.ww |= floatreg.w[0];
   2961     return val.d;
   2962 }
   2963 
   2964 double double_from_memory(uint8_t* mem, uint32_t address) {
   2965     union host_doubleword val;
   2966 
   2967     val.ww = MEM_U32(address);
   2968     val.ww <<= 32;
   2969     val.ww |= MEM_U32(address + 4);
   2970     return val.d;
   2971 }