kit

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

smoke.c (10303B)


      1 /* Hand-built KitObjBuilder roundtrip test.
      2  *
      3  * Builds a tiny AArch64-Linux ELF in memory: one .text section with two
      4  * AArch64 instructions, one .data section with an R_ABS64 relocation
      5  * against an external symbol, then runs:
      6  *
      7  *     kit_obj_builder_emit(ob, mem_writer)
      8  *     kit_obj_open("smoke", bytes, len)
      9  *
     10  * and checks that the readback produces the same shape (modulo
     11  * synthesized STT_SECTION symbols and section ordering — the equivalence
     12  * the ELF reader's documented object equivalence.
     13  *
     14  * Exit 0 = pass; non-zero = fail (with a one-line stderr explanation). */
     15 
     16 #include <kit/core.h>
     17 #include <kit/object.h>
     18 #include <stdarg.h>
     19 #include <stdio.h>
     20 #include <stdlib.h>
     21 #include <string.h>
     22 
     23 #include "lib/kit_test_target.h"
     24 
     25 /* ---- env ---- */
     26 
     27 static void* heap_alloc(KitHeap* h, size_t n, size_t a) {
     28   (void)h;
     29   (void)a;
     30   return n ? malloc(n) : NULL;
     31 }
     32 static void* heap_realloc(KitHeap* h, void* p, size_t o, size_t n, size_t a) {
     33   (void)h;
     34   (void)o;
     35   (void)a;
     36   return realloc(p, n);
     37 }
     38 static void heap_free(KitHeap* h, void* p, size_t n) {
     39   (void)h;
     40   (void)n;
     41   free(p);
     42 }
     43 static KitHeap g_heap = {heap_alloc, heap_realloc, heap_free, NULL};
     44 
     45 static void diag_emit(KitDiagSink* s, KitDiagKind k, KitSrcLoc loc,
     46                       const char* fmt, va_list ap) {
     47   static const char* names[] = {"note", "warning", "error", "fatal"};
     48   (void)s;
     49   (void)loc;
     50   fprintf(stderr, "%s: ", names[k]);
     51   vfprintf(stderr, fmt, ap);
     52   fputc('\n', stderr);
     53 }
     54 static KitDiagSink g_diag = {diag_emit, NULL, 0, 0};
     55 
     56 /* ---- assertion helpers ---- */
     57 
     58 static int g_failures;
     59 #define CHECK(cond, ...)                                   \
     60   do {                                                     \
     61     if (!(cond)) {                                         \
     62       fprintf(stderr, "FAIL %s:%d: ", __FILE__, __LINE__); \
     63       fprintf(stderr, __VA_ARGS__);                        \
     64       fputc('\n', stderr);                                 \
     65       g_failures++;                                        \
     66     }                                                      \
     67   } while (0)
     68 
     69 /* ---- input fixture ---- */
     70 
     71 /* mov w0, #42 ; ret  — AArch64, little-endian. */
     72 static const uint8_t TEXT_BYTES[8] = {
     73     0x40, 0x05, 0x80, 0x52, 0xc0, 0x03, 0x5f, 0xd6,
     74 };
     75 
     76 static KitObjBuilder* build_input(KitCompiler* c, KitTargetSpec target) {
     77   KitObjBuilder* ob = NULL;
     78   KitObjSection sec_text = KIT_SECTION_NONE;
     79   KitObjSection sec_data = KIT_SECTION_NONE;
     80   KitObjSymbol sym_foo = KIT_OBJ_SYMBOL_NONE;
     81 
     82   CHECK(kit_obj_builder_new(c, &ob) == KIT_OK && ob, "kit_obj_builder_new");
     83   if (!ob) return NULL;
     84 
     85   KitObjSectionDesc text_desc = {
     86       .name = kit_sym_intern(c, KIT_SLICE_LIT(".text")),
     87       .kind = KIT_SEC_TEXT,
     88       .flags = KIT_SF_ALLOC | KIT_SF_EXEC,
     89       .align = 4,
     90       .entsize = 0,
     91   };
     92   KitObjSectionDesc data_desc = {
     93       .name = kit_sym_intern(c, KIT_SLICE_LIT(".data")),
     94       .kind = KIT_SEC_DATA,
     95       .flags = KIT_SF_ALLOC | KIT_SF_WRITE,
     96       .align = 8,
     97       .entsize = 0,
     98   };
     99   CHECK(kit_obj_builder_section(ob, &text_desc, &sec_text) == KIT_OK,
    100         "section .text");
    101   CHECK(kit_obj_builder_section(ob, &data_desc, &sec_data) == KIT_OK,
    102         "section .data");
    103   CHECK(kit_obj_builder_write(ob, sec_text, TEXT_BYTES, sizeof TEXT_BYTES) ==
    104             KIT_OK,
    105         "write .text");
    106 
    107   static const uint8_t zero8[8] = {0};
    108   CHECK(kit_obj_builder_write(ob, sec_data, zero8, sizeof zero8) == KIT_OK,
    109         "write .data");
    110 
    111   KitObjSymbolDesc main_desc = {
    112       .name = kit_sym_intern(c, KIT_SLICE_LIT("main")),
    113       .bind = KIT_SB_GLOBAL,
    114       .kind = KIT_SK_FUNC,
    115       .section = sec_text,
    116       .value = 0,
    117       .size = sizeof TEXT_BYTES,
    118   };
    119   KitObjSymbol sym_main = KIT_OBJ_SYMBOL_NONE;
    120   CHECK(kit_obj_builder_symbol(ob, &main_desc, &sym_main) == KIT_OK,
    121         "symbol main");
    122 
    123   KitObjSymbolDesc foo_desc = {
    124       .name = kit_sym_intern(c, KIT_SLICE_LIT("foo")),
    125       .bind = KIT_SB_GLOBAL,
    126       .kind = KIT_SK_UNDEF,
    127       .section = KIT_SECTION_NONE,
    128       .value = 0,
    129       .size = 0,
    130   };
    131   CHECK(kit_obj_builder_symbol(ob, &foo_desc, &sym_foo) == KIT_OK,
    132         "symbol foo");
    133 
    134   KitObjRelocDesc reloc_desc = {
    135       .section = sec_data,
    136       .offset = 0,
    137       .kind = {.arch = target.arch,
    138                .obj_fmt = target.obj,
    139                .code = KIT_RELOC_ABS64},
    140       .symbol = sym_foo,
    141       .addend = 0,
    142   };
    143   CHECK(kit_obj_builder_reloc(ob, &reloc_desc) == KIT_OK, "reloc .data");
    144 
    145   CHECK(kit_obj_builder_finalize(ob) == KIT_OK, "finalize");
    146   return ob;
    147 }
    148 
    149 /* ---- shape inspection on kit_obj_open output ---- */
    150 
    151 static void verify_shape(KitObjFile* f) {
    152   KitObjSection text = KIT_SECTION_NONE;
    153   KitObjSection data = KIT_SECTION_NONE;
    154   CHECK(kit_obj_section_by_name(f, KIT_SLICE_LIT(".text"), &text) == KIT_OK,
    155         ".text not present after roundtrip");
    156   CHECK(kit_obj_section_by_name(f, KIT_SLICE_LIT(".data"), &data) == KIT_OK,
    157         ".data not present after roundtrip");
    158 
    159   if (text != KIT_SECTION_NONE) {
    160     KitObjSecInfo si;
    161     const uint8_t* bytes = NULL;
    162     size_t len = 0;
    163     CHECK(kit_obj_section(f, text, &si) == KIT_OK, "section .text");
    164     CHECK(si.kind == KIT_SEC_TEXT, ".text kind is %u, want %u", si.kind,
    165           KIT_SEC_TEXT);
    166     CHECK((si.flags & KIT_SF_EXEC) != 0, ".text missing SF_EXEC");
    167     CHECK((si.flags & KIT_SF_ALLOC) != 0, ".text missing SF_ALLOC");
    168     CHECK(si.size == sizeof TEXT_BYTES,
    169           ".text size mismatch: got %llu, want %zu",
    170           (unsigned long long)si.size, sizeof TEXT_BYTES);
    171     CHECK(kit_obj_section_data(f, text, &bytes, &len) == KIT_OK,
    172           "section_data .text");
    173     CHECK(len == sizeof TEXT_BYTES,
    174           ".text data size mismatch: got %zu, want %zu", len,
    175           sizeof TEXT_BYTES);
    176     if (len == sizeof TEXT_BYTES) {
    177       CHECK(memcmp(bytes, TEXT_BYTES, sizeof TEXT_BYTES) == 0,
    178             ".text bytes do not match input");
    179     }
    180   }
    181   if (data != KIT_SECTION_NONE) {
    182     KitObjSecInfo si;
    183     CHECK(kit_obj_section(f, data, &si) == KIT_OK, "section .data");
    184     CHECK(si.kind == KIT_SEC_DATA, ".data kind is %u", si.kind);
    185     CHECK((si.flags & KIT_SF_WRITE) != 0, ".data missing SF_WRITE");
    186   }
    187 
    188   /* Symbols. main: defined function in .text. foo: undefined external. */
    189   KitObjSymInfo main_si;
    190   KitObjSymInfo foo_si;
    191   CHECK(kit_obj_symbol_by_name(f, KIT_SLICE_LIT("main"), &main_si) == KIT_OK,
    192         "missing 'main' symbol");
    193   CHECK(kit_obj_symbol_by_name(f, KIT_SLICE_LIT("foo"), &foo_si) == KIT_OK,
    194         "missing 'foo' symbol");
    195   if (kit_obj_symbol_by_name(f, KIT_SLICE_LIT("main"), &main_si) == KIT_OK) {
    196     CHECK(main_si.bind == KIT_SB_GLOBAL, "main bind=%u", main_si.bind);
    197     CHECK(main_si.kind == KIT_SK_FUNC, "main kind=%u", main_si.kind);
    198   }
    199   if (kit_obj_symbol_by_name(f, KIT_SLICE_LIT("foo"), &foo_si) == KIT_OK) {
    200     CHECK(foo_si.bind == KIT_SB_GLOBAL, "foo bind=%u", foo_si.bind);
    201     CHECK(foo_si.kind == KIT_SK_UNDEF, "foo kind=%u (want SK_UNDEF=%u)",
    202           foo_si.kind, KIT_SK_UNDEF);
    203     CHECK(foo_si.section == KIT_SECTION_NONE, "foo not undefined");
    204   }
    205 
    206   /* Relocation: a single R_ABS64 against 'foo' in .data, offset 0. */
    207   if (data != KIT_SECTION_NONE) {
    208     KitObjRelocIter* it = NULL;
    209     KitObjReloc found;
    210     int ndata_relocs = 0;
    211     int have_found = 0;
    212     CHECK(kit_obj_reliter_new(f, &it) == KIT_OK && it, "reliter_new");
    213     if (it) {
    214       KitObjReloc r;
    215       while (kit_obj_reliter_next(it, &r) == KIT_ITER_ITEM) {
    216         if (r.section == data) {
    217           ++ndata_relocs;
    218           found = r;
    219           have_found = 1;
    220         }
    221       }
    222       kit_obj_reliter_free(it);
    223     }
    224     CHECK(ndata_relocs == 1, ".data reloc count = %d, want 1", ndata_relocs);
    225     CHECK(have_found, "no reloc on .data");
    226     if (have_found) {
    227       CHECK(found.kind.code == KIT_RELOC_ABS64,
    228             ".data reloc kind=%u (want ABS64=%u)", found.kind.code,
    229             KIT_RELOC_ABS64);
    230       CHECK(found.offset == 0, ".data reloc offset=%llu",
    231             (unsigned long long)found.offset);
    232       CHECK(found.addend == 0, ".data reloc addend=%lld",
    233             (long long)found.addend);
    234       CHECK(kit_slice_eq_cstr(found.sym_name, "foo"),
    235             "reloc target name = %.*s", KIT_SLICE_ARG(found.sym_name));
    236     }
    237   }
    238 }
    239 
    240 /* ---- main ---- */
    241 
    242 int main(void) {
    243   KitTargetSpec target;
    244   if (kit_test_target_init(&target) != 0) {
    245     fprintf(stderr, "FAIL: kit_test_target_init\n");
    246     return 1;
    247   }
    248 
    249   KitContext ctx;
    250   memset(&ctx, 0, sizeof ctx);
    251   ctx.heap = &g_heap;
    252   ctx.diag = &g_diag;
    253   ctx.now = -1;
    254 
    255   KitTargetOptions target_opts;
    256   memset(&target_opts, 0, sizeof target_opts);
    257   target_opts.spec = target;
    258   KitTarget* kt = NULL;
    259   if (kit_target_new(&ctx, &target_opts, &kt) != KIT_OK || !kt) {
    260     fprintf(stderr, "FAIL: kit_target_new\n");
    261     return 1;
    262   }
    263   KitCompiler* cc = NULL;
    264   if (kit_compiler_new(kt, &ctx, &cc) != KIT_OK || !cc) {
    265     fprintf(stderr, "FAIL: kit_compiler_new\n");
    266     kit_target_free(kt);
    267     return 1;
    268   }
    269   /* Build, emit, read back, inspect. */
    270   KitObjBuilder* in = build_input(cc, target);
    271 
    272   KitWriter* w = NULL;
    273   (void)kit_writer_mem(&g_heap, &w);
    274   CHECK(kit_obj_builder_emit(in, w) == KIT_OK, "emit");
    275   size_t out_len = 0;
    276   const uint8_t* out_data = kit_writer_mem_bytes(w, &out_len);
    277 
    278   /* Sanity: ELF magic. */
    279   CHECK(out_len >= 64, "ELF emit produced too few bytes (%zu)", out_len);
    280   CHECK(out_len >= 4 && out_data[0] == 0x7f && out_data[1] == 'E' &&
    281             out_data[2] == 'L' && out_data[3] == 'F',
    282         "ELF emit output missing ELF magic");
    283 
    284   /* Round-trip: copy to private buffer first since the mem writer's
    285    * storage is freed on close. */
    286   uint8_t* roundtrip = (uint8_t*)malloc(out_len ? out_len : 1);
    287   memcpy(roundtrip, out_data, out_len);
    288   kit_writer_close(w);
    289 
    290   KitSlice input = {.data = roundtrip, .len = out_len};
    291   KitObjFile* back = NULL;
    292   CHECK(kit_obj_open(&ctx, KIT_SLICE_LIT("smoke"), &input, &back) == KIT_OK &&
    293             back,
    294         "kit_obj_open failed");
    295   if (back) verify_shape(back);
    296 
    297   if (back) kit_obj_free(back);
    298   free(roundtrip);
    299   kit_obj_builder_free(in);
    300   kit_compiler_free(cc);
    301   kit_target_free(kt);
    302 
    303   if (g_failures) {
    304     fprintf(stderr, "%d failure(s)\n", g_failures);
    305     return 1;
    306   }
    307   fputs("smoke: OK\n", stderr);
    308   return 0;
    309 }