kit

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

linux.c (27648B)


      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 ||
    364         !process->os->emu->find_map_region ||
    365         process->os->emu->find_map_region(process, nbytes, page_size,
    366                                          EMU_OS_MAP_TLS, &base) != KIT_OK)
    367       return KIT_ERR;
    368     if (emu_addr_space_map(&process->image.addr_space, base, nbytes,
    369                            EMU_MEM_READ | EMU_MEM_WRITE,
    370                            EMU_MAP_ANON) != KIT_OK)
    371       return KIT_ERR;
    372     if (process->os->emu->note_map_region)
    373       process->os->emu->note_map_region(process, base, nbytes, EMU_OS_MAP_TLS);
    374     if (emu_tls_copy_module_image(process, m, base) != KIT_OK) return KIT_ERR;
    375     if (emu_tls_blocks_add(c, &ts->tls_blocks, m->module_id, base, m->memsz) !=
    376         KIT_OK)
    377       return KIT_NOMEM;
    378     if (m->module_id == 1u) process->arch->emu->set_tp(thread, base);
    379   }
    380   return KIT_OK;
    381 }
    382 
    383 static KitStatus linux_deliver_signal(EmuProcess* process, EmuThread* thread,
    384                                       int signo, u64 fault_addr, u64 fault_pc,
    385                                       u64 next_pc, u64* next_pc_out) {
    386   EmuCPUState* cpu = emu_thread_cpu(thread);
    387   LinuxProcessState* ps;
    388   LinuxThreadState* ts;
    389   LinuxSignalAction* act;
    390   u64 sp;
    391   u64 frame_sp;
    392   u64 frame_size;
    393   u64 ctx_size;
    394   u64 stack_align;
    395   u8* frame;
    396   if (!process || !thread || !cpu || !next_pc_out) return KIT_INVALID;
    397   ps = linux_process_state(process);
    398   ts = linux_thread_state(thread);
    399   if (!ps || !ts) return KIT_INVALID;
    400   if (signo <= 0 || signo >= 64) {
    401     emu_cpu_trap_fault(cpu);
    402     *next_pc_out = fault_pc ? fault_pc : next_pc;
    403     return KIT_OK;
    404   }
    405   act = &ps->signal_actions[signo];
    406   if (ts->signal_mask & (1ull << (u32)signo)) {
    407     emu_cpu_trap_fault(cpu);
    408     *next_pc_out = fault_pc ? fault_pc : next_pc;
    409     return KIT_OK;
    410   }
    411   if (!act->installed || !act->handler) {
    412     emu_cpu_trap_fault(cpu);
    413     *next_pc_out = fault_pc ? fault_pc : next_pc;
    414     return KIT_OK;
    415   }
    416   if (!process->arch || !process->arch->emu || !process->arch->emu->get_sp ||
    417       !process->arch->emu->set_sp || !process->arch->emu->signal_context_size ||
    418       !process->arch->emu->save_signal_context ||
    419       !process->arch->emu->set_signal_handler_args ||
    420       !process->arch->emu->signal_stack_align)
    421     return KIT_UNSUPPORTED;
    422   sp = process->arch->emu->get_sp(thread);
    423   ctx_size = process->arch->emu->signal_context_size(process, thread);
    424   stack_align = process->arch->emu->signal_stack_align(process, thread);
    425   if (!ctx_size || !stack_align || (stack_align & (stack_align - 1u)) != 0)
    426     return KIT_UNSUPPORTED;
    427   frame_size = EMU_LINUX_SIGFRAME_XREGS + ctx_size;
    428   if (frame_size < EMU_LINUX_SIGFRAME_SIZE)
    429     frame_size = EMU_LINUX_SIGFRAME_SIZE;
    430   frame_size = linux_round_up(frame_size, stack_align);
    431   frame_sp = (sp - frame_size) & ~(stack_align - 1u);
    432   frame = emu_cpu_va_to_host_perm(cpu, frame_sp, frame_size, EMU_MEM_WRITE);
    433   if (!frame) {
    434     emu_cpu_trap_fault(cpu);
    435     *next_pc_out = fault_pc ? fault_pc : next_pc;
    436     return KIT_OK;
    437   }
    438   memset(frame, 0, (size_t)frame_size);
    439   linux_wr64(frame, EMU_LINUX_SIGFRAME_MAGIC);
    440   linux_wr64(frame + EMU_LINUX_SIGFRAME_SAVED_PC, fault_pc);
    441   linux_wr64(frame + EMU_LINUX_SIGFRAME_SIGINFO, (u64)signo);
    442   linux_wr64(frame + EMU_LINUX_SIGFRAME_SIGINFO + 16u, fault_addr);
    443   if (process->arch->emu->save_signal_context(process, thread,
    444                                               frame + EMU_LINUX_SIGFRAME_XREGS,
    445                                               ctx_size) != KIT_OK)
    446     return KIT_ERR;
    447   ts->signal_frame_sp = frame_sp;
    448   process->arch->emu->set_sp(thread, frame_sp);
    449   if (process->arch->emu->set_signal_handler_args(
    450           process, thread, signo, frame_sp + EMU_LINUX_SIGFRAME_SIGINFO,
    451           frame_sp + EMU_LINUX_SIGFRAME_UCONTEXT) != KIT_OK)
    452     return KIT_ERR;
    453   emu_cpu_clear_trap(cpu);
    454   emu_cpu_set_pc(cpu, act->handler);
    455   *next_pc_out = act->handler;
    456   return KIT_OK;
    457 }
    458 
    459 static KitStatus linux_deliver_fault(EmuProcess* process, EmuThread* thread,
    460                                      const EmuFaultEvent* ev,
    461                                      u64* next_pc_out) {
    462   if (!ev) return KIT_INVALID;
    463   return linux_deliver_signal(process, thread, 11, ev->addr, ev->pc,
    464                               ev->next_pc, next_pc_out);
    465 }
    466 
    467 static KitStatus linux_decode_syscall(EmuProcess* process, EmuThread* thread,
    468                                       EmuSyscallRequest* out) {
    469   u32 i;
    470   if (!process || !thread || !thread->cpu || !out) return KIT_INVALID;
    471   if (!process->arch || !process->arch->emu ||
    472       !process->arch->emu->get_syscall_no ||
    473       !process->arch->emu->get_syscall_arg)
    474     return KIT_UNSUPPORTED;
    475   memset(out, 0, sizeof(*out));
    476   out->number = process->arch->emu->get_syscall_no(thread);
    477   for (i = 0; i < 6u; ++i)
    478     out->args[i] = process->arch->emu->get_syscall_arg(thread, i);
    479   return KIT_OK;
    480 }
    481 
    482 static KitStatus linux_encode_syscall_result(EmuProcess* process,
    483                                              EmuThread* thread,
    484                                              const EmuSyscallResult* r) {
    485   (void)process;
    486   if (!process || !thread || !thread->cpu || !r) return KIT_INVALID;
    487   if (!process->arch || !process->arch->emu ||
    488       !process->arch->emu->set_syscall_result)
    489     return KIT_UNSUPPORTED;
    490   process->arch->emu->set_syscall_result(thread, (u64)r->result);
    491   return KIT_OK;
    492 }
    493 
    494 static KitStatus linux_default_syscall(void* user, EmuProcess* process,
    495                                        EmuThread* thread,
    496                                        const EmuSyscallRequest* req,
    497                                        EmuSyscallResult* out) {
    498   EmuCPUState* s;
    499   u64 nr;
    500   u64 a0, a1, a2;
    501   u64 a3, a4, a5;
    502   i64 ret = -LINUX_ENOSYS;
    503   (void)user;
    504   if (!process || !thread || !thread->cpu || !req || !out) return KIT_INVALID;
    505   s = thread->cpu;
    506   nr = req->number;
    507   a0 = req->args[0];
    508   a1 = req->args[1];
    509   a2 = req->args[2];
    510   a3 = req->args[3];
    511   a4 = req->args[4];
    512   a5 = req->args[5];
    513   memset(out, 0, sizeof(*out));
    514 
    515   switch (nr) {
    516     case LINUX_SYS_exit:
    517     case LINUX_SYS_exit_group:
    518       emu_cpu_trap_exit(s, (int)(i32)a0);
    519       return KIT_OK;
    520     case LINUX_SYS_write: {
    521       u8* p = emu_cpu_va_to_host_perm(s, a1, a2, EMU_MEM_READ);
    522       ret = p ? (i64)a2 : -LINUX_EFAULT;
    523       break;
    524     }
    525     case LINUX_SYS_read:
    526       ret = a0 == 0u ? 0 : -LINUX_EBADF;
    527       break;
    528     case LINUX_SYS_close:
    529       ret = 0;
    530       break;
    531     case LINUX_SYS_brk: {
    532       u64 actual = 0;
    533       (void)emu_addr_space_set_brk(&process->image.addr_space, a0, &actual);
    534       ret = (i64)actual;
    535       break;
    536     }
    537     case LINUX_SYS_mmap: {
    538       EmuAddrSpace* as = &process->image.addr_space;
    539       LinuxProcessState* ps = linux_process_state(process);
    540       u64 addr = a0;
    541       u64 length = linux_round_up(a1, as->page_size);
    542       u64 flags = a3;
    543       u64 fd = a4;
    544       u64 off = a5;
    545       u64 map_at = 0;
    546       KitStatus st;
    547       if (!ps) {
    548         ret = -LINUX_EINVAL;
    549         break;
    550       }
    551       if (!length || (off & (as->page_size - 1u)) != 0) {
    552         ret = -LINUX_EINVAL;
    553         break;
    554       }
    555       if (!(flags & LINUX_MAP_ANONYMOUS)) {
    556         ret = -LINUX_ENOSYS;
    557         break;
    558       }
    559       if (!(flags & LINUX_MAP_PRIVATE)) {
    560         ret = -LINUX_EINVAL;
    561         break;
    562       }
    563       if ((flags & LINUX_MAP_ANONYMOUS) && (i64)fd != -1 && fd != 0) {
    564         ret = -LINUX_EBADF;
    565         break;
    566       }
    567       if (flags & (LINUX_MAP_FIXED | LINUX_MAP_FIXED_NOREPLACE)) {
    568         if ((addr & (as->page_size - 1u)) != 0) {
    569           ret = -LINUX_EINVAL;
    570           break;
    571         }
    572         map_at = addr;
    573         if (flags & LINUX_MAP_FIXED_NOREPLACE) {
    574           st = emu_addr_space_map(
    575               as, map_at, length, linux_emu_perms(a2),
    576               linux_emu_perms(a2) ? EMU_MAP_ANON : EMU_MAP_GUARD);
    577           if (st == KIT_OK) {
    578             if (process->os && process->os->emu &&
    579                 process->os->emu->note_map_region)
    580               process->os->emu->note_map_region(process, map_at, length,
    581                                                EMU_OS_MAP_MMAP);
    582             ret = (i64)map_at;
    583           } else {
    584             ret = -LINUX_EEXIST;
    585           }
    586           break;
    587         }
    588         (void)emu_addr_space_unmap(as, map_at, length);
    589       } else {
    590         u64 min_va = addr ? linux_round_up(addr, as->page_size) : ps->mmap_hint;
    591         if (addr) {
    592           st = emu_addr_space_find_gap(as, length, as->page_size, min_va,
    593                                        0x0000800000000000ull, &map_at);
    594         } else if (process->os && process->os->emu &&
    595                    process->os->emu->find_map_region) {
    596           st = process->os->emu->find_map_region(process, length, as->page_size,
    597                                                 EMU_OS_MAP_MMAP, &map_at);
    598         } else {
    599           st = KIT_UNSUPPORTED;
    600         }
    601         if (st != KIT_OK) {
    602           ret = -LINUX_ENOMEM;
    603           break;
    604         }
    605       }
    606       st = emu_addr_space_map(
    607           as, map_at, length, linux_emu_perms(a2),
    608           linux_emu_perms(a2) ? EMU_MAP_ANON : EMU_MAP_GUARD);
    609       if (st != KIT_OK) {
    610         ret = -LINUX_ENOMEM;
    611       } else {
    612         if (process->os && process->os->emu &&
    613             process->os->emu->note_map_region)
    614           process->os->emu->note_map_region(process, map_at, length,
    615                                            EMU_OS_MAP_MMAP);
    616         ret = (i64)map_at;
    617       }
    618       break;
    619     }
    620     case LINUX_SYS_munmap: {
    621       EmuAddrSpace* as = &process->image.addr_space;
    622       u64 addr = a0;
    623       u64 length = linux_round_up(a1, as->page_size);
    624       if (!length || (addr & (as->page_size - 1u)) != 0) {
    625         ret = -LINUX_EINVAL;
    626       } else {
    627         (void)emu_addr_space_unmap(as, addr, length);
    628         ret = 0;
    629       }
    630       break;
    631     }
    632     case LINUX_SYS_mprotect: {
    633       EmuAddrSpace* as = &process->image.addr_space;
    634       u64 addr = a0;
    635       u64 length = linux_round_up(a1, as->page_size);
    636       if (!length || (addr & (as->page_size - 1u)) != 0) {
    637         ret = -LINUX_EINVAL;
    638       } else if (emu_addr_space_protect(as, addr, length,
    639                                         linux_emu_perms(a2)) == KIT_OK) {
    640         ret = 0;
    641       } else {
    642         ret = -LINUX_ENOMEM;
    643       }
    644       break;
    645     }
    646     case LINUX_SYS_fstat: {
    647       u8* p = emu_cpu_va_to_host_perm(s, a1, 128u, EMU_MEM_WRITE);
    648       if (!p) {
    649         ret = -LINUX_EFAULT;
    650       } else {
    651         memset(p, 0, 128u);
    652         ret = 0;
    653       }
    654       break;
    655     }
    656     case LINUX_SYS_openat:
    657       ret = -2;
    658       break;
    659     case LINUX_SYS_lseek:
    660       ret = (i64)a1;
    661       break;
    662     case LINUX_SYS_readv: {
    663       u8* p = emu_cpu_va_to_host_perm(s, a1, a2 * 16u, EMU_MEM_READ);
    664       ret = p ? 0 : -LINUX_EFAULT;
    665       break;
    666     }
    667     case LINUX_SYS_writev: {
    668       u8* p = emu_cpu_va_to_host_perm(s, a1, a2 * 16u, EMU_MEM_READ);
    669       u64 total = 0;
    670       u64 i;
    671       if (!p) {
    672         ret = -LINUX_EFAULT;
    673         break;
    674       }
    675       for (i = 0; i < a2; ++i) {
    676         u64 l = 0;
    677         u32 j;
    678         for (j = 0; j < 8u; ++j) l |= ((u64)p[i * 16u + 8u + j]) << (8u * j);
    679         total += l;
    680       }
    681       ret = (i64)total;
    682       break;
    683     }
    684     case LINUX_SYS_set_tid_address:
    685       ret = 1;
    686       break;
    687     case LINUX_SYS_clock_gettime: {
    688       u8* p = emu_cpu_va_to_host_perm(s, a1, 16u, EMU_MEM_WRITE);
    689       if (!p) {
    690         ret = -LINUX_EFAULT;
    691       } else {
    692         memset(p, 0, 16u);
    693         ret = 0;
    694       }
    695       break;
    696     }
    697     case LINUX_SYS_sched_yield:
    698       ret = 0;
    699       break;
    700     case LINUX_SYS_rt_sigaction:
    701       if (a0 < 64u && a1) {
    702         LinuxProcessState* ps = linux_process_state(process);
    703         u8* p = emu_cpu_va_to_host_perm(s, a1, 24u, EMU_MEM_READ);
    704         if (!ps) {
    705           ret = -LINUX_EINVAL;
    706         } else if (!p) {
    707           ret = -LINUX_EFAULT;
    708         } else {
    709           u64 handler = 0, flags = 0, restorer = 0;
    710           u32 j;
    711           for (j = 0; j < 8u; ++j) handler |= ((u64)p[j]) << (8u * j);
    712           for (j = 0; j < 8u; ++j) flags |= ((u64)p[8u + j]) << (8u * j);
    713           for (j = 0; j < 8u; ++j) restorer |= ((u64)p[16u + j]) << (8u * j);
    714           ps->signal_actions[a0].handler = handler;
    715           ps->signal_actions[a0].flags = flags;
    716           ps->signal_actions[a0].restorer = restorer;
    717           ps->signal_actions[a0].installed = 1;
    718           ret = 0;
    719         }
    720       } else {
    721         ret = 0;
    722       }
    723       break;
    724     case LINUX_SYS_rt_sigprocmask:
    725       if (a0 == 0u && a1) {
    726         LinuxThreadState* ts = linux_thread_state(thread);
    727         u8* p = emu_cpu_va_to_host_perm(s, a1, 8u, EMU_MEM_READ);
    728         if (!ts) {
    729           ret = -LINUX_EINVAL;
    730         } else if (!p) {
    731           ret = -LINUX_EFAULT;
    732         } else {
    733           ts->signal_mask |= linux_rd64(p);
    734           ret = 0;
    735         }
    736       } else if (a0 == 1u && a1) {
    737         LinuxThreadState* ts = linux_thread_state(thread);
    738         u8* p = emu_cpu_va_to_host_perm(s, a1, 8u, EMU_MEM_READ);
    739         if (!ts) {
    740           ret = -LINUX_EINVAL;
    741         } else if (!p) {
    742           ret = -LINUX_EFAULT;
    743         } else {
    744           ts->signal_mask &= ~linux_rd64(p);
    745           ret = 0;
    746         }
    747       } else if (a0 == 2u && a1) {
    748         LinuxThreadState* ts = linux_thread_state(thread);
    749         u8* p = emu_cpu_va_to_host_perm(s, a1, 8u, EMU_MEM_READ);
    750         if (!ts) {
    751           ret = -LINUX_EINVAL;
    752         } else if (!p) {
    753           ret = -LINUX_EFAULT;
    754         } else {
    755           ts->signal_mask = linux_rd64(p);
    756           ret = 0;
    757         }
    758       } else {
    759         ret = 0;
    760       }
    761       break;
    762     case LINUX_SYS_rt_sigreturn: {
    763       LinuxThreadState* ts = linux_thread_state(thread);
    764       u64 frame_sp;
    765       u8* frame;
    766       u64 ctx_size;
    767       u64 frame_size;
    768       if (!process->arch || !process->arch->emu ||
    769           !process->arch->emu->get_sp ||
    770           !process->arch->emu->signal_context_size ||
    771           !process->arch->emu->restore_signal_context) {
    772         ret = -LINUX_ENOSYS;
    773         break;
    774       }
    775       if (!ts) {
    776         ret = -LINUX_EINVAL;
    777         break;
    778       }
    779       ctx_size = process->arch->emu->signal_context_size(process, thread);
    780       frame_size = EMU_LINUX_SIGFRAME_XREGS + ctx_size;
    781       frame_sp = ts->signal_frame_sp ? ts->signal_frame_sp
    782                                      : process->arch->emu->get_sp(thread);
    783       frame = emu_cpu_va_to_host_perm(s, frame_sp, frame_size, EMU_MEM_READ);
    784       if (!frame || linux_rd64(frame) != EMU_LINUX_SIGFRAME_MAGIC) {
    785         ret = -LINUX_EFAULT;
    786         break;
    787       }
    788       if (process->arch->emu->restore_signal_context(
    789               process, thread, frame + EMU_LINUX_SIGFRAME_XREGS, ctx_size) !=
    790           KIT_OK) {
    791         ret = -LINUX_EFAULT;
    792         break;
    793       }
    794       emu_cpu_set_pc(s, linux_rd64(frame + EMU_LINUX_SIGFRAME_SAVED_PC));
    795       ts->signal_frame_sp = 0;
    796       out->flags |= EMU_SYSCALL_RESULT_SKIP_ENCODE;
    797       ret = 0;
    798       break;
    799     }
    800     case LINUX_SYS_getpid:
    801     case LINUX_SYS_getuid:
    802     case LINUX_SYS_geteuid:
    803     case LINUX_SYS_getgid:
    804     case LINUX_SYS_getegid:
    805       ret = 1;
    806       break;
    807     default:
    808       ret = -LINUX_ENOSYS;
    809       break;
    810   }
    811 
    812   out->result = ret;
    813   return KIT_OK;
    814 }
    815 
    816 static u64 linux_syscall_next_pc(EmuProcess* process, EmuThread* thread,
    817                                  const EmuSyscallRequest* req, u64 next_pc) {
    818   EmuCPUState* s = emu_thread_cpu(thread);
    819   (void)process;
    820   if (req && req->number == LINUX_SYS_rt_sigreturn && s) return emu_cpu_pc(s);
    821   return next_pc;
    822 }
    823 
    824 const KitOsEmuOps kit_os_linux_emu_ops = {
    825     .init_process_private = linux_init_process_private,
    826     .destroy_process_private = linux_destroy_process_private,
    827     .init_thread_private = linux_init_thread_private,
    828     .destroy_thread_private = linux_destroy_thread_private,
    829     .init_process = linux_init_process,
    830     .init_thread = linux_init_thread,
    831     .decode_syscall = linux_decode_syscall,
    832     .encode_syscall_result = linux_encode_syscall_result,
    833     .syscall_next_pc = linux_syscall_next_pc,
    834     .find_map_region = linux_find_map_region,
    835     .note_map_region = linux_note_map_region,
    836     .default_syscall = linux_default_syscall,
    837     .deliver_fault = linux_deliver_fault,
    838 };