kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

linux.c (27592B)


      1 #include <string.h>
      2 
      3 #include "core/slice.h"
      4 #include "emu/emu.h"
      5 #include "obj/format.h"
      6 
      7 #define LINUX_SYS_openat 56u
      8 #define LINUX_SYS_close 57u
      9 #define LINUX_SYS_lseek 62u
     10 #define LINUX_SYS_read 63u
     11 #define LINUX_SYS_write 64u
     12 #define LINUX_SYS_readv 65u
     13 #define LINUX_SYS_writev 66u
     14 #define LINUX_SYS_fstat 80u
     15 #define LINUX_SYS_exit 93u
     16 #define LINUX_SYS_exit_group 94u
     17 #define LINUX_SYS_set_tid_address 96u
     18 #define LINUX_SYS_clock_gettime 113u
     19 #define LINUX_SYS_sched_yield 124u
     20 #define LINUX_SYS_rt_sigaction 134u
     21 #define LINUX_SYS_rt_sigprocmask 135u
     22 #define LINUX_SYS_rt_sigreturn 139u
     23 #define LINUX_SYS_getpid 172u
     24 #define LINUX_SYS_getuid 174u
     25 #define LINUX_SYS_geteuid 175u
     26 #define LINUX_SYS_getgid 176u
     27 #define LINUX_SYS_getegid 177u
     28 #define LINUX_SYS_brk 214u
     29 #define LINUX_SYS_mmap 222u
     30 #define LINUX_SYS_mprotect 226u
     31 #define LINUX_SYS_munmap 215u
     32 
     33 #define LINUX_PROT_READ 1u
     34 #define LINUX_PROT_WRITE 2u
     35 #define LINUX_PROT_EXEC 4u
     36 
     37 #define LINUX_MAP_PRIVATE 0x02u
     38 #define LINUX_MAP_FIXED 0x10u
     39 #define LINUX_MAP_ANONYMOUS 0x20u
     40 #define LINUX_MAP_FIXED_NOREPLACE 0x100000u
     41 
     42 #define LINUX_EBADF 9
     43 #define LINUX_EFAULT 14
     44 #define LINUX_EINVAL 22
     45 #define LINUX_ENOSYS 38
     46 #define LINUX_ENOMEM 12
     47 #define LINUX_EEXIST 17
     48 
     49 #define EMU_LINUX_SIGFRAME_MAGIC 0x53494746524d3031ull
     50 #define EMU_LINUX_SIGFRAME_SIZE 512u
     51 #define EMU_LINUX_SIGFRAME_SAVED_PC 8u
     52 #define EMU_LINUX_SIGFRAME_SIGINFO 32u
     53 #define EMU_LINUX_SIGFRAME_UCONTEXT 64u
     54 #define EMU_LINUX_SIGFRAME_XREGS 128u
     55 #define EMU_LINUX_STACK_SIZE (1u * 1024u * 1024u)
     56 #define EMU_LINUX_BRK_RESERVE (2u * 1024u * 1024u)
     57 #define EMU_LINUX_INITIAL_MMAP_HINT 0x4000000000ull
     58 
     59 typedef struct LinuxSignalAction {
     60   u64 handler;
     61   u64 flags;
     62   u64 restorer;
     63   int installed;
     64 } LinuxSignalAction;
     65 
     66 typedef struct LinuxProcessState {
     67   u64 mmap_hint;
     68   LinuxSignalAction signal_actions[64];
     69 } LinuxProcessState;
     70 
     71 typedef struct LinuxThreadState {
     72   u64 signal_mask;
     73   u64 signal_frame_sp;
     74   EmuTlsBlocks tls_blocks;
     75 } LinuxThreadState;
     76 
     77 static u64 linux_round_up(u64 v, u64 a) { return (v + a - 1u) & ~(a - 1u); }
     78 
     79 static LinuxProcessState* linux_process_state(EmuProcess* process) {
     80   return process ? (LinuxProcessState*)process->os_private : NULL;
     81 }
     82 
     83 static LinuxThreadState* linux_thread_state(EmuThread* thread) {
     84   return thread ? (LinuxThreadState*)thread->os_private : NULL;
     85 }
     86 
     87 static u64 linux_rd64(const u8* p) {
     88   return (u64)p[0] | ((u64)p[1] << 8) | ((u64)p[2] << 16) | ((u64)p[3] << 24) |
     89          ((u64)p[4] << 32) | ((u64)p[5] << 40) | ((u64)p[6] << 48) |
     90          ((u64)p[7] << 56);
     91 }
     92 
     93 static void linux_wr64(u8* p, u64 v) {
     94   u32 i;
     95   for (i = 0; i < 8u; ++i) p[i] = (u8)(v >> (8u * i));
     96 }
     97 
     98 static void linux_free_stack_lists(Heap* heap, u64* argv_addrs, int argc,
     99                                    u64* envp_addrs, int envc) {
    100   if (argv_addrs) heap->free(heap, argv_addrs, sizeof(u64) * (size_t)argc);
    101   if (envp_addrs) heap->free(heap, envp_addrs, sizeof(u64) * (size_t)envc);
    102 }
    103 
    104 static u8 linux_emu_perms(u64 prot) {
    105   u8 perms = 0;
    106   if (prot & LINUX_PROT_READ) perms |= EMU_MEM_READ;
    107   if (prot & LINUX_PROT_WRITE) perms |= EMU_MEM_WRITE;
    108   if (prot & LINUX_PROT_EXEC) perms |= EMU_MEM_EXEC;
    109   return perms;
    110 }
    111 
    112 static KitStatus linux_find_map_region(EmuProcess* process, u64 nbytes,
    113                                        u64 align, u32 purpose, u64* out) {
    114   EmuAddrSpace* as;
    115   LinuxProcessState* ps;
    116   u64 max_va = 0x0000800000000000ull;
    117   u64 min_va;
    118   if (!process || !out) return KIT_INVALID;
    119   ps = linux_process_state(process);
    120   if (!ps) return KIT_INVALID;
    121   as = &process->image.addr_space;
    122   min_va = ps->mmap_hint;
    123   if (purpose == EMU_OS_MAP_TLS && min_va < 0x7000000000ull)
    124     min_va = 0x7000000000ull;
    125   return emu_addr_space_find_gap(as, nbytes, align ? align : as->page_size,
    126                                  min_va, max_va, out);
    127 }
    128 
    129 static void linux_note_map_region(EmuProcess* process, u64 base, u64 nbytes,
    130                                   u32 purpose) {
    131   LinuxProcessState* ps;
    132   u64 page_size;
    133   (void)purpose;
    134   if (!process || !nbytes) return;
    135   ps = linux_process_state(process);
    136   if (!ps) return;
    137   page_size = process->image.addr_space.page_size
    138                   ? process->image.addr_space.page_size
    139                   : 0x1000u;
    140   ps->mmap_hint = linux_round_up(base + nbytes, page_size);
    141 }
    142 
    143 static KitStatus linux_init_process_private(Compiler* c, EmuProcess* process) {
    144   LinuxProcessState* ps;
    145   KitStatus st;
    146   if (!c || !process) return KIT_INVALID;
    147   st = emu_process_os_alloc(c, process, sizeof(LinuxProcessState),
    148                             _Alignof(LinuxProcessState));
    149   if (st != KIT_OK) return st;
    150   ps = linux_process_state(process);
    151   ps->mmap_hint = EMU_LINUX_INITIAL_MMAP_HINT;
    152   return KIT_OK;
    153 }
    154 
    155 static void linux_destroy_process_private(Compiler* c, EmuProcess* process) {
    156   if (!c || !process) return;
    157   emu_process_os_free(c, process, sizeof(LinuxProcessState));
    158 }
    159 
    160 static KitStatus linux_init_thread_private(Compiler* c, EmuProcess* process,
    161                                            EmuThread* thread) {
    162   (void)process;
    163   if (!c || !thread) return KIT_INVALID;
    164   return emu_thread_os_alloc(c, thread, sizeof(LinuxThreadState),
    165                              _Alignof(LinuxThreadState));
    166 }
    167 
    168 static void linux_destroy_thread_private(Compiler* c, EmuThread* thread) {
    169   LinuxThreadState* ts;
    170   if (!c || !thread) return;
    171   ts = linux_thread_state(thread);
    172   if (ts) emu_tls_destroy_blocks(c, &ts->tls_blocks);
    173   emu_thread_os_free(c, thread, sizeof(LinuxThreadState));
    174 }
    175 
    176 static KitStatus linux_init_process(Compiler* c, EmuProcess* process,
    177                                     const EmuLoadOptions* opts,
    178                                     const EmuLoadedImage* image) {
    179   EmuLoadedImage* img;
    180   EmuLoadedObject* main_obj;
    181   Heap* heap;
    182   u64 image_end;
    183   u64 brk_start;
    184   u64 stack_guard_base;
    185   u64 stack_base;
    186   u64 stack_top;
    187   u64 cursor;
    188   u64 at_random_va;
    189   u64 table_bytes;
    190   u64 sp;
    191   u64* argv_addrs = NULL;
    192   u64* envp_addrs = NULL;
    193   int argc = 0;
    194   int envc = 0;
    195   const char* const* p;
    196   u32 i;
    197   u8* tp;
    198   enum {
    199     AT_NULL_ = 0,
    200     AT_PHDR = 3,
    201     AT_PHENT = 4,
    202     AT_PHNUM = 5,
    203     AT_PAGESZ = 6,
    204     AT_ENTRY = 9,
    205     AT_RANDOM = 25
    206   };
    207   struct {
    208     u64 type;
    209     u64 val;
    210   } aux[7];
    211   u32 aux_count = sizeof(aux) / sizeof(aux[0]);
    212 
    213   (void)image;
    214   if (!c || !process || !opts) return KIT_INVALID;
    215   if (!linux_process_state(process)) return KIT_INVALID;
    216   img = &process->image;
    217   if (!img->link_map.nobjects ||
    218       img->link_map.main_object >= img->link_map.nobjects)
    219     return KIT_INVALID;
    220   main_obj = &img->link_map.objects[img->link_map.main_object];
    221   heap = c->ctx->heap;
    222 
    223   image_end = linux_round_up(main_obj->map_end, img->addr_space.page_size);
    224   brk_start = image_end;
    225   stack_guard_base = brk_start + EMU_LINUX_BRK_RESERVE;
    226   stack_base = stack_guard_base + img->addr_space.page_size;
    227   stack_top = stack_base + EMU_LINUX_STACK_SIZE;
    228   if (emu_addr_space_map(&img->addr_space, stack_guard_base,
    229                          img->addr_space.page_size, 0,
    230                          EMU_MAP_GUARD) != KIT_OK ||
    231       emu_addr_space_map(&img->addr_space, stack_base, EMU_LINUX_STACK_SIZE,
    232                          EMU_MEM_READ | EMU_MEM_WRITE, EMU_MAP_ANON) != KIT_OK)
    233     return KIT_ERR;
    234 
    235   if (opts->argv) {
    236     for (p = opts->argv; *p; ++p) ++argc;
    237   }
    238   if (opts->envp) {
    239     for (p = opts->envp; *p; ++p) ++envc;
    240   }
    241   if (argc > 0) {
    242     argv_addrs = (u64*)heap->alloc(heap, sizeof(u64) * (size_t)argc, 8u);
    243     if (!argv_addrs) return KIT_ERR;
    244   }
    245   if (envc > 0) {
    246     envp_addrs = (u64*)heap->alloc(heap, sizeof(u64) * (size_t)envc, 8u);
    247     if (!envp_addrs) {
    248       linux_free_stack_lists(heap, argv_addrs, argc, envp_addrs, envc);
    249       return KIT_ERR;
    250     }
    251   }
    252 
    253   cursor = stack_top;
    254   for (i = 0; i < (u32)argc; ++i) {
    255     size_t slen = slice_from_cstr(opts->argv[i]).len + 1u;
    256     cursor -= slen;
    257     if (emu_addr_space_copy_in(&img->addr_space, cursor, opts->argv[i], slen) !=
    258         KIT_OK) {
    259       linux_free_stack_lists(heap, argv_addrs, argc, envp_addrs, envc);
    260       return KIT_INVALID;
    261     }
    262     argv_addrs[i] = cursor;
    263   }
    264   for (i = 0; i < (u32)envc; ++i) {
    265     size_t slen = slice_from_cstr(opts->envp[i]).len + 1u;
    266     cursor -= slen;
    267     if (emu_addr_space_copy_in(&img->addr_space, cursor, opts->envp[i], slen) !=
    268         KIT_OK) {
    269       linux_free_stack_lists(heap, argv_addrs, argc, envp_addrs, envc);
    270       return KIT_INVALID;
    271     }
    272     envp_addrs[i] = cursor;
    273   }
    274   cursor -= 16u;
    275   {
    276     u8 random[16];
    277     for (i = 0; i < 16u; ++i) random[i] = (u8)(0xa5u ^ i);
    278     if (emu_addr_space_copy_in(&img->addr_space, cursor, random, 16u) !=
    279         KIT_OK) {
    280       linux_free_stack_lists(heap, argv_addrs, argc, envp_addrs, envc);
    281       return KIT_INVALID;
    282     }
    283   }
    284   at_random_va = cursor;
    285   cursor &= ~(u64)0xfu;
    286 
    287   aux[0].type = AT_PHDR;
    288   aux[0].val = img->process_info.headers_vaddr;
    289   aux[1].type = AT_PHENT;
    290   aux[1].val = img->process_info.header_entry_size;
    291   aux[2].type = AT_PHNUM;
    292   aux[2].val = img->process_info.header_count;
    293   aux[3].type = AT_PAGESZ;
    294   aux[3].val = img->addr_space.page_size;
    295   aux[4].type = AT_ENTRY;
    296   aux[4].val = img->entry_pc;
    297   aux[5].type = AT_RANDOM;
    298   aux[5].val = at_random_va;
    299   aux[6].type = AT_NULL_;
    300   aux[6].val = 0;
    301 
    302   table_bytes =
    303       8u + (u64)(argc + 1) * 8u + (u64)(envc + 1) * 8u + (u64)aux_count * 16u;
    304   sp = (cursor - table_bytes) & ~(u64)0xfu;
    305   tp = emu_addr_space_ptr(&img->addr_space, sp, table_bytes, EMU_MEM_WRITE);
    306   if (!tp) {
    307     linux_free_stack_lists(heap, argv_addrs, argc, envp_addrs, envc);
    308     return KIT_INVALID;
    309   }
    310   linux_wr64(tp, (u64)argc);
    311   tp += 8u;
    312   for (i = 0; i < (u32)argc; ++i) {
    313     linux_wr64(tp, argv_addrs[i]);
    314     tp += 8u;
    315   }
    316   linux_wr64(tp, 0);
    317   tp += 8u;
    318   for (i = 0; i < (u32)envc; ++i) {
    319     linux_wr64(tp, envp_addrs[i]);
    320     tp += 8u;
    321   }
    322   linux_wr64(tp, 0);
    323   tp += 8u;
    324   for (i = 0; i < aux_count; ++i) {
    325     linux_wr64(tp, aux[i].type);
    326     tp += 8u;
    327     linux_wr64(tp, aux[i].val);
    328     tp += 8u;
    329   }
    330   linux_free_stack_lists(heap, argv_addrs, argc, envp_addrs, envc);
    331 
    332   img->addr_space.brk_base = brk_start;
    333   img->addr_space.brk_cur = brk_start;
    334   img->addr_space.brk_max = brk_start + EMU_LINUX_BRK_RESERVE;
    335   linux_note_map_region(process, stack_base, EMU_LINUX_STACK_SIZE,
    336                         EMU_OS_MAP_MMAP);
    337   img->initial_sp = sp;
    338   if (emu_dl_init_process(c, process) != KIT_OK) return KIT_ERR;
    339   if (!process->obj_format || !process->obj_format->emu ||
    340       emu_dl_load_dependencies_and_relocate(c, process, opts,
    341                                             process->obj_format->emu) != KIT_OK)
    342     return KIT_ERR;
    343   return KIT_OK;
    344 }
    345 
    346 static KitStatus linux_init_thread(Compiler* c, EmuProcess* process,
    347                                    EmuThread* thread) {
    348   LinuxThreadState* ts;
    349   u32 i;
    350   if (!c || !process || !thread || !thread->cpu) return KIT_INVALID;
    351   ts = linux_thread_state(thread);
    352   if (!ts) return KIT_INVALID;
    353   if (!process->arch || !process->arch->emu || !process->arch->emu->set_tp)
    354     return KIT_UNSUPPORTED;
    355   for (i = 0; i < process->tls_state.nmodules; ++i) {
    356     EmuTlsModule* m = &process->tls_state.modules[i];
    357     u64 page_size = process->image.addr_space.page_size
    358                         ? process->image.addr_space.page_size
    359                         : 0x1000u;
    360     u64 nbytes = linux_round_up(m->memsz ? m->memsz : m->filesz, page_size);
    361     u64 base = 0;
    362     if (!nbytes) continue;
    363     if (!process->os || !process->os->emu_find_map_region ||
    364         process->os->emu_find_map_region(process, nbytes, page_size,
    365                                          EMU_OS_MAP_TLS, &base) != KIT_OK)
    366       return KIT_ERR;
    367     if (emu_addr_space_map(&process->image.addr_space, base, nbytes,
    368                            EMU_MEM_READ | EMU_MEM_WRITE,
    369                            EMU_MAP_ANON) != KIT_OK)
    370       return KIT_ERR;
    371     if (process->os->emu_note_map_region)
    372       process->os->emu_note_map_region(process, base, nbytes, EMU_OS_MAP_TLS);
    373     if (emu_tls_copy_module_image(process, m, base) != KIT_OK) return KIT_ERR;
    374     if (emu_tls_blocks_add(c, &ts->tls_blocks, m->module_id, base, m->memsz) !=
    375         KIT_OK)
    376       return KIT_NOMEM;
    377     if (m->module_id == 1u) process->arch->emu->set_tp(thread, base);
    378   }
    379   return KIT_OK;
    380 }
    381 
    382 static KitStatus linux_deliver_signal(EmuProcess* process, EmuThread* thread,
    383                                       int signo, u64 fault_addr, u64 fault_pc,
    384                                       u64 next_pc, u64* next_pc_out) {
    385   EmuCPUState* cpu = emu_thread_cpu(thread);
    386   LinuxProcessState* ps;
    387   LinuxThreadState* ts;
    388   LinuxSignalAction* act;
    389   u64 sp;
    390   u64 frame_sp;
    391   u64 frame_size;
    392   u64 ctx_size;
    393   u64 stack_align;
    394   u8* frame;
    395   if (!process || !thread || !cpu || !next_pc_out) return KIT_INVALID;
    396   ps = linux_process_state(process);
    397   ts = linux_thread_state(thread);
    398   if (!ps || !ts) return KIT_INVALID;
    399   if (signo <= 0 || signo >= 64) {
    400     emu_cpu_trap_fault(cpu);
    401     *next_pc_out = fault_pc ? fault_pc : next_pc;
    402     return KIT_OK;
    403   }
    404   act = &ps->signal_actions[signo];
    405   if (ts->signal_mask & (1ull << (u32)signo)) {
    406     emu_cpu_trap_fault(cpu);
    407     *next_pc_out = fault_pc ? fault_pc : next_pc;
    408     return KIT_OK;
    409   }
    410   if (!act->installed || !act->handler) {
    411     emu_cpu_trap_fault(cpu);
    412     *next_pc_out = fault_pc ? fault_pc : next_pc;
    413     return KIT_OK;
    414   }
    415   if (!process->arch || !process->arch->emu || !process->arch->emu->get_sp ||
    416       !process->arch->emu->set_sp || !process->arch->emu->signal_context_size ||
    417       !process->arch->emu->save_signal_context ||
    418       !process->arch->emu->set_signal_handler_args ||
    419       !process->arch->emu->signal_stack_align)
    420     return KIT_UNSUPPORTED;
    421   sp = process->arch->emu->get_sp(thread);
    422   ctx_size = process->arch->emu->signal_context_size(process, thread);
    423   stack_align = process->arch->emu->signal_stack_align(process, thread);
    424   if (!ctx_size || !stack_align || (stack_align & (stack_align - 1u)) != 0)
    425     return KIT_UNSUPPORTED;
    426   frame_size = EMU_LINUX_SIGFRAME_XREGS + ctx_size;
    427   if (frame_size < EMU_LINUX_SIGFRAME_SIZE)
    428     frame_size = EMU_LINUX_SIGFRAME_SIZE;
    429   frame_size = linux_round_up(frame_size, stack_align);
    430   frame_sp = (sp - frame_size) & ~(stack_align - 1u);
    431   frame = emu_cpu_va_to_host_perm(cpu, frame_sp, frame_size, EMU_MEM_WRITE);
    432   if (!frame) {
    433     emu_cpu_trap_fault(cpu);
    434     *next_pc_out = fault_pc ? fault_pc : next_pc;
    435     return KIT_OK;
    436   }
    437   memset(frame, 0, (size_t)frame_size);
    438   linux_wr64(frame, EMU_LINUX_SIGFRAME_MAGIC);
    439   linux_wr64(frame + EMU_LINUX_SIGFRAME_SAVED_PC, fault_pc);
    440   linux_wr64(frame + EMU_LINUX_SIGFRAME_SIGINFO, (u64)signo);
    441   linux_wr64(frame + EMU_LINUX_SIGFRAME_SIGINFO + 16u, fault_addr);
    442   if (process->arch->emu->save_signal_context(process, thread,
    443                                               frame + EMU_LINUX_SIGFRAME_XREGS,
    444                                               ctx_size) != KIT_OK)
    445     return KIT_ERR;
    446   ts->signal_frame_sp = frame_sp;
    447   process->arch->emu->set_sp(thread, frame_sp);
    448   if (process->arch->emu->set_signal_handler_args(
    449           process, thread, signo, frame_sp + EMU_LINUX_SIGFRAME_SIGINFO,
    450           frame_sp + EMU_LINUX_SIGFRAME_UCONTEXT) != KIT_OK)
    451     return KIT_ERR;
    452   emu_cpu_clear_trap(cpu);
    453   emu_cpu_set_pc(cpu, act->handler);
    454   *next_pc_out = act->handler;
    455   return KIT_OK;
    456 }
    457 
    458 static KitStatus linux_deliver_fault(EmuProcess* process, EmuThread* thread,
    459                                      const EmuFaultEvent* ev,
    460                                      u64* next_pc_out) {
    461   if (!ev) return KIT_INVALID;
    462   return linux_deliver_signal(process, thread, 11, ev->addr, ev->pc,
    463                               ev->next_pc, next_pc_out);
    464 }
    465 
    466 static KitStatus linux_decode_syscall(EmuProcess* process, EmuThread* thread,
    467                                       EmuSyscallRequest* out) {
    468   u32 i;
    469   if (!process || !thread || !thread->cpu || !out) return KIT_INVALID;
    470   if (!process->arch || !process->arch->emu ||
    471       !process->arch->emu->get_syscall_no ||
    472       !process->arch->emu->get_syscall_arg)
    473     return KIT_UNSUPPORTED;
    474   memset(out, 0, sizeof(*out));
    475   out->number = process->arch->emu->get_syscall_no(thread);
    476   for (i = 0; i < 6u; ++i)
    477     out->args[i] = process->arch->emu->get_syscall_arg(thread, i);
    478   return KIT_OK;
    479 }
    480 
    481 static KitStatus linux_encode_syscall_result(EmuProcess* process,
    482                                              EmuThread* thread,
    483                                              const EmuSyscallResult* r) {
    484   (void)process;
    485   if (!process || !thread || !thread->cpu || !r) return KIT_INVALID;
    486   if (!process->arch || !process->arch->emu ||
    487       !process->arch->emu->set_syscall_result)
    488     return KIT_UNSUPPORTED;
    489   process->arch->emu->set_syscall_result(thread, (u64)r->result);
    490   return KIT_OK;
    491 }
    492 
    493 static KitStatus linux_default_syscall(void* user, EmuProcess* process,
    494                                        EmuThread* thread,
    495                                        const EmuSyscallRequest* req,
    496                                        EmuSyscallResult* out) {
    497   EmuCPUState* s;
    498   u64 nr;
    499   u64 a0, a1, a2;
    500   u64 a3, a4, a5;
    501   i64 ret = -LINUX_ENOSYS;
    502   (void)user;
    503   if (!process || !thread || !thread->cpu || !req || !out) return KIT_INVALID;
    504   s = thread->cpu;
    505   nr = req->number;
    506   a0 = req->args[0];
    507   a1 = req->args[1];
    508   a2 = req->args[2];
    509   a3 = req->args[3];
    510   a4 = req->args[4];
    511   a5 = req->args[5];
    512   memset(out, 0, sizeof(*out));
    513 
    514   switch (nr) {
    515     case LINUX_SYS_exit:
    516     case LINUX_SYS_exit_group:
    517       emu_cpu_trap_exit(s, (int)(i32)a0);
    518       return KIT_OK;
    519     case LINUX_SYS_write: {
    520       u8* p = emu_cpu_va_to_host_perm(s, a1, a2, EMU_MEM_READ);
    521       ret = p ? (i64)a2 : -LINUX_EFAULT;
    522       break;
    523     }
    524     case LINUX_SYS_read:
    525       ret = a0 == 0u ? 0 : -LINUX_EBADF;
    526       break;
    527     case LINUX_SYS_close:
    528       ret = 0;
    529       break;
    530     case LINUX_SYS_brk: {
    531       u64 actual = 0;
    532       (void)emu_addr_space_set_brk(&process->image.addr_space, a0, &actual);
    533       ret = (i64)actual;
    534       break;
    535     }
    536     case LINUX_SYS_mmap: {
    537       EmuAddrSpace* as = &process->image.addr_space;
    538       LinuxProcessState* ps = linux_process_state(process);
    539       u64 addr = a0;
    540       u64 length = linux_round_up(a1, as->page_size);
    541       u64 flags = a3;
    542       u64 fd = a4;
    543       u64 off = a5;
    544       u64 map_at = 0;
    545       KitStatus st;
    546       if (!ps) {
    547         ret = -LINUX_EINVAL;
    548         break;
    549       }
    550       if (!length || (off & (as->page_size - 1u)) != 0) {
    551         ret = -LINUX_EINVAL;
    552         break;
    553       }
    554       if (!(flags & LINUX_MAP_ANONYMOUS)) {
    555         ret = -LINUX_ENOSYS;
    556         break;
    557       }
    558       if (!(flags & LINUX_MAP_PRIVATE)) {
    559         ret = -LINUX_EINVAL;
    560         break;
    561       }
    562       if ((flags & LINUX_MAP_ANONYMOUS) && (i64)fd != -1 && fd != 0) {
    563         ret = -LINUX_EBADF;
    564         break;
    565       }
    566       if (flags & (LINUX_MAP_FIXED | LINUX_MAP_FIXED_NOREPLACE)) {
    567         if ((addr & (as->page_size - 1u)) != 0) {
    568           ret = -LINUX_EINVAL;
    569           break;
    570         }
    571         map_at = addr;
    572         if (flags & LINUX_MAP_FIXED_NOREPLACE) {
    573           st = emu_addr_space_map(
    574               as, map_at, length, linux_emu_perms(a2),
    575               linux_emu_perms(a2) ? EMU_MAP_ANON : EMU_MAP_GUARD);
    576           if (st == KIT_OK) {
    577             if (process->os && process->os->emu_note_map_region)
    578               process->os->emu_note_map_region(process, map_at, length,
    579                                                EMU_OS_MAP_MMAP);
    580             ret = (i64)map_at;
    581           } else {
    582             ret = -LINUX_EEXIST;
    583           }
    584           break;
    585         }
    586         (void)emu_addr_space_unmap(as, map_at, length);
    587       } else {
    588         u64 min_va = addr ? linux_round_up(addr, as->page_size) : ps->mmap_hint;
    589         if (addr) {
    590           st = emu_addr_space_find_gap(as, length, as->page_size, min_va,
    591                                        0x0000800000000000ull, &map_at);
    592         } else if (process->os && process->os->emu_find_map_region) {
    593           st = process->os->emu_find_map_region(process, length, as->page_size,
    594                                                 EMU_OS_MAP_MMAP, &map_at);
    595         } else {
    596           st = KIT_UNSUPPORTED;
    597         }
    598         if (st != KIT_OK) {
    599           ret = -LINUX_ENOMEM;
    600           break;
    601         }
    602       }
    603       st = emu_addr_space_map(
    604           as, map_at, length, linux_emu_perms(a2),
    605           linux_emu_perms(a2) ? EMU_MAP_ANON : EMU_MAP_GUARD);
    606       if (st != KIT_OK) {
    607         ret = -LINUX_ENOMEM;
    608       } else {
    609         if (process->os && process->os->emu_note_map_region)
    610           process->os->emu_note_map_region(process, map_at, length,
    611                                            EMU_OS_MAP_MMAP);
    612         ret = (i64)map_at;
    613       }
    614       break;
    615     }
    616     case LINUX_SYS_munmap: {
    617       EmuAddrSpace* as = &process->image.addr_space;
    618       u64 addr = a0;
    619       u64 length = linux_round_up(a1, as->page_size);
    620       if (!length || (addr & (as->page_size - 1u)) != 0) {
    621         ret = -LINUX_EINVAL;
    622       } else {
    623         (void)emu_addr_space_unmap(as, addr, length);
    624         ret = 0;
    625       }
    626       break;
    627     }
    628     case LINUX_SYS_mprotect: {
    629       EmuAddrSpace* as = &process->image.addr_space;
    630       u64 addr = a0;
    631       u64 length = linux_round_up(a1, as->page_size);
    632       if (!length || (addr & (as->page_size - 1u)) != 0) {
    633         ret = -LINUX_EINVAL;
    634       } else if (emu_addr_space_protect(as, addr, length,
    635                                         linux_emu_perms(a2)) == KIT_OK) {
    636         ret = 0;
    637       } else {
    638         ret = -LINUX_ENOMEM;
    639       }
    640       break;
    641     }
    642     case LINUX_SYS_fstat: {
    643       u8* p = emu_cpu_va_to_host_perm(s, a1, 128u, EMU_MEM_WRITE);
    644       if (!p) {
    645         ret = -LINUX_EFAULT;
    646       } else {
    647         memset(p, 0, 128u);
    648         ret = 0;
    649       }
    650       break;
    651     }
    652     case LINUX_SYS_openat:
    653       ret = -2;
    654       break;
    655     case LINUX_SYS_lseek:
    656       ret = (i64)a1;
    657       break;
    658     case LINUX_SYS_readv: {
    659       u8* p = emu_cpu_va_to_host_perm(s, a1, a2 * 16u, EMU_MEM_READ);
    660       ret = p ? 0 : -LINUX_EFAULT;
    661       break;
    662     }
    663     case LINUX_SYS_writev: {
    664       u8* p = emu_cpu_va_to_host_perm(s, a1, a2 * 16u, EMU_MEM_READ);
    665       u64 total = 0;
    666       u64 i;
    667       if (!p) {
    668         ret = -LINUX_EFAULT;
    669         break;
    670       }
    671       for (i = 0; i < a2; ++i) {
    672         u64 l = 0;
    673         u32 j;
    674         for (j = 0; j < 8u; ++j) l |= ((u64)p[i * 16u + 8u + j]) << (8u * j);
    675         total += l;
    676       }
    677       ret = (i64)total;
    678       break;
    679     }
    680     case LINUX_SYS_set_tid_address:
    681       ret = 1;
    682       break;
    683     case LINUX_SYS_clock_gettime: {
    684       u8* p = emu_cpu_va_to_host_perm(s, a1, 16u, EMU_MEM_WRITE);
    685       if (!p) {
    686         ret = -LINUX_EFAULT;
    687       } else {
    688         memset(p, 0, 16u);
    689         ret = 0;
    690       }
    691       break;
    692     }
    693     case LINUX_SYS_sched_yield:
    694       ret = 0;
    695       break;
    696     case LINUX_SYS_rt_sigaction:
    697       if (a0 < 64u && a1) {
    698         LinuxProcessState* ps = linux_process_state(process);
    699         u8* p = emu_cpu_va_to_host_perm(s, a1, 24u, EMU_MEM_READ);
    700         if (!ps) {
    701           ret = -LINUX_EINVAL;
    702         } else if (!p) {
    703           ret = -LINUX_EFAULT;
    704         } else {
    705           u64 handler = 0, flags = 0, restorer = 0;
    706           u32 j;
    707           for (j = 0; j < 8u; ++j) handler |= ((u64)p[j]) << (8u * j);
    708           for (j = 0; j < 8u; ++j) flags |= ((u64)p[8u + j]) << (8u * j);
    709           for (j = 0; j < 8u; ++j) restorer |= ((u64)p[16u + j]) << (8u * j);
    710           ps->signal_actions[a0].handler = handler;
    711           ps->signal_actions[a0].flags = flags;
    712           ps->signal_actions[a0].restorer = restorer;
    713           ps->signal_actions[a0].installed = 1;
    714           ret = 0;
    715         }
    716       } else {
    717         ret = 0;
    718       }
    719       break;
    720     case LINUX_SYS_rt_sigprocmask:
    721       if (a0 == 0u && a1) {
    722         LinuxThreadState* ts = linux_thread_state(thread);
    723         u8* p = emu_cpu_va_to_host_perm(s, a1, 8u, EMU_MEM_READ);
    724         if (!ts) {
    725           ret = -LINUX_EINVAL;
    726         } else if (!p) {
    727           ret = -LINUX_EFAULT;
    728         } else {
    729           ts->signal_mask |= linux_rd64(p);
    730           ret = 0;
    731         }
    732       } else if (a0 == 1u && a1) {
    733         LinuxThreadState* ts = linux_thread_state(thread);
    734         u8* p = emu_cpu_va_to_host_perm(s, a1, 8u, EMU_MEM_READ);
    735         if (!ts) {
    736           ret = -LINUX_EINVAL;
    737         } else if (!p) {
    738           ret = -LINUX_EFAULT;
    739         } else {
    740           ts->signal_mask &= ~linux_rd64(p);
    741           ret = 0;
    742         }
    743       } else if (a0 == 2u && a1) {
    744         LinuxThreadState* ts = linux_thread_state(thread);
    745         u8* p = emu_cpu_va_to_host_perm(s, a1, 8u, EMU_MEM_READ);
    746         if (!ts) {
    747           ret = -LINUX_EINVAL;
    748         } else if (!p) {
    749           ret = -LINUX_EFAULT;
    750         } else {
    751           ts->signal_mask = linux_rd64(p);
    752           ret = 0;
    753         }
    754       } else {
    755         ret = 0;
    756       }
    757       break;
    758     case LINUX_SYS_rt_sigreturn: {
    759       LinuxThreadState* ts = linux_thread_state(thread);
    760       u64 frame_sp;
    761       u8* frame;
    762       u64 ctx_size;
    763       u64 frame_size;
    764       if (!process->arch || !process->arch->emu ||
    765           !process->arch->emu->get_sp ||
    766           !process->arch->emu->signal_context_size ||
    767           !process->arch->emu->restore_signal_context) {
    768         ret = -LINUX_ENOSYS;
    769         break;
    770       }
    771       if (!ts) {
    772         ret = -LINUX_EINVAL;
    773         break;
    774       }
    775       ctx_size = process->arch->emu->signal_context_size(process, thread);
    776       frame_size = EMU_LINUX_SIGFRAME_XREGS + ctx_size;
    777       frame_sp = ts->signal_frame_sp ? ts->signal_frame_sp
    778                                      : process->arch->emu->get_sp(thread);
    779       frame = emu_cpu_va_to_host_perm(s, frame_sp, frame_size, EMU_MEM_READ);
    780       if (!frame || linux_rd64(frame) != EMU_LINUX_SIGFRAME_MAGIC) {
    781         ret = -LINUX_EFAULT;
    782         break;
    783       }
    784       if (process->arch->emu->restore_signal_context(
    785               process, thread, frame + EMU_LINUX_SIGFRAME_XREGS, ctx_size) !=
    786           KIT_OK) {
    787         ret = -LINUX_EFAULT;
    788         break;
    789       }
    790       emu_cpu_set_pc(s, linux_rd64(frame + EMU_LINUX_SIGFRAME_SAVED_PC));
    791       ts->signal_frame_sp = 0;
    792       out->flags |= EMU_SYSCALL_RESULT_SKIP_ENCODE;
    793       ret = 0;
    794       break;
    795     }
    796     case LINUX_SYS_getpid:
    797     case LINUX_SYS_getuid:
    798     case LINUX_SYS_geteuid:
    799     case LINUX_SYS_getgid:
    800     case LINUX_SYS_getegid:
    801       ret = 1;
    802       break;
    803     default:
    804       ret = -LINUX_ENOSYS;
    805       break;
    806   }
    807 
    808   out->result = ret;
    809   return KIT_OK;
    810 }
    811 
    812 static u64 linux_syscall_next_pc(EmuProcess* process, EmuThread* thread,
    813                                  const EmuSyscallRequest* req, u64 next_pc) {
    814   EmuCPUState* s = emu_thread_cpu(thread);
    815   (void)process;
    816   if (req && req->number == LINUX_SYS_rt_sigreturn && s) return emu_cpu_pc(s);
    817   return next_pc;
    818 }
    819 
    820 const KitOsImpl linux_os_impl = {
    821     .kind = KIT_OS_LINUX,
    822     .name = "linux",
    823     .emu_init_process_private = linux_init_process_private,
    824     .emu_destroy_process_private = linux_destroy_process_private,
    825     .emu_init_thread_private = linux_init_thread_private,
    826     .emu_destroy_thread_private = linux_destroy_thread_private,
    827     .emu_init_process = linux_init_process,
    828     .emu_init_thread = linux_init_thread,
    829     .emu_decode_syscall = linux_decode_syscall,
    830     .emu_encode_syscall_result = linux_encode_syscall_result,
    831     .emu_syscall_next_pc = linux_syscall_next_pc,
    832     .emu_find_map_region = linux_find_map_region,
    833     .emu_note_map_region = linux_note_map_region,
    834     .emu_default_syscall = linux_default_syscall,
    835     .emu_deliver_fault = linux_deliver_fault,
    836 };