kit

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

mutate.c (10039B)


      1 /* Exercises the post-finalize mutator API:
      2  *
      3  *   1. Build a small ELF with .text + .data + an ext-undef symbol.
      4  *   2. After finalize, remove .data and rename the .text symbol; also
      5  *      flip a SB_GLOBAL symbol to SB_LOCAL.
      6  *   3. Emit + reopen, verify:
      7  *        - .data is gone
      8  *        - the .text symbol shows up under the new name
      9  *        - the localized symbol round-trips as SB_LOCAL
     10  *        - relocs that pointed at .data are dropped
     11  *        - the spurious UNDEF (kind=SK_UNDEF, !referenced) is pruned by the
     12  *          sweep that mutators now share with the historical UNDEF prune. */
     13 
     14 #include <kit/core.h>
     15 #include <kit/object.h>
     16 #include <stdarg.h>
     17 #include <stdio.h>
     18 #include <stdlib.h>
     19 #include <string.h>
     20 
     21 #include "lib/kit_test_target.h"
     22 
     23 static void* heap_alloc(KitHeap* h, size_t n, size_t a) {
     24   (void)h;
     25   (void)a;
     26   return n ? malloc(n) : NULL;
     27 }
     28 static void* heap_realloc(KitHeap* h, void* p, size_t o, size_t n, size_t a) {
     29   (void)h;
     30   (void)o;
     31   (void)a;
     32   return realloc(p, n);
     33 }
     34 static void heap_free(KitHeap* h, void* p, size_t n) {
     35   (void)h;
     36   (void)n;
     37   free(p);
     38 }
     39 static KitHeap g_heap = {heap_alloc, heap_realloc, heap_free, NULL};
     40 
     41 static void diag_emit(KitDiagSink* s, KitDiagKind k, KitSrcLoc loc,
     42                       const char* fmt, va_list ap) {
     43   static const char* names[] = {"note", "warning", "error", "fatal"};
     44   (void)s;
     45   (void)loc;
     46   fprintf(stderr, "%s: ", names[k]);
     47   vfprintf(stderr, fmt, ap);
     48   fputc('\n', stderr);
     49 }
     50 static KitDiagSink g_diag = {diag_emit, NULL, 0, 0};
     51 
     52 static int g_failures;
     53 #define CHECK(cond, ...)                                   \
     54   do {                                                     \
     55     if (!(cond)) {                                         \
     56       fprintf(stderr, "FAIL %s:%d: ", __FILE__, __LINE__); \
     57       fprintf(stderr, __VA_ARGS__);                        \
     58       fputc('\n', stderr);                                 \
     59       g_failures++;                                        \
     60     }                                                      \
     61   } while (0)
     62 
     63 /* mov w0, #7 ; ret */
     64 static const uint8_t TEXT_BYTES[8] = {
     65     0xe0, 0x00, 0x80, 0x52, 0xc0, 0x03, 0x5f, 0xd6,
     66 };
     67 
     68 int main(void) {
     69   KitTargetSpec target;
     70   if (kit_test_target_init(&target) != 0) {
     71     fprintf(stderr, "FAIL: kit_test_target_init\n");
     72     return 1;
     73   }
     74   KitContext ctx = {.heap = &g_heap,
     75                     .file_io = NULL,
     76                     .diag = &g_diag,
     77                     .now = -1};
     78   KitTargetOptions target_opts;
     79   memset(&target_opts, 0, sizeof target_opts);
     80   target_opts.spec = target;
     81   KitTarget* kt = NULL;
     82   if (kit_target_new(&ctx, &target_opts, &kt) != KIT_OK || !kt) {
     83     fprintf(stderr, "FAIL: kit_target_new\n");
     84     return 1;
     85   }
     86   KitCompiler* cc = NULL;
     87   if (kit_compiler_new(kt, &ctx, &cc) != KIT_OK || !cc) {
     88     fprintf(stderr, "FAIL: kit_compiler_new\n");
     89     kit_target_free(kt);
     90     return 1;
     91   }
     92   /* ---- build ---- */
     93   KitObjBuilder* ob = NULL;
     94   CHECK(kit_obj_builder_new(cc, &ob) == KIT_OK && ob, "kit_obj_builder_new");
     95 
     96   KitSym entry_new_nm = kit_sym_intern(cc, KIT_SLICE_LIT("renamed_entry"));
     97 
     98   KitObjSection sec_text = KIT_SECTION_NONE;
     99   KitObjSection sec_data = KIT_SECTION_NONE;
    100   KitObjSectionDesc text_desc = {
    101       .name = kit_sym_intern(cc, KIT_SLICE_LIT(".text")),
    102       .kind = KIT_SEC_TEXT,
    103       .flags = KIT_SF_ALLOC | KIT_SF_EXEC,
    104       .align = 4,
    105       .entsize = 0,
    106   };
    107   KitObjSectionDesc data_desc = {
    108       .name = kit_sym_intern(cc, KIT_SLICE_LIT(".data")),
    109       .kind = KIT_SEC_DATA,
    110       .flags = KIT_SF_ALLOC | KIT_SF_WRITE,
    111       .align = 8,
    112       .entsize = 0,
    113   };
    114   CHECK(kit_obj_builder_section(ob, &text_desc, &sec_text) == KIT_OK,
    115         "section .text");
    116   CHECK(kit_obj_builder_section(ob, &data_desc, &sec_data) == KIT_OK,
    117         "section .data");
    118 
    119   CHECK(kit_obj_builder_write(ob, sec_text, TEXT_BYTES, sizeof TEXT_BYTES) ==
    120             KIT_OK,
    121         "write .text");
    122   static const uint8_t zero8[8] = {0};
    123   CHECK(kit_obj_builder_write(ob, sec_data, zero8, sizeof zero8) == KIT_OK,
    124         "write .data");
    125 
    126   KitObjSymbol sym_entry = KIT_OBJ_SYMBOL_NONE;
    127   KitObjSymbolDesc entry_desc = {
    128       .name = kit_sym_intern(cc, KIT_SLICE_LIT("entry")),
    129       .bind = KIT_SB_GLOBAL,
    130       .kind = KIT_SK_FUNC,
    131       .section = sec_text,
    132       .value = 0,
    133       .size = sizeof TEXT_BYTES,
    134   };
    135   CHECK(kit_obj_builder_symbol(ob, &entry_desc, &sym_entry) == KIT_OK,
    136         "symbol entry");
    137 
    138   KitObjSymbol sym_keep = KIT_OBJ_SYMBOL_NONE;
    139   KitObjSymbolDesc keep_desc = {
    140       .name = kit_sym_intern(cc, KIT_SLICE_LIT("keep_global")),
    141       .bind = KIT_SB_GLOBAL,
    142       .kind = KIT_SK_FUNC,
    143       .section = sec_text,
    144       .value = 0,
    145       .size = 0,
    146   };
    147   CHECK(kit_obj_builder_symbol(ob, &keep_desc, &sym_keep) == KIT_OK,
    148         "symbol keep_global");
    149 
    150   KitObjSymbol sym_foo = KIT_OBJ_SYMBOL_NONE;
    151   KitObjSymbolDesc foo_desc = {
    152       .name = kit_sym_intern(cc, KIT_SLICE_LIT("foo_ref")),
    153       .bind = KIT_SB_GLOBAL,
    154       .kind = KIT_SK_UNDEF,
    155       .section = KIT_SECTION_NONE,
    156       .value = 0,
    157       .size = 0,
    158   };
    159   CHECK(kit_obj_builder_symbol(ob, &foo_desc, &sym_foo) == KIT_OK,
    160         "symbol foo_ref");
    161 
    162   /* Spurious extern: !referenced means sweep will tombstone it. */
    163   KitObjSymbol sym_spurious = KIT_OBJ_SYMBOL_NONE;
    164   KitObjSymbolDesc spurious_desc = {
    165       .name = kit_sym_intern(cc, KIT_SLICE_LIT("spurious_extern")),
    166       .bind = KIT_SB_GLOBAL,
    167       .kind = KIT_SK_UNDEF,
    168       .section = KIT_SECTION_NONE,
    169       .value = 0,
    170       .size = 0,
    171   };
    172   CHECK(kit_obj_builder_symbol(ob, &spurious_desc, &sym_spurious) == KIT_OK,
    173         "symbol spurious_extern");
    174   (void)sym_spurious;
    175 
    176   /* One reloc in .data against foo_ref (which is referenced — survives).
    177    * One reloc in .data against the about-to-rename entry (also survives but
    178    * the containing .data gets removed, so the reloc dies via cascade). */
    179   KitObjRelocDesc foo_reloc = {
    180       .section = sec_data,
    181       .offset = 0,
    182       .kind = {.arch = target.arch,
    183                .obj_fmt = target.obj,
    184                .code = KIT_RELOC_ABS64},
    185       .symbol = sym_foo,
    186       .addend = 0,
    187   };
    188   KitObjRelocDesc entry_reloc = {
    189       .section = sec_data,
    190       .offset = 8,
    191       .kind = {.arch = target.arch,
    192                .obj_fmt = target.obj,
    193                .code = KIT_RELOC_ABS64},
    194       .symbol = sym_entry,
    195       .addend = 0,
    196   };
    197   CHECK(kit_obj_builder_reloc(ob, &foo_reloc) == KIT_OK, "reloc foo_ref");
    198   CHECK(kit_obj_builder_reloc(ob, &entry_reloc) == KIT_OK, "reloc entry");
    199 
    200   CHECK(kit_obj_builder_finalize(ob) == KIT_OK, "finalize");
    201 
    202   /* ---- mutate via the public API ---- */
    203   CHECK(kit_obj_builder_remove_section(ob, sec_data) == KIT_OK,
    204         "remove_section .data");
    205   CHECK(kit_obj_builder_rename_symbol(ob, sym_entry, entry_new_nm) == KIT_OK,
    206         "rename_symbol entry -> renamed_entry");
    207   CHECK(kit_obj_builder_symbol_set_bind(ob, sym_keep, KIT_SB_LOCAL) == KIT_OK,
    208         "set_bind keep_global -> local");
    209 
    210   /* ---- emit + reopen ---- */
    211   KitWriter* w = NULL;
    212   (void)kit_writer_mem(&g_heap, &w);
    213   CHECK(kit_obj_builder_emit(ob, w) == KIT_OK, "emit after mutate");
    214 
    215   size_t out_len = 0;
    216   const uint8_t* out_data = kit_writer_mem_bytes(w, &out_len);
    217   uint8_t* roundtrip = (uint8_t*)malloc(out_len ? out_len : 1);
    218   memcpy(roundtrip, out_data, out_len);
    219   kit_writer_close(w);
    220 
    221   KitSlice input = {.data = roundtrip, .len = out_len};
    222   KitObjFile* f = NULL;
    223   CHECK(kit_obj_open(&ctx, KIT_SLICE_LIT("mutate"), &input, &f) == KIT_OK && f,
    224         "reopen");
    225 
    226   if (f) {
    227     /* .data should be gone. */
    228     KitObjSection s_data = KIT_SECTION_NONE;
    229     KitStatus st_data =
    230         kit_obj_section_by_name(f, KIT_SLICE_LIT(".data"), &s_data);
    231     CHECK(st_data == KIT_NOT_FOUND, ".data still present after removal");
    232 
    233     /* .text should remain. */
    234     KitObjSection s_text = KIT_SECTION_NONE;
    235     CHECK(kit_obj_section_by_name(f, KIT_SLICE_LIT(".text"), &s_text) == KIT_OK,
    236           ".text missing after roundtrip");
    237 
    238     /* Renamed entry symbol is present; old name is gone. */
    239     KitObjSymInfo si;
    240     CHECK(kit_obj_symbol_by_name(f, KIT_SLICE_LIT("renamed_entry"), &si) ==
    241               KIT_OK,
    242           "renamed_entry not found");
    243     CHECK(
    244         kit_obj_symbol_by_name(f, KIT_SLICE_LIT("entry"), &si) == KIT_NOT_FOUND,
    245         "old 'entry' symbol survived rename");
    246 
    247     /* keep_global was localized; reads back as SB_LOCAL. */
    248     CHECK(
    249         kit_obj_symbol_by_name(f, KIT_SLICE_LIT("keep_global"), &si) == KIT_OK,
    250         "keep_global lost");
    251     CHECK(si.bind == KIT_SB_LOCAL, "keep_global bind=%d, want LOCAL=%d",
    252           (int)si.bind, (int)KIT_SB_LOCAL);
    253 
    254     /* Spurious extern was pruned by the sweep. */
    255     CHECK(kit_obj_symbol_by_name(f, KIT_SLICE_LIT("spurious_extern"), &si) ==
    256               KIT_NOT_FOUND,
    257           "spurious_extern survived sweep");
    258 
    259     /* foo_ref was the target of a reloc, but its containing section
    260      * (.data) was removed — the reloc is gone, but is foo_ref itself
    261      * still referenced enough to survive? The sweep marks it referenced
    262      * when the relocation is created, BUT the reloc is then dropped at
    263      * sweep. The symbol's `referenced` flag remains set, so foo_ref survives
    264      * as a plain UNDEF. */
    265     CHECK(kit_obj_symbol_by_name(f, KIT_SLICE_LIT("foo_ref"), &si) == KIT_OK,
    266           "foo_ref UNDEF should survive");
    267 
    268     /* No relocs should remain — all were in the removed .data. */
    269     KitObjRelocIter* rit = NULL;
    270     CHECK(kit_obj_reliter_new(f, &rit) == KIT_OK, "reliter_new");
    271     int nrel = 0;
    272     KitObjReloc r;
    273     while (kit_obj_reliter_next(rit, &r) == KIT_ITER_ITEM) ++nrel;
    274     kit_obj_reliter_free(rit);
    275     CHECK(nrel == 0, "expected 0 relocs after removing .data, got %d", nrel);
    276 
    277     kit_obj_free(f);
    278   }
    279 
    280   free(roundtrip);
    281   kit_obj_builder_free(ob);
    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 1;
    288   }
    289   fputs("mutate: OK\n", stderr);
    290   return 0;
    291 }