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 }