kit

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

rv32_class32.c (9691B)


      1 /* ELFCLASS32 write->read round-trip — the first ELFCLASS32 consumer test
      2  * for the kit rv32 port.
      3  *
      4  * Builds a tiny riscv32-none-elf relocatable in memory: one .text section
      5  * with a few bytes, a GLOBAL FUNC symbol, and one R_ABS32 relocation (the
      6  * RV32 primary absolute reloc, which the ELF emitter lowers to
      7  * ELF_R_RISCV_32). Then:
      8  *
      9  *     kit_obj_builder_emit(ob, mem_writer)
     10  *     kit_obj_open("rv32_class32", bytes, len)
     11  *
     12  * and asserts the readback is a 32-bit (ELFCLASS32) RISC-V object whose
     13  * symbol and relocation survived intact.
     14  *
     15  * Unlike the AArch64/x64/rv64 unit tests this does NOT use
     16  * kit_test_target_init: that helper hardcodes ptr_size=8 and has no rv32
     17  * branch. Instead we build a fixed rv32 KitTargetSpec directly.
     18  *
     19  * Exit 0 = pass; non-zero = fail (with one-line stderr explanations). */
     20 
     21 #include <kit/core.h>
     22 #include <kit/object.h>
     23 #include <stdarg.h>
     24 #include <stdio.h>
     25 #include <stdlib.h>
     26 #include <string.h>
     27 
     28 /* ---- env ---- */
     29 
     30 static void* heap_alloc(KitHeap* h, size_t n, size_t a) {
     31   (void)h;
     32   (void)a;
     33   return n ? malloc(n) : NULL;
     34 }
     35 static void* heap_realloc(KitHeap* h, void* p, size_t o, size_t n, size_t a) {
     36   (void)h;
     37   (void)o;
     38   (void)a;
     39   return realloc(p, n);
     40 }
     41 static void heap_free(KitHeap* h, void* p, size_t n) {
     42   (void)h;
     43   (void)n;
     44   free(p);
     45 }
     46 static KitHeap g_heap = {heap_alloc, heap_realloc, heap_free, NULL};
     47 
     48 static void diag_emit(KitDiagSink* s, KitDiagKind k, KitSrcLoc loc,
     49                       const char* fmt, va_list ap) {
     50   static const char* names[] = {"note", "warning", "error", "fatal"};
     51   (void)s;
     52   (void)loc;
     53   fprintf(stderr, "%s: ", names[k]);
     54   vfprintf(stderr, fmt, ap);
     55   fputc('\n', stderr);
     56 }
     57 static KitDiagSink g_diag = {diag_emit, NULL, 0, 0};
     58 
     59 /* ---- assertion helpers ---- */
     60 
     61 static int g_failures;
     62 #define CHECK(cond, ...)                                   \
     63   do {                                                     \
     64     if (!(cond)) {                                         \
     65       fprintf(stderr, "FAIL %s:%d: ", __FILE__, __LINE__); \
     66       fprintf(stderr, __VA_ARGS__);                        \
     67       fputc('\n', stderr);                                 \
     68       g_failures++;                                        \
     69     }                                                      \
     70   } while (0)
     71 
     72 /* ---- ELF on-the-wire constants (raw-byte checks) ---- */
     73 
     74 #define EI_CLASS 4
     75 #define ELFCLASS32 1
     76 #define EM_RISCV 243
     77 
     78 /* ---- input fixture ---- */
     79 
     80 /* RV32I: addi a0, zero, 0 ; jalr zero, 0(ra)  — little-endian.
     81  * Exact instruction encoding is irrelevant to this object-shape test; the
     82  * bytes only need to round-trip verbatim. */
     83 static const uint8_t TEXT_BYTES[8] = {
     84     0x13, 0x05, 0x00, 0x00, 0x67, 0x80, 0x00, 0x00,
     85 };
     86 
     87 #define RELOC_OFFSET 4u
     88 
     89 int main(void) {
     90   /* Fixed rv32 target: ELFCLASS32, RISC-V, little-endian, 4-byte pointers. */
     91   KitTargetSpec target;
     92   memset(&target, 0, sizeof target);
     93   target.arch = KIT_ARCH_RV32;
     94   target.os = KIT_OS_LINUX;
     95   target.obj = KIT_OBJ_ELF;
     96   target.ptr_size = 4;
     97   target.ptr_align = 4;
     98   target.big_endian = 0;
     99 
    100   KitContext ctx;
    101   memset(&ctx, 0, sizeof ctx);
    102   ctx.heap = &g_heap;
    103   ctx.diag = &g_diag;
    104   ctx.now = -1;
    105 
    106   KitTargetOptions target_opts;
    107   memset(&target_opts, 0, sizeof target_opts);
    108   target_opts.spec = target;
    109   KitTarget* kt = NULL;
    110   if (kit_target_new(&ctx, &target_opts, &kt) != KIT_OK || !kt) {
    111     fprintf(stderr, "FAIL: kit_target_new (rv32)\n");
    112     return 1;
    113   }
    114   KitCompiler* cc = NULL;
    115   if (kit_compiler_new(kt, &ctx, &cc) != KIT_OK || !cc) {
    116     fprintf(stderr, "FAIL: kit_compiler_new\n");
    117     kit_target_free(kt);
    118     return 1;
    119   }
    120 
    121   /* ---- build ---- */
    122   KitObjBuilder* in = NULL;
    123   CHECK(kit_obj_builder_new(cc, &in) == KIT_OK && in, "kit_obj_builder_new");
    124 
    125   KitObjSection sec_text = KIT_SECTION_NONE;
    126   KitObjSectionDesc text_desc = {
    127       .name = kit_sym_intern(cc, KIT_SLICE_LIT(".text")),
    128       .kind = KIT_SEC_TEXT,
    129       .flags = KIT_SF_ALLOC | KIT_SF_EXEC,
    130       .align = 4,
    131       .entsize = 0,
    132   };
    133   CHECK(kit_obj_builder_section(in, &text_desc, &sec_text) == KIT_OK,
    134         "section .text");
    135   CHECK(kit_obj_builder_write(in, sec_text, TEXT_BYTES, sizeof TEXT_BYTES) ==
    136             KIT_OK,
    137         "write .text");
    138 
    139   KitObjSymbol sym_func = KIT_OBJ_SYMBOL_NONE;
    140   KitObjSymbolDesc func_desc = {
    141       .name = kit_sym_intern(cc, KIT_SLICE_LIT("rv32_sym")),
    142       .bind = KIT_SB_GLOBAL,
    143       .kind = KIT_SK_FUNC,
    144       .section = sec_text,
    145       .value = 0,
    146       .size = sizeof TEXT_BYTES,
    147   };
    148   CHECK(kit_obj_builder_symbol(in, &func_desc, &sym_func) == KIT_OK,
    149         "symbol rv32_sym");
    150 
    151   /* One 32-bit absolute reloc. R_ABS32 -> ELF_R_RISCV_32 on RV32, so an
    152    * Elf32_Rela survives the round-trip; aimed at rv32_sym itself. */
    153   KitObjRelocDesc reloc_desc = {
    154       .section = sec_text,
    155       .offset = RELOC_OFFSET,
    156       .kind = {.arch = target.arch,
    157                .obj_fmt = target.obj,
    158                .code = KIT_RELOC_ABS32},
    159       .symbol = sym_func,
    160       .addend = 0,
    161   };
    162   CHECK(kit_obj_builder_reloc(in, &reloc_desc) == KIT_OK, "reloc .text");
    163 
    164   CHECK(kit_obj_builder_finalize(in) == KIT_OK, "finalize");
    165 
    166   /* ---- emit to memory ---- */
    167   KitWriter* w = NULL;
    168   (void)kit_writer_mem(&g_heap, &w);
    169   CHECK(kit_obj_builder_emit(in, w) == KIT_OK, "emit");
    170   size_t out_len = 0;
    171   const uint8_t* out_data = kit_writer_mem_bytes(w, &out_len);
    172 
    173   /* Sanity: ELF magic. */
    174   CHECK(out_len >= 52, "ELF emit produced too few bytes (%zu)", out_len);
    175   CHECK(out_len >= 4 && out_data[0] == 0x7f && out_data[1] == 'E' &&
    176             out_data[2] == 'L' && out_data[3] == 'F',
    177         "ELF emit output missing ELF magic");
    178 
    179   /* (a) Raw-byte class/machine checks on the emitted image. For ELFCLASS32
    180    * e_machine is a little-endian uint16 at offset 18; for ELFCLASS64 it is
    181    * also at 18, so this offset is valid regardless. e_ident[EI_CLASS] must
    182    * be ELFCLASS32 (==1) for the rv32 target. */
    183   if (out_len > EI_CLASS) {
    184     CHECK(out_data[EI_CLASS] == ELFCLASS32,
    185           "e_ident[EI_CLASS]=%u after emit, want ELFCLASS32=%u",
    186           out_data[EI_CLASS], ELFCLASS32);
    187   }
    188   if (out_len >= 20) {
    189     unsigned machine =
    190         (unsigned)out_data[18] | ((unsigned)out_data[19] << 8);
    191     CHECK(machine == EM_RISCV, "e_machine=%u after emit, want EM_RISCV=%u",
    192           machine, EM_RISCV);
    193   }
    194 
    195   /* Round-trip: copy to a private buffer first — the mem writer's storage is
    196    * freed on close. */
    197   uint8_t* roundtrip = (uint8_t*)malloc(out_len ? out_len : 1);
    198   memcpy(roundtrip, out_data, out_len);
    199   kit_writer_close(w);
    200 
    201   /* ---- reopen ---- */
    202   KitSlice input = {.data = roundtrip, .len = out_len};
    203   KitObjFile* back = NULL;
    204   CHECK(kit_obj_open(&ctx, KIT_SLICE_LIT("rv32_class32"), &input, &back) ==
    205                 KIT_OK &&
    206             back,
    207         "kit_obj_open failed");
    208 
    209   /* (b) The reader resolves the image to an rv32 target spec. */
    210   if (back) {
    211     KitTargetSpec got = kit_obj_target(back);
    212     CHECK(got.arch == KIT_ARCH_RV32, "reopened arch=%d, want KIT_ARCH_RV32=%d",
    213           (int)got.arch, (int)KIT_ARCH_RV32);
    214     CHECK(got.ptr_size == 4, "reopened ptr_size=%u, want 4",
    215           (unsigned)got.ptr_size);
    216     CHECK(got.obj == KIT_OBJ_ELF, "reopened obj fmt is not ELF");
    217   }
    218 
    219   /* (c) The symbol survives: GLOBAL FUNC in .text at value 0. */
    220   if (back) {
    221     KitObjSection text = KIT_SECTION_NONE;
    222     CHECK(kit_obj_section_by_name(back, KIT_SLICE_LIT(".text"), &text) ==
    223               KIT_OK,
    224           ".text not present after roundtrip");
    225 
    226     KitObjSymInfo si;
    227     if (kit_obj_symbol_by_name(back, KIT_SLICE_LIT("rv32_sym"), &si) ==
    228         KIT_OK) {
    229       CHECK(si.bind == KIT_SB_GLOBAL, "rv32_sym bind=%u", si.bind);
    230       CHECK(si.kind == KIT_SK_FUNC, "rv32_sym kind=%u (want SK_FUNC)",
    231             si.kind);
    232       CHECK(si.value == 0, "rv32_sym value=%llu, want 0",
    233             (unsigned long long)si.value);
    234       if (text != KIT_SECTION_NONE) {
    235         CHECK(si.section == text, "rv32_sym not bound to .text");
    236       }
    237     } else {
    238       CHECK(0, "missing 'rv32_sym' symbol after roundtrip");
    239     }
    240   }
    241 
    242   /* (d) The relocation survives: one R_ABS32 in .text at RELOC_OFFSET against
    243    * rv32_sym, addend 0. */
    244   if (back) {
    245     KitObjRelocIter* it = NULL;
    246     KitObjReloc found;
    247     int ntext = 0;
    248     int have = 0;
    249     KitObjSection text = KIT_SECTION_NONE;
    250     (void)kit_obj_section_by_name(back, KIT_SLICE_LIT(".text"), &text);
    251 
    252     CHECK(kit_obj_reliter_new(back, &it) == KIT_OK && it, "reliter_new");
    253     if (it) {
    254       KitObjReloc r;
    255       while (kit_obj_reliter_next(it, &r) == KIT_ITER_ITEM) {
    256         if (r.section == text) {
    257           ++ntext;
    258           found = r;
    259           have = 1;
    260         }
    261       }
    262       kit_obj_reliter_free(it);
    263     }
    264     CHECK(ntext == 1, ".text reloc count = %d, want 1", ntext);
    265     CHECK(have, "no reloc on .text after roundtrip");
    266     if (have) {
    267       CHECK(found.kind.code == KIT_RELOC_ABS32,
    268             ".text reloc code=%u (want ABS32=%u)", found.kind.code,
    269             KIT_RELOC_ABS32);
    270       CHECK(found.offset == RELOC_OFFSET, ".text reloc offset=%llu, want %u",
    271             (unsigned long long)found.offset, RELOC_OFFSET);
    272       CHECK(found.addend == 0, ".text reloc addend=%lld",
    273             (long long)found.addend);
    274       CHECK(kit_slice_eq_cstr(found.sym_name, "rv32_sym"),
    275             "reloc target name = %.*s", KIT_SLICE_ARG(found.sym_name));
    276     }
    277   }
    278 
    279   if (back) kit_obj_free(back);
    280   free(roundtrip);
    281   kit_obj_builder_free(in);
    282   kit_compiler_free(cc);
    283   kit_target_free(kt);
    284 
    285   if (g_failures) {
    286     fprintf(stderr, "%d failure(s)\n", g_failures);
    287     return g_failures;
    288   }
    289   fputs("rv32_class32: OK\n", stderr);
    290   return 0;
    291 }