kit

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

rv64_smoke_test.c (42084B)


      1 /* RV64 emulator smoke test.
      2  *
      3  * Builds a tiny statically-linked rv64 ELF64 in memory whose _start
      4  * does:
      5  *
      6  *   addi a0, zero, 42      # exit code
      7  *   addi a7, zero, 94      # SYS_exit_group
      8  *   ecall
      9  *
     10  * Runs it through kit_emu_run and asserts the lifted/JIT path exits
     11  * with code 42.
     12  *
     13  * This exercises:
     14  *  - the ELF64 loader (header + program-header validation, PT_LOAD
     15  *    placement, argv/envp/auxv stack layout)
     16  *  - the shared RV64 ArchDecodeOps path (ADDI, ECALL)
     17  *  - the syscall handler (SYS_exit_group)
     18  *  - the RV64 ArchEmuOps lifter and host JIT dispatch path. */
     19 
     20 #include <kit/compile.h>
     21 #include <kit/core.h>
     22 #include <kit/emu.h>
     23 #include <kit/jit.h>
     24 #include <stdio.h>
     25 #include <stdlib.h>
     26 #include <string.h>
     27 #include <sys/mman.h>
     28 #include <unistd.h>
     29 #if defined(__APPLE__)
     30 #include <mach/mach.h>
     31 #include <mach/mach_vm.h>
     32 #define XM_DUAL_APPLE 1
     33 #else
     34 #define XM_DUAL_APPLE 0
     35 #endif
     36 #if defined(__linux__)
     37 #include <sys/syscall.h>
     38 #define XM_DUAL_LINUX 1
     39 #else
     40 #define XM_DUAL_LINUX 0
     41 #endif
     42 
     43 /* Internal headers used only for header-only helpers (no internal link symbols
     44  * are referenced — this test drives the emulator entirely through the public
     45  * kit_emu_* API, so it links the public archive): rv64 instruction encoders
     46  * (static inline) and ELF64 layout constants. The accompanying white-box unit
     47  * tests for the decoder / address space / syscall units live in
     48  * rv64_vm_unit_test.c, which links the library objects directly. */
     49 #include "arch/riscv/isa.h"
     50 #include "core/core.h"
     51 #include "lib/kit_unit.h"
     52 #include "obj/elf/elf.h"
     53 
     54 /* Shared test context replaces the per-file heap/diag/counter globals;
     55  * EXPECT aliases CU_EXPECT so the call sites below are unchanged. */
     56 static KitUnit g_u;
     57 #define EXPECT(cond, ...) CU_EXPECT(&g_u, cond, __VA_ARGS__)
     58 
     59 static int xm_to_posix(int p) {
     60   int q = 0;
     61   if (p & KIT_PROT_READ) q |= PROT_READ;
     62   if (p & KIT_PROT_WRITE) q |= PROT_WRITE;
     63   if (p & KIT_PROT_EXEC) q |= PROT_EXEC;
     64   return q;
     65 }
     66 
     67 typedef struct XmTok {
     68   void* w;
     69   void* r;
     70   size_t n;
     71 } XmTok;
     72 
     73 static KitStatus xm_reserve_single(size_t n, KitExecMemRegion* out) {
     74   void* m =
     75       mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
     76   if (m == MAP_FAILED) return KIT_NOMEM;
     77   out->write = m;
     78   out->runtime = m;
     79   out->size = n;
     80   out->token = NULL;
     81   return KIT_OK;
     82 }
     83 
     84 static KitStatus xm_reserve(void* user, size_t n, int prot,
     85                             KitExecMemRegion* out) {
     86   (void)user;
     87   if (!out || !n) return KIT_INVALID;
     88   if (!(prot & KIT_PROT_EXEC)) return xm_reserve_single(n, out);
     89 #if XM_DUAL_APPLE
     90   {
     91     void* w =
     92         mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
     93     mach_vm_address_t r = 0;
     94     vm_prot_t cur = 0, max = 0;
     95     XmTok* tok;
     96     if (w == MAP_FAILED) return KIT_NOMEM;
     97     if (mach_vm_remap(mach_task_self(), &r, (mach_vm_size_t)n, 0,
     98                       VM_FLAGS_ANYWHERE, mach_task_self(),
     99                       (mach_vm_address_t)(uintptr_t)w, FALSE, &cur, &max,
    100                       VM_INHERIT_NONE) != KERN_SUCCESS) {
    101       munmap(w, n);
    102       return KIT_NOMEM;
    103     }
    104     if (mprotect((void*)(uintptr_t)r, n, PROT_READ) != 0) {
    105       munmap((void*)(uintptr_t)r, n);
    106       munmap(w, n);
    107       return KIT_NOMEM;
    108     }
    109     tok = (XmTok*)malloc(sizeof(*tok));
    110     if (!tok) {
    111       munmap((void*)(uintptr_t)r, n);
    112       munmap(w, n);
    113       return KIT_NOMEM;
    114     }
    115     tok->w = w;
    116     tok->r = (void*)(uintptr_t)r;
    117     tok->n = n;
    118     out->write = w;
    119     out->runtime = (void*)(uintptr_t)r;
    120     out->size = n;
    121     out->token = tok;
    122     return KIT_OK;
    123   }
    124 #elif XM_DUAL_LINUX
    125   {
    126     int fd = (int)syscall(SYS_memfd_create, "kit-emu-jit-test", 0u);
    127     void *w, *r;
    128     XmTok* tok;
    129     if (fd < 0) return KIT_NOMEM;
    130     if (ftruncate(fd, (off_t)n) != 0) {
    131       close(fd);
    132       return KIT_NOMEM;
    133     }
    134     w = mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    135     if (w == MAP_FAILED) {
    136       close(fd);
    137       return KIT_NOMEM;
    138     }
    139     r = mmap(NULL, n, PROT_READ, MAP_SHARED, fd, 0);
    140     close(fd);
    141     if (r == MAP_FAILED) {
    142       munmap(w, n);
    143       return KIT_NOMEM;
    144     }
    145     tok = (XmTok*)malloc(sizeof(*tok));
    146     if (!tok) {
    147       munmap(r, n);
    148       munmap(w, n);
    149       return KIT_NOMEM;
    150     }
    151     tok->w = w;
    152     tok->r = r;
    153     tok->n = n;
    154     out->write = w;
    155     out->runtime = r;
    156     out->size = n;
    157     out->token = tok;
    158     return KIT_OK;
    159   }
    160 #else
    161   return xm_reserve_single(n, out);
    162 #endif
    163 }
    164 
    165 static KitStatus xm_protect(void* user, void* addr, size_t n, int prot) {
    166   (void)user;
    167   return mprotect(addr, n, xm_to_posix(prot)) == 0 ? KIT_OK : KIT_IO;
    168 }
    169 
    170 static void xm_release(void* user, KitExecMemRegion* r) {
    171   (void)user;
    172   if (!r || !r->size) return;
    173   if (r->token) {
    174     XmTok* tok = (XmTok*)r->token;
    175     if (tok->r && tok->r != tok->w) munmap(tok->r, tok->n);
    176     if (tok->w) munmap(tok->w, tok->n);
    177     free(tok);
    178   } else if (r->write) {
    179     munmap(r->write, r->size);
    180   }
    181   memset(r, 0, sizeof(*r));
    182 }
    183 
    184 static void xm_flush(void* user, void* addr, size_t n) {
    185   (void)user;
    186 #if defined(__aarch64__) || defined(__arm__) || defined(__riscv)
    187   __builtin___clear_cache((char*)addr, (char*)addr + n);
    188 #else
    189   (void)addr;
    190   (void)n;
    191 #endif
    192 }
    193 
    194 static KitExecMem g_execmem = {
    195     16 * 1024, xm_reserve, xm_protect, xm_release, xm_flush, NULL,
    196 };
    197 
    198 static KitCompiler* new_host_compiler(void) {
    199   KitArchKind arch;
    200   KitOSKind os;
    201   KitObjFmt obj;
    202   KitTargetSpec t;
    203   KitCompiler* c = NULL;
    204 #if defined(__x86_64__) || defined(_M_X64)
    205   arch = KIT_ARCH_X86_64;
    206 #elif defined(__aarch64__) || defined(_M_ARM64)
    207   arch = KIT_ARCH_ARM_64;
    208 #elif defined(__riscv) && __riscv_xlen == 64
    209   arch = KIT_ARCH_RV64;
    210 #else
    211   return NULL;
    212 #endif
    213 #if defined(__APPLE__)
    214   os = KIT_OS_MACOS;
    215   obj = KIT_OBJ_MACHO;
    216 #elif defined(__linux__)
    217   os = KIT_OS_LINUX;
    218   obj = KIT_OBJ_ELF;
    219 #else
    220   return NULL;
    221 #endif
    222   t = kit_unit_target(arch, os, obj);
    223   if (kit_unit_compiler_new(&g_u, t, &c) != KIT_OK || !c) {
    224     fprintf(stderr, "host compiler_new failed\n");
    225     exit(2);
    226   }
    227   return c;
    228 }
    229 
    230 /* ============================================================
    231  * Minimal RV64 ELF64 builder
    232  * ============================================================ */
    233 
    234 /* Writes a u16 / u32 / u64 LE into a byte buffer at offset `off`. */
    235 static void put16(unsigned char* b, size_t off, unsigned v) {
    236   b[off + 0] = (unsigned char)v;
    237   b[off + 1] = (unsigned char)(v >> 8);
    238 }
    239 static void put32(unsigned char* b, size_t off, unsigned v) {
    240   b[off + 0] = (unsigned char)v;
    241   b[off + 1] = (unsigned char)(v >> 8);
    242   b[off + 2] = (unsigned char)(v >> 16);
    243   b[off + 3] = (unsigned char)(v >> 24);
    244 }
    245 static void put64(unsigned char* b, size_t off, uint64_t v) {
    246   put32(b, off, (unsigned)v);
    247   put32(b, off + 4, (unsigned)(v >> 32));
    248 }
    249 
    250 static void put_phdr(unsigned char* b, size_t off, uint32_t type,
    251                      uint32_t flags, uint64_t file_off, uint64_t vaddr,
    252                      uint64_t filesz, uint64_t memsz, uint64_t align) {
    253   put32(b, off + 0, type);
    254   put32(b, off + 4, flags);
    255   put64(b, off + 8, file_off);
    256   put64(b, off + 16, vaddr);
    257   put64(b, off + 24, vaddr);
    258   put64(b, off + 32, filesz);
    259   put64(b, off + 40, memsz);
    260   put64(b, off + 48, align);
    261 }
    262 
    263 static void put_dyn(unsigned char* b, size_t off, uint64_t tag, uint64_t val) {
    264   put64(b, off + 0, tag);
    265   put64(b, off + 8, val);
    266 }
    267 
    268 static void put_sym(unsigned char* b, size_t off, uint32_t name, uint8_t info,
    269                     uint16_t shndx, uint64_t value, uint64_t size) {
    270   put32(b, off + 0, name);
    271   b[off + 4] = info;
    272   b[off + 5] = 0;
    273   put16(b, off + 6, shndx);
    274   put64(b, off + 8, value);
    275   put64(b, off + 16, size);
    276 }
    277 
    278 static void put_rela(unsigned char* b, size_t off, uint64_t r_offset,
    279                      uint64_t r_info, int64_t addend) {
    280   put64(b, off + 0, r_offset);
    281   put64(b, off + 8, r_info);
    282   put64(b, off + 16, (uint64_t)addend);
    283 }
    284 
    285 static int64_t pcrel_delta(uint64_t from, uint64_t to) {
    286   if (to >= from) return (int64_t)(to - from);
    287   return -(int64_t)(from - to);
    288 }
    289 
    290 static uint32_t rv_pcrel_hi(uint64_t from, uint64_t to) {
    291   int64_t d = pcrel_delta(from, to);
    292   return (uint32_t)((d + 0x800) >> 12) & 0xfffffu;
    293 }
    294 
    295 static int32_t rv_pcrel_lo(uint64_t from, uint64_t to) {
    296   int64_t d = pcrel_delta(from, to);
    297   int64_t hi = (d + 0x800) >> 12;
    298   return (int32_t)(d - (hi << 12));
    299 }
    300 
    301 /* Build a static rv64 ELF: ehdr + 1 phdr + text. The text segment is
    302  * page-aligned at virtual address 0x10000 and contains the three
    303  * instructions described in the file header. Returns the buffer (must
    304  * be freed). */
    305 static unsigned char* build_minimal_elf(size_t* out_len) {
    306   /* Layout:
    307    *   [0..63]   ELF64 ehdr
    308    *   [64..119] one PT_LOAD phdr (size 56)
    309    *   [120..]   pad to page boundary
    310    *   page-aligned: .text bytes (3 instructions = 12 bytes)
    311    *
    312    * We use a 4 KiB page; the .text starts at file offset 0x1000 and
    313    * VA 0x11000 (so the loader's lo_va == 0x11000 unless we choose a
    314    * lower vaddr for the PT_LOAD).
    315    *
    316    * Easier: have PT_LOAD cover [0, end_of_text) at VA 0x10000, file
    317    * offset 0, filesz = end-of-text. e_entry points at the start of
    318    * .text. .text begins at file offset 0x1000 (page-aligned). */
    319   enum {
    320     PAGE = 0x1000u,
    321     BASE_VA = 0x10000ull,
    322     TEXT_OFF = 0x1000u,
    323     TEXT_LEN = 12u,
    324   };
    325   size_t total = TEXT_OFF + TEXT_LEN;
    326   unsigned char* b = (unsigned char*)calloc(1, total);
    327   if (!b) return NULL;
    328 
    329   /* ELF header — 64 bytes. */
    330   b[EI_MAG0] = ELFMAG0;
    331   b[EI_MAG1] = ELFMAG1;
    332   b[EI_MAG2] = ELFMAG2;
    333   b[EI_MAG3] = ELFMAG3;
    334   b[EI_CLASS] = ELFCLASS64;
    335   b[EI_DATA] = ELFDATA2LSB;
    336   b[EI_VERSION] = EV_CURRENT;
    337   b[EI_OSABI] = ELFOSABI_NONE;
    338   put16(b, 16, ET_EXEC);            /* e_type */
    339   put16(b, 18, EM_RISCV);           /* e_machine */
    340   put32(b, 20, EV_CURRENT);         /* e_version */
    341   put64(b, 24, BASE_VA + TEXT_OFF); /* e_entry */
    342   put64(b, 32, 64);                 /* e_phoff */
    343   put64(b, 40, 0);                  /* e_shoff (none) */
    344   put32(b, 48, 0);                  /* e_flags */
    345   put16(b, 52, ELF64_EHDR_SIZE);    /* e_ehsize */
    346   put16(b, 54, ELF64_PHDR_SIZE);    /* e_phentsize */
    347   put16(b, 56, 1);                  /* e_phnum */
    348   put16(b, 58, 0);                  /* e_shentsize */
    349   put16(b, 60, 0);                  /* e_shnum */
    350   put16(b, 62, 0);                  /* e_shstrndx */
    351 
    352   /* PT_LOAD phdr — 56 bytes at offset 64. */
    353   put32(b, 64 + 0, PT_LOAD);     /* p_type */
    354   put32(b, 64 + 4, PF_R | PF_X); /* p_flags */
    355   put64(b, 64 + 8, 0);           /* p_offset */
    356   put64(b, 64 + 16, BASE_VA);    /* p_vaddr */
    357   put64(b, 64 + 24, BASE_VA);    /* p_paddr */
    358   put64(b, 64 + 32, total);      /* p_filesz */
    359   put64(b, 64 + 40, total);      /* p_memsz */
    360   put64(b, 64 + 48, PAGE);       /* p_align */
    361 
    362   /* .text: addi a0,zero,42 ; addi a7,zero,94 ; ecall */
    363   put32(b, TEXT_OFF + 0, rv_addi(RV_A0, RV_ZERO, 42));
    364   put32(b, TEXT_OFF + 4, rv_addi(RV_A7, RV_ZERO, 94));
    365   put32(b, TEXT_OFF + 8, rv_ecall());
    366 
    367   *out_len = total;
    368   return b;
    369 }
    370 
    371 /* Dynamic fixture for the intended emu loader surface:
    372  *   PT_INTERP + PT_DYNAMIC + DT_NEEDED + JUMP_SLOT import + PT_TLS.
    373  *
    374  * The import resolver is expected to fill `import_add_got` with a
    375  * guest-callable thunk for import_add(a0). The TLS setup is expected to seed tp
    376  * so that ld t1, 0(tp) reads the PT_TLS initializer 11. The guest exits 31
    377  * + 11. */
    378 static unsigned char* build_dynamic_import_tls_elf(size_t* out_len) {
    379   enum {
    380     PAGE = 0x1000u,
    381     BASE_VA = 0x10000ull,
    382     PHNUM = 5u,
    383     TEXT_OFF = 0x1000u,
    384     INTERP_OFF = 0x1100u,
    385     DYNSTR_OFF = 0x1180u,
    386     DYNSYM_OFF = 0x1200u,
    387     RELA_OFF = 0x1300u,
    388     DYNAMIC_OFF = 0x2000u,
    389     DATA_OFF = 0x2100u,
    390     TLS_OFF = 0x2200u,
    391     TOTAL = 0x2300u,
    392   };
    393   const uint64_t text_va = BASE_VA + TEXT_OFF;
    394   const uint64_t interp_va = BASE_VA + INTERP_OFF;
    395   const uint64_t dynstr_va = BASE_VA + DYNSTR_OFF;
    396   const uint64_t dynsym_va = BASE_VA + DYNSYM_OFF;
    397   const uint64_t rela_va = BASE_VA + RELA_OFF;
    398   const uint64_t dynamic_va = BASE_VA + DYNAMIC_OFF;
    399   const uint64_t got_va = BASE_VA + DATA_OFF;
    400   const uint64_t tls_va = BASE_VA + TLS_OFF;
    401   const char interp[] = "/kit-test-ld.so";
    402   const char needed[] = "libkit-emu-test.so";
    403   const char import_name[] = "import_add";
    404   const size_t dynstr_needed_off = 1u;
    405   const size_t dynstr_import_off = 1u + sizeof(needed);
    406   unsigned char* b = (unsigned char*)calloc(1, TOTAL);
    407   unsigned char* ds;
    408   size_t dynstr_len;
    409 
    410   if (!b) return NULL;
    411 
    412   b[EI_MAG0] = ELFMAG0;
    413   b[EI_MAG1] = ELFMAG1;
    414   b[EI_MAG2] = ELFMAG2;
    415   b[EI_MAG3] = ELFMAG3;
    416   b[EI_CLASS] = ELFCLASS64;
    417   b[EI_DATA] = ELFDATA2LSB;
    418   b[EI_VERSION] = EV_CURRENT;
    419   b[EI_OSABI] = ELFOSABI_LINUX;
    420   put16(b, 16, ET_EXEC);
    421   put16(b, 18, EM_RISCV);
    422   put32(b, 20, EV_CURRENT);
    423   put64(b, 24, text_va);
    424   put64(b, 32, ELF64_EHDR_SIZE);
    425   put32(b, 48, EF_RISCV_FLOAT_ABI_DOUBLE);
    426   put16(b, 52, ELF64_EHDR_SIZE);
    427   put16(b, 54, ELF64_PHDR_SIZE);
    428   put16(b, 56, PHNUM);
    429 
    430   put_phdr(b, 64 + 0u * ELF64_PHDR_SIZE, PT_LOAD, PF_R | PF_X, 0, BASE_VA,
    431            0x1400u, 0x1400u, PAGE);
    432   put_phdr(b, 64 + 1u * ELF64_PHDR_SIZE, PT_LOAD, PF_R | PF_W, DYNAMIC_OFF,
    433            dynamic_va, TLS_OFF + 8u - DYNAMIC_OFF, TLS_OFF + 8u - DYNAMIC_OFF,
    434            PAGE);
    435   put_phdr(b, 64 + 2u * ELF64_PHDR_SIZE, PT_INTERP, PF_R, INTERP_OFF, interp_va,
    436            sizeof(interp), sizeof(interp), 1u);
    437   put_phdr(b, 64 + 3u * ELF64_PHDR_SIZE, PT_DYNAMIC, PF_R | PF_W, DYNAMIC_OFF,
    438            dynamic_va, 10u * ELF64_DYN_SIZE, 10u * ELF64_DYN_SIZE, 8u);
    439   put_phdr(b, 64 + 4u * ELF64_PHDR_SIZE, PT_TLS, PF_R, TLS_OFF, tls_va, 8u, 8u,
    440            8u);
    441 
    442   put32(b, TEXT_OFF + 0u, rv_auipc(RV_T0, rv_pcrel_hi(text_va, got_va)));
    443   put32(b, TEXT_OFF + 4u, rv_ld(RV_T0, RV_T0, rv_pcrel_lo(text_va, got_va)));
    444   put32(b, TEXT_OFF + 8u, rv_addi(RV_A0, RV_ZERO, 31));
    445   put32(b, TEXT_OFF + 12u, rv_jalr(RV_RA, RV_T0, 0));
    446   put32(b, TEXT_OFF + 16u, rv_ld(RV_T1, RV_TP, 0));
    447   put32(b, TEXT_OFF + 20u, rv_add(RV_A0, RV_A0, RV_T1));
    448   put32(b, TEXT_OFF + 24u, rv_addi(RV_A7, RV_ZERO, 94));
    449   put32(b, TEXT_OFF + 28u, rv_ecall());
    450 
    451   memcpy(b + INTERP_OFF, interp, sizeof(interp));
    452   ds = b + DYNSTR_OFF;
    453   ds[0] = 0;
    454   memcpy(ds + dynstr_needed_off, needed, sizeof(needed));
    455   memcpy(ds + dynstr_import_off, import_name, sizeof(import_name));
    456   dynstr_len = dynstr_import_off + sizeof(import_name);
    457 
    458   put_sym(b, DYNSYM_OFF, 0, 0, SHN_UNDEF, 0, 0);
    459   put_sym(b, DYNSYM_OFF + ELF64_SYM_SIZE, (uint32_t)dynstr_import_off,
    460           ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), SHN_UNDEF, 0, 0);
    461   put_rela(b, RELA_OFF, got_va, ELF64_R_INFO(1u, ELF_R_RISCV_JUMP_SLOT), 0);
    462 
    463   put_dyn(b, DYNAMIC_OFF + 0u * ELF64_DYN_SIZE, DT_NEEDED, dynstr_needed_off);
    464   put_dyn(b, DYNAMIC_OFF + 1u * ELF64_DYN_SIZE, DT_STRTAB, dynstr_va);
    465   put_dyn(b, DYNAMIC_OFF + 2u * ELF64_DYN_SIZE, DT_STRSZ, dynstr_len);
    466   put_dyn(b, DYNAMIC_OFF + 3u * ELF64_DYN_SIZE, DT_SYMTAB, dynsym_va);
    467   put_dyn(b, DYNAMIC_OFF + 4u * ELF64_DYN_SIZE, DT_SYMENT, ELF64_SYM_SIZE);
    468   put_dyn(b, DYNAMIC_OFF + 5u * ELF64_DYN_SIZE, DT_PLTREL, DT_RELA);
    469   put_dyn(b, DYNAMIC_OFF + 6u * ELF64_DYN_SIZE, DT_JMPREL, rela_va);
    470   put_dyn(b, DYNAMIC_OFF + 7u * ELF64_DYN_SIZE, DT_PLTRELSZ, ELF64_RELA_SIZE);
    471   put_dyn(b, DYNAMIC_OFF + 8u * ELF64_DYN_SIZE, DT_RELAENT, ELF64_RELA_SIZE);
    472   put_dyn(b, DYNAMIC_OFF + 9u * ELF64_DYN_SIZE, DT_NULL, 0);
    473 
    474   put64(b, TLS_OFF, 11u);
    475   *out_len = TOTAL;
    476   return b;
    477 }
    478 
    479 static unsigned char* build_tls_distinct_elf(size_t* out_len) {
    480   enum {
    481     PAGE = 0x1000u,
    482     BASE_VA = 0x10000ull,
    483     PHNUM = 3u,
    484     TEXT_OFF = 0x1000u,
    485     TLS_OFF = 0x2000u,
    486     TOTAL = 0x2010u,
    487   };
    488   const uint64_t text_va = BASE_VA + TEXT_OFF;
    489   const uint64_t tls_va = BASE_VA + TLS_OFF;
    490   unsigned char* b = (unsigned char*)calloc(1, TOTAL);
    491   if (!b) return NULL;
    492 
    493   b[EI_MAG0] = ELFMAG0;
    494   b[EI_MAG1] = ELFMAG1;
    495   b[EI_MAG2] = ELFMAG2;
    496   b[EI_MAG3] = ELFMAG3;
    497   b[EI_CLASS] = ELFCLASS64;
    498   b[EI_DATA] = ELFDATA2LSB;
    499   b[EI_VERSION] = EV_CURRENT;
    500   b[EI_OSABI] = ELFOSABI_LINUX;
    501   put16(b, 16, ET_EXEC);
    502   put16(b, 18, EM_RISCV);
    503   put32(b, 20, EV_CURRENT);
    504   put64(b, 24, text_va);
    505   put64(b, 32, ELF64_EHDR_SIZE);
    506   put32(b, 48, EF_RISCV_FLOAT_ABI_DOUBLE);
    507   put16(b, 52, ELF64_EHDR_SIZE);
    508   put16(b, 54, ELF64_PHDR_SIZE);
    509   put16(b, 56, PHNUM);
    510 
    511   put_phdr(b, 64 + 0u * ELF64_PHDR_SIZE, PT_LOAD, PF_R | PF_X, 0, BASE_VA,
    512            TEXT_OFF + 40u, TEXT_OFF + 40u, PAGE);
    513   put_phdr(b, 64 + 1u * ELF64_PHDR_SIZE, PT_LOAD, PF_R | PF_W, TLS_OFF, tls_va,
    514            16u, 16u, PAGE);
    515   put_phdr(b, 64 + 2u * ELF64_PHDR_SIZE, PT_TLS, PF_R, TLS_OFF, tls_va, 8u, 16u,
    516            8u);
    517 
    518   put32(b, TEXT_OFF + 0u, rv_auipc(RV_T0, rv_pcrel_hi(text_va, tls_va)));
    519   put32(b, TEXT_OFF + 4u, rv_addi(RV_T0, RV_T0, rv_pcrel_lo(text_va, tls_va)));
    520   put32(b, TEXT_OFF + 8u, rv_addi(RV_T1, RV_ZERO, 9));
    521   put32(b, TEXT_OFF + 12u, rv_sd(RV_T1, RV_TP, 0));
    522   put32(b, TEXT_OFF + 16u, rv_ld(RV_A0, RV_T0, 0));
    523   put32(b, TEXT_OFF + 20u, rv_ld(RV_T2, RV_TP, 0));
    524   put32(b, TEXT_OFF + 24u, rv_add(RV_A0, RV_A0, RV_T2));
    525   put32(b, TEXT_OFF + 28u, rv_addi(RV_A7, RV_ZERO, 94));
    526   put32(b, TEXT_OFF + 32u, rv_ecall());
    527 
    528   put64(b, TLS_OFF, 11u);
    529   *out_len = TOTAL;
    530   return b;
    531 }
    532 
    533 static unsigned char* build_host_import_elf(size_t* out_len) {
    534   enum {
    535     PAGE = 0x1000u,
    536     BASE_VA = 0x10000ull,
    537     PHNUM = 3u,
    538     TEXT_OFF = 0x1000u,
    539     DYNSTR_OFF = 0x1100u,
    540     DYNSYM_OFF = 0x1180u,
    541     RELA_OFF = 0x1200u,
    542     DYNAMIC_OFF = 0x2000u,
    543     GOT_OFF = 0x2100u,
    544     TOTAL = 0x2200u,
    545   };
    546   const uint64_t text_va = BASE_VA + TEXT_OFF;
    547   const uint64_t dynstr_va = BASE_VA + DYNSTR_OFF;
    548   const uint64_t dynsym_va = BASE_VA + DYNSYM_OFF;
    549   const uint64_t rela_va = BASE_VA + RELA_OFF;
    550   const uint64_t dynamic_va = BASE_VA + DYNAMIC_OFF;
    551   const uint64_t got_va = BASE_VA + GOT_OFF;
    552   const char needed[] = "libhostbridge.so";
    553   const char import_name[] = "host_add2";
    554   const size_t dynstr_needed_off = 1u;
    555   const size_t dynstr_import_off = 1u + sizeof(needed);
    556   unsigned char* b = (unsigned char*)calloc(1, TOTAL);
    557   unsigned char* ds;
    558   size_t dynstr_len;
    559   if (!b) return NULL;
    560 
    561   b[EI_MAG0] = ELFMAG0;
    562   b[EI_MAG1] = ELFMAG1;
    563   b[EI_MAG2] = ELFMAG2;
    564   b[EI_MAG3] = ELFMAG3;
    565   b[EI_CLASS] = ELFCLASS64;
    566   b[EI_DATA] = ELFDATA2LSB;
    567   b[EI_VERSION] = EV_CURRENT;
    568   b[EI_OSABI] = ELFOSABI_LINUX;
    569   put16(b, 16, ET_EXEC);
    570   put16(b, 18, EM_RISCV);
    571   put32(b, 20, EV_CURRENT);
    572   put64(b, 24, text_va);
    573   put64(b, 32, ELF64_EHDR_SIZE);
    574   put32(b, 48, EF_RISCV_FLOAT_ABI_DOUBLE);
    575   put16(b, 52, ELF64_EHDR_SIZE);
    576   put16(b, 54, ELF64_PHDR_SIZE);
    577   put16(b, 56, PHNUM);
    578   put_phdr(b, 64 + 0u * ELF64_PHDR_SIZE, PT_LOAD, PF_R | PF_X, 0, BASE_VA,
    579            0x1300u, 0x1300u, PAGE);
    580   put_phdr(b, 64 + 1u * ELF64_PHDR_SIZE, PT_LOAD, PF_R | PF_W, DYNAMIC_OFF,
    581            dynamic_va, GOT_OFF + 8u - DYNAMIC_OFF, GOT_OFF + 8u - DYNAMIC_OFF,
    582            PAGE);
    583   put_phdr(b, 64 + 2u * ELF64_PHDR_SIZE, PT_DYNAMIC, PF_R | PF_W, DYNAMIC_OFF,
    584            dynamic_va, 10u * ELF64_DYN_SIZE, 10u * ELF64_DYN_SIZE, 8u);
    585 
    586   put32(b, TEXT_OFF + 0u, rv_auipc(RV_T0, rv_pcrel_hi(text_va, got_va)));
    587   put32(b, TEXT_OFF + 4u, rv_ld(RV_T0, RV_T0, rv_pcrel_lo(text_va, got_va)));
    588   put32(b, TEXT_OFF + 8u, rv_addi(RV_A0, RV_ZERO, 35));
    589   put32(b, TEXT_OFF + 12u, rv_addi(RV_A1, RV_ZERO, 7));
    590   put32(b, TEXT_OFF + 16u, rv_jalr(RV_RA, RV_T0, 0));
    591   put32(b, TEXT_OFF + 20u, rv_addi(RV_A7, RV_ZERO, 94));
    592   put32(b, TEXT_OFF + 24u, rv_ecall());
    593 
    594   ds = b + DYNSTR_OFF;
    595   ds[0] = 0;
    596   memcpy(ds + dynstr_needed_off, needed, sizeof(needed));
    597   memcpy(ds + dynstr_import_off, import_name, sizeof(import_name));
    598   dynstr_len = dynstr_import_off + sizeof(import_name);
    599   put_sym(b, DYNSYM_OFF, 0, 0, SHN_UNDEF, 0, 0);
    600   put_sym(b, DYNSYM_OFF + ELF64_SYM_SIZE, (uint32_t)dynstr_import_off,
    601           ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), SHN_UNDEF, 0, 0);
    602   put_rela(b, RELA_OFF, got_va, ELF64_R_INFO(1u, ELF_R_RISCV_JUMP_SLOT), 0);
    603   put_dyn(b, DYNAMIC_OFF + 0u * ELF64_DYN_SIZE, DT_NEEDED, dynstr_needed_off);
    604   put_dyn(b, DYNAMIC_OFF + 1u * ELF64_DYN_SIZE, DT_STRTAB, dynstr_va);
    605   put_dyn(b, DYNAMIC_OFF + 2u * ELF64_DYN_SIZE, DT_STRSZ, dynstr_len);
    606   put_dyn(b, DYNAMIC_OFF + 3u * ELF64_DYN_SIZE, DT_SYMTAB, dynsym_va);
    607   put_dyn(b, DYNAMIC_OFF + 4u * ELF64_DYN_SIZE, DT_SYMENT, ELF64_SYM_SIZE);
    608   put_dyn(b, DYNAMIC_OFF + 5u * ELF64_DYN_SIZE, DT_PLTREL, DT_RELA);
    609   put_dyn(b, DYNAMIC_OFF + 6u * ELF64_DYN_SIZE, DT_JMPREL, rela_va);
    610   put_dyn(b, DYNAMIC_OFF + 7u * ELF64_DYN_SIZE, DT_PLTRELSZ, ELF64_RELA_SIZE);
    611   put_dyn(b, DYNAMIC_OFF + 8u * ELF64_DYN_SIZE, DT_RELAENT, ELF64_RELA_SIZE);
    612   put_dyn(b, DYNAMIC_OFF + 9u * ELF64_DYN_SIZE, DT_NULL, 0);
    613   *out_len = TOTAL;
    614   return b;
    615 }
    616 
    617 static unsigned char* build_dso_import_main_elf(size_t* out_len) {
    618   enum {
    619     PAGE = 0x1000u,
    620     BASE_VA = 0x10000ull,
    621     PHNUM = 3u,
    622     TEXT_OFF = 0x1000u,
    623     DYNSTR_OFF = 0x1100u,
    624     DYNSYM_OFF = 0x1180u,
    625     RELA_OFF = 0x1200u,
    626     DYNAMIC_OFF = 0x2000u,
    627     GOT_OFF = 0x2100u,
    628     TOTAL = 0x2200u,
    629   };
    630   const uint64_t text_va = BASE_VA + TEXT_OFF;
    631   const uint64_t dynstr_va = BASE_VA + DYNSTR_OFF;
    632   const uint64_t dynsym_va = BASE_VA + DYNSYM_OFF;
    633   const uint64_t rela_va = BASE_VA + RELA_OFF;
    634   const uint64_t dynamic_va = BASE_VA + DYNAMIC_OFF;
    635   const uint64_t got_va = BASE_VA + GOT_OFF;
    636   const char needed[] = "libdsoadd.so";
    637   const char import_name[] = "dso_add";
    638   const size_t dynstr_needed_off = 1u;
    639   const size_t dynstr_import_off = 1u + sizeof(needed);
    640   unsigned char* b = (unsigned char*)calloc(1, TOTAL);
    641   unsigned char* ds;
    642   size_t dynstr_len;
    643   if (!b) return NULL;
    644 
    645   b[EI_MAG0] = ELFMAG0;
    646   b[EI_MAG1] = ELFMAG1;
    647   b[EI_MAG2] = ELFMAG2;
    648   b[EI_MAG3] = ELFMAG3;
    649   b[EI_CLASS] = ELFCLASS64;
    650   b[EI_DATA] = ELFDATA2LSB;
    651   b[EI_VERSION] = EV_CURRENT;
    652   b[EI_OSABI] = ELFOSABI_LINUX;
    653   put16(b, 16, ET_EXEC);
    654   put16(b, 18, EM_RISCV);
    655   put32(b, 20, EV_CURRENT);
    656   put64(b, 24, text_va);
    657   put64(b, 32, ELF64_EHDR_SIZE);
    658   put32(b, 48, EF_RISCV_FLOAT_ABI_DOUBLE);
    659   put16(b, 52, ELF64_EHDR_SIZE);
    660   put16(b, 54, ELF64_PHDR_SIZE);
    661   put16(b, 56, PHNUM);
    662   put_phdr(b, 64 + 0u * ELF64_PHDR_SIZE, PT_LOAD, PF_R | PF_X, 0, BASE_VA,
    663            0x1300u, 0x1300u, PAGE);
    664   put_phdr(b, 64 + 1u * ELF64_PHDR_SIZE, PT_LOAD, PF_R | PF_W, DYNAMIC_OFF,
    665            dynamic_va, GOT_OFF + 8u - DYNAMIC_OFF, GOT_OFF + 8u - DYNAMIC_OFF,
    666            PAGE);
    667   put_phdr(b, 64 + 2u * ELF64_PHDR_SIZE, PT_DYNAMIC, PF_R | PF_W, DYNAMIC_OFF,
    668            dynamic_va, 10u * ELF64_DYN_SIZE, 10u * ELF64_DYN_SIZE, 8u);
    669 
    670   put32(b, TEXT_OFF + 0u, rv_auipc(RV_T0, rv_pcrel_hi(text_va, got_va)));
    671   put32(b, TEXT_OFF + 4u, rv_ld(RV_T0, RV_T0, rv_pcrel_lo(text_va, got_va)));
    672   put32(b, TEXT_OFF + 8u, rv_addi(RV_A0, RV_ZERO, 35));
    673   put32(b, TEXT_OFF + 12u, rv_jalr(RV_RA, RV_T0, 0));
    674   put32(b, TEXT_OFF + 16u, rv_addi(RV_A7, RV_ZERO, 94));
    675   put32(b, TEXT_OFF + 20u, rv_ecall());
    676 
    677   ds = b + DYNSTR_OFF;
    678   ds[0] = 0;
    679   memcpy(ds + dynstr_needed_off, needed, sizeof(needed));
    680   memcpy(ds + dynstr_import_off, import_name, sizeof(import_name));
    681   dynstr_len = dynstr_import_off + sizeof(import_name);
    682   put_sym(b, DYNSYM_OFF, 0, 0, SHN_UNDEF, 0, 0);
    683   put_sym(b, DYNSYM_OFF + ELF64_SYM_SIZE, (uint32_t)dynstr_import_off,
    684           ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), SHN_UNDEF, 0, 0);
    685   put_rela(b, RELA_OFF, got_va, ELF64_R_INFO(1u, ELF_R_RISCV_JUMP_SLOT), 0);
    686   put_dyn(b, DYNAMIC_OFF + 0u * ELF64_DYN_SIZE, DT_NEEDED, dynstr_needed_off);
    687   put_dyn(b, DYNAMIC_OFF + 1u * ELF64_DYN_SIZE, DT_STRTAB, dynstr_va);
    688   put_dyn(b, DYNAMIC_OFF + 2u * ELF64_DYN_SIZE, DT_STRSZ, dynstr_len);
    689   put_dyn(b, DYNAMIC_OFF + 3u * ELF64_DYN_SIZE, DT_SYMTAB, dynsym_va);
    690   put_dyn(b, DYNAMIC_OFF + 4u * ELF64_DYN_SIZE, DT_SYMENT, ELF64_SYM_SIZE);
    691   put_dyn(b, DYNAMIC_OFF + 5u * ELF64_DYN_SIZE, DT_PLTREL, DT_RELA);
    692   put_dyn(b, DYNAMIC_OFF + 6u * ELF64_DYN_SIZE, DT_JMPREL, rela_va);
    693   put_dyn(b, DYNAMIC_OFF + 7u * ELF64_DYN_SIZE, DT_PLTRELSZ, ELF64_RELA_SIZE);
    694   put_dyn(b, DYNAMIC_OFF + 8u * ELF64_DYN_SIZE, DT_RELAENT, ELF64_RELA_SIZE);
    695   put_dyn(b, DYNAMIC_OFF + 9u * ELF64_DYN_SIZE, DT_NULL, 0);
    696   *out_len = TOTAL;
    697   return b;
    698 }
    699 
    700 static unsigned char* build_dso_import_so(size_t* out_len) {
    701   enum {
    702     PAGE = 0x1000u,
    703     PHNUM = 3u,
    704     TEXT_OFF = 0x1000u,
    705     DYNAMIC_OFF = 0x2000u,
    706     DYNSTR_OFF = 0x2100u,
    707     DYNSYM_OFF = 0x2180u,
    708     HASH_OFF = 0x2200u,
    709     RELA_OFF = 0x2220u,
    710     DATA_OFF = 0x2300u,
    711     TOTAL = 0x2400u,
    712   };
    713   const uint64_t text_va = TEXT_OFF;
    714   const uint64_t dynamic_va = DYNAMIC_OFF;
    715   const uint64_t dynstr_va = DYNSTR_OFF;
    716   const uint64_t dynsym_va = DYNSYM_OFF;
    717   const uint64_t hash_va = HASH_OFF;
    718   const uint64_t rela_va = RELA_OFF;
    719   const uint64_t ptr_va = DATA_OFF;
    720   const uint64_t value_va = DATA_OFF + 8u;
    721   const char soname[] = "libdsoadd.so";
    722   const char symbol[] = "dso_add";
    723   const size_t dynstr_soname_off = 1u;
    724   const size_t dynstr_symbol_off = 1u + sizeof(soname);
    725   unsigned char* b = (unsigned char*)calloc(1, TOTAL);
    726   unsigned char* ds;
    727   size_t dynstr_len;
    728   if (!b) return NULL;
    729 
    730   b[EI_MAG0] = ELFMAG0;
    731   b[EI_MAG1] = ELFMAG1;
    732   b[EI_MAG2] = ELFMAG2;
    733   b[EI_MAG3] = ELFMAG3;
    734   b[EI_CLASS] = ELFCLASS64;
    735   b[EI_DATA] = ELFDATA2LSB;
    736   b[EI_VERSION] = EV_CURRENT;
    737   b[EI_OSABI] = ELFOSABI_LINUX;
    738   put16(b, 16, ET_DYN);
    739   put16(b, 18, EM_RISCV);
    740   put32(b, 20, EV_CURRENT);
    741   put64(b, 24, text_va);
    742   put64(b, 32, ELF64_EHDR_SIZE);
    743   put32(b, 48, EF_RISCV_FLOAT_ABI_DOUBLE);
    744   put16(b, 52, ELF64_EHDR_SIZE);
    745   put16(b, 54, ELF64_PHDR_SIZE);
    746   put16(b, 56, PHNUM);
    747   put_phdr(b, 64 + 0u * ELF64_PHDR_SIZE, PT_LOAD, PF_R | PF_X, 0, 0,
    748            TEXT_OFF + 24u, TEXT_OFF + 24u, PAGE);
    749   put_phdr(b, 64 + 1u * ELF64_PHDR_SIZE, PT_LOAD, PF_R | PF_W, DYNAMIC_OFF,
    750            dynamic_va, DATA_OFF + 16u - DYNAMIC_OFF,
    751            DATA_OFF + 16u - DYNAMIC_OFF, PAGE);
    752   put_phdr(b, 64 + 2u * ELF64_PHDR_SIZE, PT_DYNAMIC, PF_R | PF_W, DYNAMIC_OFF,
    753            dynamic_va, 10u * ELF64_DYN_SIZE, 10u * ELF64_DYN_SIZE, 8u);
    754 
    755   put32(b, TEXT_OFF + 0u, rv_auipc(RV_T0, rv_pcrel_hi(text_va, ptr_va)));
    756   put32(b, TEXT_OFF + 4u, rv_ld(RV_T0, RV_T0, rv_pcrel_lo(text_va, ptr_va)));
    757   put32(b, TEXT_OFF + 8u, rv_ld(RV_T1, RV_T0, 0));
    758   put32(b, TEXT_OFF + 12u, rv_add(RV_A0, RV_A0, RV_T1));
    759   put32(b, TEXT_OFF + 16u, rv_jalr(RV_ZERO, RV_RA, 0));
    760 
    761   ds = b + DYNSTR_OFF;
    762   ds[0] = 0;
    763   memcpy(ds + dynstr_soname_off, soname, sizeof(soname));
    764   memcpy(ds + dynstr_symbol_off, symbol, sizeof(symbol));
    765   dynstr_len = dynstr_symbol_off + sizeof(symbol);
    766   put_sym(b, DYNSYM_OFF, 0, 0, SHN_UNDEF, 0, 0);
    767   put_sym(b, DYNSYM_OFF + ELF64_SYM_SIZE, (uint32_t)dynstr_symbol_off,
    768           ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), 1u, text_va, 20u);
    769   put32(b, HASH_OFF + 0u, 1u);
    770   put32(b, HASH_OFF + 4u, 2u);
    771   put32(b, HASH_OFF + 8u, 1u);
    772   put32(b, HASH_OFF + 12u, 0u);
    773   put_rela(b, RELA_OFF, ptr_va, ELF64_R_INFO(0u, ELF_R_RISCV_RELATIVE),
    774            (int64_t)value_va);
    775   put64(b, DATA_OFF + 8u, 7u);
    776   put_dyn(b, DYNAMIC_OFF + 0u * ELF64_DYN_SIZE, DT_SONAME, dynstr_soname_off);
    777   put_dyn(b, DYNAMIC_OFF + 1u * ELF64_DYN_SIZE, DT_STRTAB, dynstr_va);
    778   put_dyn(b, DYNAMIC_OFF + 2u * ELF64_DYN_SIZE, DT_STRSZ, dynstr_len);
    779   put_dyn(b, DYNAMIC_OFF + 3u * ELF64_DYN_SIZE, DT_SYMTAB, dynsym_va);
    780   put_dyn(b, DYNAMIC_OFF + 4u * ELF64_DYN_SIZE, DT_SYMENT, ELF64_SYM_SIZE);
    781   put_dyn(b, DYNAMIC_OFF + 5u * ELF64_DYN_SIZE, DT_HASH, hash_va);
    782   put_dyn(b, DYNAMIC_OFF + 6u * ELF64_DYN_SIZE, DT_RELA, rela_va);
    783   put_dyn(b, DYNAMIC_OFF + 7u * ELF64_DYN_SIZE, DT_RELASZ, ELF64_RELA_SIZE);
    784   put_dyn(b, DYNAMIC_OFF + 8u * ELF64_DYN_SIZE, DT_RELAENT, ELF64_RELA_SIZE);
    785   put_dyn(b, DYNAMIC_OFF + 9u * ELF64_DYN_SIZE, DT_NULL, 0);
    786   *out_len = TOTAL;
    787   return b;
    788 }
    789 
    790 /* Static fixture for the intended permission/fault/signal surface. The guest
    791  * installs a minimal SIGSEGV handler, writes to its RX text page, and expects
    792  * Linux/RV64 signal delivery to transfer control to `handler`, which exits 42.
    793  */
    794 static unsigned char* build_signal_perms_elf(size_t* out_len) {
    795   enum {
    796     PAGE = 0x1000u,
    797     BASE_VA = 0x10000ull,
    798     TEXT_OFF = 0x1000u,
    799     DATA_OFF = 0x2000u,
    800     TOTAL = 0x2040u,
    801   };
    802   const uint64_t text_va = BASE_VA + TEXT_OFF;
    803   const uint64_t data_va = BASE_VA + DATA_OFF;
    804   const uint64_t handler_va = text_va + 48u;
    805   unsigned char* b = (unsigned char*)calloc(1, TOTAL);
    806 
    807   if (!b) return NULL;
    808 
    809   b[EI_MAG0] = ELFMAG0;
    810   b[EI_MAG1] = ELFMAG1;
    811   b[EI_MAG2] = ELFMAG2;
    812   b[EI_MAG3] = ELFMAG3;
    813   b[EI_CLASS] = ELFCLASS64;
    814   b[EI_DATA] = ELFDATA2LSB;
    815   b[EI_VERSION] = EV_CURRENT;
    816   b[EI_OSABI] = ELFOSABI_LINUX;
    817   put16(b, 16, ET_EXEC);
    818   put16(b, 18, EM_RISCV);
    819   put32(b, 20, EV_CURRENT);
    820   put64(b, 24, text_va);
    821   put64(b, 32, ELF64_EHDR_SIZE);
    822   put32(b, 48, EF_RISCV_FLOAT_ABI_DOUBLE);
    823   put16(b, 52, ELF64_EHDR_SIZE);
    824   put16(b, 54, ELF64_PHDR_SIZE);
    825   put16(b, 56, 2);
    826 
    827   put_phdr(b, 64 + 0u * ELF64_PHDR_SIZE, PT_LOAD, PF_R | PF_X, 0, BASE_VA,
    828            TEXT_OFF + 60u, TEXT_OFF + 60u, PAGE);
    829   put_phdr(b, 64 + 1u * ELF64_PHDR_SIZE, PT_LOAD, PF_R | PF_W, DATA_OFF,
    830            data_va, 32u, 32u, PAGE);
    831 
    832   put32(b, TEXT_OFF + 0u, rv_addi(RV_A0, RV_ZERO, 11)); /* SIGSEGV */
    833   put32(b, TEXT_OFF + 4u, rv_auipc(RV_A1, rv_pcrel_hi(text_va + 4u, data_va)));
    834   put32(b, TEXT_OFF + 8u,
    835         rv_addi(RV_A1, RV_A1, rv_pcrel_lo(text_va + 4u, data_va)));
    836   put32(b, TEXT_OFF + 12u, rv_addi(RV_A2, RV_ZERO, 0));
    837   put32(b, TEXT_OFF + 16u, rv_addi(RV_A3, RV_ZERO, 8));
    838   put32(b, TEXT_OFF + 20u, rv_addi(RV_A7, RV_ZERO, 134)); /* rt_sigaction */
    839   put32(b, TEXT_OFF + 24u, rv_ecall());
    840   put32(b, TEXT_OFF + 28u, rv_auipc(RV_T0, 0));
    841   put32(b, TEXT_OFF + 32u, rv_sd(RV_ZERO, RV_T0, 0));
    842   put32(b, TEXT_OFF + 36u, rv_addi(RV_A0, RV_ZERO, 1));
    843   put32(b, TEXT_OFF + 40u, rv_addi(RV_A7, RV_ZERO, 94));
    844   put32(b, TEXT_OFF + 44u, rv_ecall());
    845   put32(b, TEXT_OFF + 48u, rv_addi(RV_A0, RV_ZERO, 42));
    846   put32(b, TEXT_OFF + 52u, rv_addi(RV_A7, RV_ZERO, 94));
    847   put32(b, TEXT_OFF + 56u, rv_ecall());
    848 
    849   put64(b, DATA_OFF + 0u, handler_va);
    850   put64(b, DATA_OFF + 8u, 0);
    851   put64(b, DATA_OFF + 16u, 0);
    852   put64(b, DATA_OFF + 24u, 0);
    853 
    854   *out_len = TOTAL;
    855   return b;
    856 }
    857 
    858 static unsigned char* build_signal_load_fault_elf(size_t* out_len) {
    859   enum {
    860     PAGE = 0x1000u,
    861     BASE_VA = 0x10000ull,
    862     TEXT_OFF = 0x1000u,
    863     DATA_OFF = 0x2000u,
    864     TOTAL = 0x2040u,
    865   };
    866   const uint64_t text_va = BASE_VA + TEXT_OFF;
    867   const uint64_t data_va = BASE_VA + DATA_OFF;
    868   const uint64_t handler_va = text_va + 48u;
    869   unsigned char* b = (unsigned char*)calloc(1, TOTAL);
    870 
    871   if (!b) return NULL;
    872 
    873   b[EI_MAG0] = ELFMAG0;
    874   b[EI_MAG1] = ELFMAG1;
    875   b[EI_MAG2] = ELFMAG2;
    876   b[EI_MAG3] = ELFMAG3;
    877   b[EI_CLASS] = ELFCLASS64;
    878   b[EI_DATA] = ELFDATA2LSB;
    879   b[EI_VERSION] = EV_CURRENT;
    880   b[EI_OSABI] = ELFOSABI_LINUX;
    881   put16(b, 16, ET_EXEC);
    882   put16(b, 18, EM_RISCV);
    883   put32(b, 20, EV_CURRENT);
    884   put64(b, 24, text_va);
    885   put64(b, 32, ELF64_EHDR_SIZE);
    886   put32(b, 48, EF_RISCV_FLOAT_ABI_DOUBLE);
    887   put16(b, 52, ELF64_EHDR_SIZE);
    888   put16(b, 54, ELF64_PHDR_SIZE);
    889   put16(b, 56, 2);
    890 
    891   put_phdr(b, 64 + 0u * ELF64_PHDR_SIZE, PT_LOAD, PF_R | PF_X, 0, BASE_VA,
    892            TEXT_OFF + 60u, TEXT_OFF + 60u, PAGE);
    893   put_phdr(b, 64 + 1u * ELF64_PHDR_SIZE, PT_LOAD, PF_R | PF_W, DATA_OFF,
    894            data_va, 32u, 32u, PAGE);
    895 
    896   put32(b, TEXT_OFF + 0u, rv_addi(RV_A0, RV_ZERO, 11));
    897   put32(b, TEXT_OFF + 4u, rv_auipc(RV_A1, rv_pcrel_hi(text_va + 4u, data_va)));
    898   put32(b, TEXT_OFF + 8u,
    899         rv_addi(RV_A1, RV_A1, rv_pcrel_lo(text_va + 4u, data_va)));
    900   put32(b, TEXT_OFF + 12u, rv_addi(RV_A2, RV_ZERO, 0));
    901   put32(b, TEXT_OFF + 16u, rv_addi(RV_A3, RV_ZERO, 8));
    902   put32(b, TEXT_OFF + 20u, rv_addi(RV_A7, RV_ZERO, 134));
    903   put32(b, TEXT_OFF + 24u, rv_ecall());
    904   put32(b, TEXT_OFF + 28u, rv_addi(RV_T0, RV_ZERO, 0));
    905   put32(b, TEXT_OFF + 32u, rv_ld(RV_T1, RV_T0, 0));
    906   put32(b, TEXT_OFF + 36u, rv_addi(RV_A0, RV_ZERO, 1));
    907   put32(b, TEXT_OFF + 40u, rv_addi(RV_A7, RV_ZERO, 94));
    908   put32(b, TEXT_OFF + 44u, rv_ecall());
    909   put32(b, TEXT_OFF + 48u, rv_addi(RV_A0, RV_ZERO, 42));
    910   put32(b, TEXT_OFF + 52u, rv_addi(RV_A7, RV_ZERO, 94));
    911   put32(b, TEXT_OFF + 56u, rv_ecall());
    912 
    913   put64(b, DATA_OFF + 0u, handler_va);
    914   put64(b, DATA_OFF + 8u, 0);
    915   put64(b, DATA_OFF + 16u, 0);
    916   put64(b, DATA_OFF + 24u, 0);
    917 
    918   *out_len = TOTAL;
    919   return b;
    920 }
    921 
    922 static unsigned char* build_signal_sigreturn_elf(size_t* out_len) {
    923   enum {
    924     PAGE = 0x1000u,
    925     BASE_VA = 0x10000ull,
    926     TEXT_OFF = 0x1000u,
    927     DATA_OFF = 0x2000u,
    928     TOTAL = 0x2040u,
    929   };
    930   const uint64_t text_va = BASE_VA + TEXT_OFF;
    931   const uint64_t data_va = BASE_VA + DATA_OFF;
    932   const uint64_t handler_va = text_va + 48u;
    933   unsigned char* b = (unsigned char*)calloc(1, TOTAL);
    934   if (!b) return NULL;
    935 
    936   b[EI_MAG0] = ELFMAG0;
    937   b[EI_MAG1] = ELFMAG1;
    938   b[EI_MAG2] = ELFMAG2;
    939   b[EI_MAG3] = ELFMAG3;
    940   b[EI_CLASS] = ELFCLASS64;
    941   b[EI_DATA] = ELFDATA2LSB;
    942   b[EI_VERSION] = EV_CURRENT;
    943   b[EI_OSABI] = ELFOSABI_LINUX;
    944   put16(b, 16, ET_EXEC);
    945   put16(b, 18, EM_RISCV);
    946   put32(b, 20, EV_CURRENT);
    947   put64(b, 24, text_va);
    948   put64(b, 32, ELF64_EHDR_SIZE);
    949   put32(b, 48, EF_RISCV_FLOAT_ABI_DOUBLE);
    950   put16(b, 52, ELF64_EHDR_SIZE);
    951   put16(b, 54, ELF64_PHDR_SIZE);
    952   put16(b, 56, 2);
    953 
    954   put_phdr(b, 64 + 0u * ELF64_PHDR_SIZE, PT_LOAD, PF_R | PF_X, 0, BASE_VA,
    955            TEXT_OFF + 80u, TEXT_OFF + 80u, PAGE);
    956   put_phdr(b, 64 + 1u * ELF64_PHDR_SIZE, PT_LOAD, PF_R | PF_W, DATA_OFF,
    957            data_va, 32u, 32u, PAGE);
    958 
    959   put32(b, TEXT_OFF + 0u, rv_addi(RV_A0, RV_ZERO, 11));
    960   put32(b, TEXT_OFF + 4u, rv_auipc(RV_A1, rv_pcrel_hi(text_va + 4u, data_va)));
    961   put32(b, TEXT_OFF + 8u,
    962         rv_addi(RV_A1, RV_A1, rv_pcrel_lo(text_va + 4u, data_va)));
    963   put32(b, TEXT_OFF + 12u, rv_addi(RV_A2, RV_ZERO, 0));
    964   put32(b, TEXT_OFF + 16u, rv_addi(RV_A3, RV_ZERO, 8));
    965   put32(b, TEXT_OFF + 20u, rv_addi(RV_A7, RV_ZERO, 134));
    966   put32(b, TEXT_OFF + 24u, rv_ecall());
    967   put32(b, TEXT_OFF + 28u, rv_auipc(RV_T0, 0));
    968   put32(b, TEXT_OFF + 32u, rv_sd(RV_ZERO, RV_T0, 0));
    969   put32(b, TEXT_OFF + 36u, rv_addi(RV_A0, RV_ZERO, 42));
    970   put32(b, TEXT_OFF + 40u, rv_addi(RV_A7, RV_ZERO, 94));
    971   put32(b, TEXT_OFF + 44u, rv_ecall());
    972 
    973   put32(b, TEXT_OFF + 48u, rv_auipc(RV_A0, rv_pcrel_hi(handler_va, text_va)));
    974   put32(b, TEXT_OFF + 52u,
    975         rv_addi(RV_A0, RV_A0, rv_pcrel_lo(handler_va, text_va)));
    976   put32(b, TEXT_OFF + 56u, rv_addi(RV_A1, RV_ZERO, 1));
    977   put32(b, TEXT_OFF + 60u, rv_addi(RV_A2, RV_ZERO, 7));
    978   put32(b, TEXT_OFF + 64u, rv_addi(RV_A7, RV_ZERO, 226));
    979   put32(b, TEXT_OFF + 68u, rv_ecall());
    980   put32(b, TEXT_OFF + 72u, rv_addi(RV_A7, RV_ZERO, 139));
    981   put32(b, TEXT_OFF + 76u, rv_ecall());
    982 
    983   put64(b, DATA_OFF + 0u, handler_va);
    984   put64(b, DATA_OFF + 8u, 0);
    985   put64(b, DATA_OFF + 16u, 0);
    986   put64(b, DATA_OFF + 24u, 0);
    987   *out_len = TOTAL;
    988   return b;
    989 }
    990 
    991 /* ============================================================
    992  * Decoder smoke (sanity-check a handful of encodings before the
    993  * end-to-end JIT run).
    994  * ============================================================ */
    995 static void emu_fixture_expect_exit_with_bindings(
    996     const char* name, unsigned char* elf, size_t elf_len, int want_exit,
    997     uint32_t max_blocks, const KitEmuExternalBindings* bindings);
    998 
    999 static void emu_fixture_expect_exit(const char* name, unsigned char* elf,
   1000                                     size_t elf_len, int want_exit,
   1001                                     uint32_t max_blocks) {
   1002   KitEmuExternalBindings no_bindings;
   1003   memset(&no_bindings, 0, sizeof(no_bindings));
   1004   emu_fixture_expect_exit_with_bindings(name, elf, elf_len, want_exit,
   1005                                         max_blocks, &no_bindings);
   1006 }
   1007 
   1008 static void emu_fixture_expect_exit_with_bindings(
   1009     const char* name, unsigned char* elf, size_t elf_len, int want_exit,
   1010     uint32_t max_blocks, const KitEmuExternalBindings* bindings) {
   1011   KitCompiler* c;
   1012   KitJitHost host;
   1013   KitEmuOptions opts;
   1014   KitTargetSpec guest_target;
   1015   KitStatus st;
   1016   int exit_code = -1;
   1017   long ps;
   1018   (void)max_blocks; /* kit_emu_run drives the guest to its exit syscall */
   1019 
   1020   c = new_host_compiler();
   1021   if (!c) {
   1022     free(elf);
   1023     return;
   1024   }
   1025   ps = sysconf(_SC_PAGESIZE);
   1026   if (ps > 0) g_execmem.page_size = (size_t)ps;
   1027 
   1028   memset(&host, 0, sizeof(host));
   1029   host.execmem = &g_execmem;
   1030   memset(&guest_target, 0, sizeof(guest_target));
   1031   guest_target.arch = KIT_ARCH_RV64;
   1032   guest_target.os = KIT_OS_LINUX;
   1033   guest_target.obj = KIT_OBJ_ELF;
   1034   guest_target.ptr_size = 8u;
   1035   guest_target.ptr_align = 8u;
   1036   memset(&opts, 0, sizeof(opts));
   1037   opts.guest_bytes.data = elf;
   1038   opts.guest_bytes.len = elf_len;
   1039   opts.guest_target = guest_target;
   1040   opts.has_guest_target = true;
   1041   opts.jit_host = &host;
   1042   if (bindings) opts.bindings = *bindings;
   1043 
   1044   /* Public end-to-end entry: loads the guest, runs it to its exit syscall, and
   1045    * returns the code. A guest fault makes kit_emu_run return non-OK. */
   1046   st = kit_emu_run(c, &opts, &exit_code);
   1047   EXPECT(st == KIT_OK, "%s: kit_emu_run returned %d", name, (int)st);
   1048   EXPECT(exit_code == want_exit, "%s: exit_code should be %d, got %d", name,
   1049          want_exit, exit_code);
   1050   if (st == KIT_OK && exit_code == want_exit)
   1051     fprintf(stderr, "PASS %s\n", name);
   1052 
   1053   free(elf);
   1054   kit_compiler_free(c);
   1055 }
   1056 
   1057 static void jit_vertical_smoke(void) {
   1058   unsigned char* elf;
   1059   size_t elf_len;
   1060 
   1061   elf = build_minimal_elf(&elf_len);
   1062   EXPECT(elf != NULL, "static ELF buffer allocation failed");
   1063   if (!elf) return;
   1064   emu_fixture_expect_exit("static-rv64-exit", elf, elf_len, 42, 8);
   1065 }
   1066 
   1067 static uint64_t host_plus5(uint64_t v) { return v + 5u; }
   1068 static uint64_t host_add2(uint64_t a, uint64_t b) { return a + b; }
   1069 
   1070 static KitStatus host_import_resolver(void* user, KitEmu* emu,
   1071                                       const KitEmuImportRequest* req,
   1072                                       KitEmuResolvedImport* out) {
   1073   (void)user;
   1074   (void)emu;
   1075   memset(out, 0, sizeof(*out));
   1076   if (req && kit_slice_eq_cstr(req->symbol_name, "host_add2")) {
   1077     out->host_fn = (void*)host_add2;
   1078     out->signature.abi = KIT_EMU_IMPORT_ABI_GUEST_C;
   1079     out->signature.result = KIT_EMU_VALUE_U64;
   1080     out->signature.nargs = 2u;
   1081     out->signature.args[0] = KIT_EMU_VALUE_U64;
   1082     out->signature.args[1] = KIT_EMU_VALUE_U64;
   1083     return KIT_OK;
   1084   }
   1085   if (req && kit_slice_eq_cstr(req->symbol_name, "import_add")) {
   1086     out->host_fn = (void*)host_plus5;
   1087     out->signature.abi = KIT_EMU_IMPORT_ABI_GUEST_C;
   1088     out->signature.result = KIT_EMU_VALUE_U64;
   1089     out->signature.nargs = 1u;
   1090     out->signature.args[0] = KIT_EMU_VALUE_U64;
   1091     return KIT_OK;
   1092   }
   1093   return KIT_NOT_FOUND;
   1094 }
   1095 
   1096 typedef struct DsoFixture {
   1097   unsigned char* bytes;
   1098   size_t len;
   1099 } DsoFixture;
   1100 
   1101 static KitStatus dso_object_resolver(void* user, KitEmu* emu,
   1102                                      const KitEmuObjectRequest* req,
   1103                                      KitEmuResolvedObject* out) {
   1104   DsoFixture* d = (DsoFixture*)user;
   1105   (void)emu;
   1106   memset(out, 0, sizeof(*out));
   1107   if (d && req && kit_slice_eq_cstr(req->object_name, "libdsoadd.so")) {
   1108     out->object_bytes.data = d->bytes;
   1109     out->object_bytes.len = d->len;
   1110     return KIT_OK;
   1111   }
   1112   return KIT_NOT_FOUND;
   1113 }
   1114 
   1115 static void dynamic_import_tls_red(void) {
   1116   unsigned char* elf;
   1117   size_t elf_len;
   1118 
   1119   elf = build_dynamic_import_tls_elf(&elf_len);
   1120   EXPECT(elf != NULL, "dynamic import/TLS ELF buffer allocation failed");
   1121   if (!elf) return;
   1122   emu_fixture_expect_exit("dynamic-import-tls-rv64", elf, elf_len, 42, 32);
   1123 }
   1124 
   1125 static void host_import_bridge_smoke(void) {
   1126   unsigned char* elf;
   1127   size_t elf_len;
   1128   KitEmuExternalBindings bindings;
   1129   elf = build_host_import_elf(&elf_len);
   1130   EXPECT(elf != NULL, "host import ELF buffer allocation failed");
   1131   if (!elf) return;
   1132   memset(&bindings, 0, sizeof(bindings));
   1133   bindings.resolve_import = host_import_resolver;
   1134   emu_fixture_expect_exit_with_bindings("host-import-bridge-rv64", elf, elf_len,
   1135                                         42, 32, &bindings);
   1136 }
   1137 
   1138 static void dso_import_reloc_smoke(void) {
   1139   unsigned char* elf;
   1140   size_t elf_len;
   1141   DsoFixture dso;
   1142   KitEmuExternalBindings bindings;
   1143   memset(&dso, 0, sizeof(dso));
   1144   dso.bytes = build_dso_import_so(&dso.len);
   1145   EXPECT(dso.bytes != NULL, "DSO ELF buffer allocation failed");
   1146   if (!dso.bytes) return;
   1147   elf = build_dso_import_main_elf(&elf_len);
   1148   EXPECT(elf != NULL, "DSO main ELF buffer allocation failed");
   1149   if (!elf) {
   1150     free(dso.bytes);
   1151     return;
   1152   }
   1153   memset(&bindings, 0, sizeof(bindings));
   1154   bindings.resolve_object = dso_object_resolver;
   1155   bindings.user = &dso;
   1156   emu_fixture_expect_exit_with_bindings("dso-import-reloc-rv64", elf, elf_len,
   1157                                         42, 64, &bindings);
   1158   free(dso.bytes);
   1159 }
   1160 
   1161 static void tls_distinct_smoke(void) {
   1162   unsigned char* elf;
   1163   size_t elf_len;
   1164   elf = build_tls_distinct_elf(&elf_len);
   1165   EXPECT(elf != NULL, "distinct TLS ELF buffer allocation failed");
   1166   if (!elf) return;
   1167   emu_fixture_expect_exit("tls-distinct-rv64", elf, elf_len, 20, 32);
   1168 }
   1169 
   1170 static void signal_perms_red(void) {
   1171   unsigned char* elf;
   1172   size_t elf_len;
   1173 
   1174   elf = build_signal_perms_elf(&elf_len);
   1175   EXPECT(elf != NULL, "signal/perms ELF buffer allocation failed");
   1176   if (!elf) return;
   1177   emu_fixture_expect_exit("signal-perms-rv64", elf, elf_len, 42, 64);
   1178 }
   1179 
   1180 static void signal_load_fault_smoke(void) {
   1181   unsigned char* elf;
   1182   size_t elf_len;
   1183 
   1184   elf = build_signal_load_fault_elf(&elf_len);
   1185   EXPECT(elf != NULL, "signal/load-fault ELF buffer allocation failed");
   1186   if (!elf) return;
   1187   emu_fixture_expect_exit("signal-load-fault-rv64", elf, elf_len, 42, 64);
   1188 }
   1189 
   1190 static void signal_sigreturn_smoke(void) {
   1191   unsigned char* elf;
   1192   size_t elf_len;
   1193   elf = build_signal_sigreturn_elf(&elf_len);
   1194   EXPECT(elf != NULL, "signal sigreturn ELF buffer allocation failed");
   1195   if (!elf) return;
   1196   emu_fixture_expect_exit("signal-sigreturn-rv64", elf, elf_len, 42, 96);
   1197 }
   1198 
   1199 int main(void) {
   1200   kit_unit_init(&g_u);
   1201   jit_vertical_smoke();
   1202   dynamic_import_tls_red();
   1203   host_import_bridge_smoke();
   1204   dso_import_reloc_smoke();
   1205   tls_distinct_smoke();
   1206   signal_perms_red();
   1207   signal_load_fault_smoke();
   1208   signal_sigreturn_smoke();
   1209   if (g_u.fails) {
   1210     fprintf(stderr, "FAILED %d check(s)\n", g_u.fails);
   1211     return 1;
   1212   }
   1213   fprintf(stderr, "OK\n");
   1214   return 0;
   1215 }