kit

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

mutate.c (10076B)


      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                     .metrics = NULL,
     78                     .now = -1};
     79   KitTargetOptions target_opts;
     80   memset(&target_opts, 0, sizeof target_opts);
     81   target_opts.spec = target;
     82   KitTarget* kt = NULL;
     83   if (kit_target_new(&ctx, &target_opts, &kt) != KIT_OK || !kt) {
     84     fprintf(stderr, "FAIL: kit_target_new\n");
     85     return 1;
     86   }
     87   KitCompiler* cc = NULL;
     88   if (kit_compiler_new(kt, &ctx, &cc) != KIT_OK || !cc) {
     89     fprintf(stderr, "FAIL: kit_compiler_new\n");
     90     kit_target_free(kt);
     91     return 1;
     92   }
     93   /* ---- build ---- */
     94   KitObjBuilder* ob = NULL;
     95   CHECK(kit_obj_builder_new(cc, &ob) == KIT_OK && ob, "kit_obj_builder_new");
     96 
     97   KitSym entry_new_nm = kit_sym_intern(cc, KIT_SLICE_LIT("renamed_entry"));
     98 
     99   KitObjSection sec_text = KIT_SECTION_NONE;
    100   KitObjSection sec_data = KIT_SECTION_NONE;
    101   KitObjSectionDesc text_desc = {
    102       .name = kit_sym_intern(cc, KIT_SLICE_LIT(".text")),
    103       .kind = KIT_SEC_TEXT,
    104       .flags = KIT_SF_ALLOC | KIT_SF_EXEC,
    105       .align = 4,
    106       .entsize = 0,
    107   };
    108   KitObjSectionDesc data_desc = {
    109       .name = kit_sym_intern(cc, KIT_SLICE_LIT(".data")),
    110       .kind = KIT_SEC_DATA,
    111       .flags = KIT_SF_ALLOC | KIT_SF_WRITE,
    112       .align = 8,
    113       .entsize = 0,
    114   };
    115   CHECK(kit_obj_builder_section(ob, &text_desc, &sec_text) == KIT_OK,
    116         "section .text");
    117   CHECK(kit_obj_builder_section(ob, &data_desc, &sec_data) == KIT_OK,
    118         "section .data");
    119 
    120   CHECK(kit_obj_builder_write(ob, sec_text, TEXT_BYTES, sizeof TEXT_BYTES) ==
    121             KIT_OK,
    122         "write .text");
    123   static const uint8_t zero8[8] = {0};
    124   CHECK(kit_obj_builder_write(ob, sec_data, zero8, sizeof zero8) == KIT_OK,
    125         "write .data");
    126 
    127   KitObjSymbol sym_entry = KIT_OBJ_SYMBOL_NONE;
    128   KitObjSymbolDesc entry_desc = {
    129       .name = kit_sym_intern(cc, KIT_SLICE_LIT("entry")),
    130       .bind = KIT_SB_GLOBAL,
    131       .kind = KIT_SK_FUNC,
    132       .section = sec_text,
    133       .value = 0,
    134       .size = sizeof TEXT_BYTES,
    135   };
    136   CHECK(kit_obj_builder_symbol(ob, &entry_desc, &sym_entry) == KIT_OK,
    137         "symbol entry");
    138 
    139   KitObjSymbol sym_keep = KIT_OBJ_SYMBOL_NONE;
    140   KitObjSymbolDesc keep_desc = {
    141       .name = kit_sym_intern(cc, KIT_SLICE_LIT("keep_global")),
    142       .bind = KIT_SB_GLOBAL,
    143       .kind = KIT_SK_FUNC,
    144       .section = sec_text,
    145       .value = 0,
    146       .size = 0,
    147   };
    148   CHECK(kit_obj_builder_symbol(ob, &keep_desc, &sym_keep) == KIT_OK,
    149         "symbol keep_global");
    150 
    151   KitObjSymbol sym_foo = KIT_OBJ_SYMBOL_NONE;
    152   KitObjSymbolDesc foo_desc = {
    153       .name = kit_sym_intern(cc, KIT_SLICE_LIT("foo_ref")),
    154       .bind = KIT_SB_GLOBAL,
    155       .kind = KIT_SK_UNDEF,
    156       .section = KIT_SECTION_NONE,
    157       .value = 0,
    158       .size = 0,
    159   };
    160   CHECK(kit_obj_builder_symbol(ob, &foo_desc, &sym_foo) == KIT_OK,
    161         "symbol foo_ref");
    162 
    163   /* Spurious extern: !referenced means sweep will tombstone it. */
    164   KitObjSymbol sym_spurious = KIT_OBJ_SYMBOL_NONE;
    165   KitObjSymbolDesc spurious_desc = {
    166       .name = kit_sym_intern(cc, KIT_SLICE_LIT("spurious_extern")),
    167       .bind = KIT_SB_GLOBAL,
    168       .kind = KIT_SK_UNDEF,
    169       .section = KIT_SECTION_NONE,
    170       .value = 0,
    171       .size = 0,
    172   };
    173   CHECK(kit_obj_builder_symbol(ob, &spurious_desc, &sym_spurious) == KIT_OK,
    174         "symbol spurious_extern");
    175   (void)sym_spurious;
    176 
    177   /* One reloc in .data against foo_ref (which is referenced — survives).
    178    * One reloc in .data against the about-to-rename entry (also survives but
    179    * the containing .data gets removed, so the reloc dies via cascade). */
    180   KitObjRelocDesc foo_reloc = {
    181       .section = sec_data,
    182       .offset = 0,
    183       .kind = {.arch = target.arch,
    184                .obj_fmt = target.obj,
    185                .code = KIT_RELOC_ABS64},
    186       .symbol = sym_foo,
    187       .addend = 0,
    188   };
    189   KitObjRelocDesc entry_reloc = {
    190       .section = sec_data,
    191       .offset = 8,
    192       .kind = {.arch = target.arch,
    193                .obj_fmt = target.obj,
    194                .code = KIT_RELOC_ABS64},
    195       .symbol = sym_entry,
    196       .addend = 0,
    197   };
    198   CHECK(kit_obj_builder_reloc(ob, &foo_reloc) == KIT_OK, "reloc foo_ref");
    199   CHECK(kit_obj_builder_reloc(ob, &entry_reloc) == KIT_OK, "reloc entry");
    200 
    201   CHECK(kit_obj_builder_finalize(ob) == KIT_OK, "finalize");
    202 
    203   /* ---- mutate via the public API ---- */
    204   CHECK(kit_obj_builder_remove_section(ob, sec_data) == KIT_OK,
    205         "remove_section .data");
    206   CHECK(kit_obj_builder_rename_symbol(ob, sym_entry, entry_new_nm) == KIT_OK,
    207         "rename_symbol entry -> renamed_entry");
    208   CHECK(kit_obj_builder_symbol_set_bind(ob, sym_keep, KIT_SB_LOCAL) == KIT_OK,
    209         "set_bind keep_global -> local");
    210 
    211   /* ---- emit + reopen ---- */
    212   KitWriter* w = NULL;
    213   (void)kit_writer_mem(&g_heap, &w);
    214   CHECK(kit_obj_builder_emit(ob, w) == KIT_OK, "emit after mutate");
    215 
    216   size_t out_len = 0;
    217   const uint8_t* out_data = kit_writer_mem_bytes(w, &out_len);
    218   uint8_t* roundtrip = (uint8_t*)malloc(out_len ? out_len : 1);
    219   memcpy(roundtrip, out_data, out_len);
    220   kit_writer_close(w);
    221 
    222   KitSlice input = {.data = roundtrip, .len = out_len};
    223   KitObjFile* f = NULL;
    224   CHECK(kit_obj_open(&ctx, KIT_SLICE_LIT("mutate"), &input, &f) == KIT_OK && f,
    225         "reopen");
    226 
    227   if (f) {
    228     /* .data should be gone. */
    229     KitObjSection s_data = KIT_SECTION_NONE;
    230     KitStatus st_data =
    231         kit_obj_section_by_name(f, KIT_SLICE_LIT(".data"), &s_data);
    232     CHECK(st_data == KIT_NOT_FOUND, ".data still present after removal");
    233 
    234     /* .text should remain. */
    235     KitObjSection s_text = KIT_SECTION_NONE;
    236     CHECK(kit_obj_section_by_name(f, KIT_SLICE_LIT(".text"), &s_text) == KIT_OK,
    237           ".text missing after roundtrip");
    238 
    239     /* Renamed entry symbol is present; old name is gone. */
    240     KitObjSymInfo si;
    241     CHECK(kit_obj_symbol_by_name(f, KIT_SLICE_LIT("renamed_entry"), &si) ==
    242               KIT_OK,
    243           "renamed_entry not found");
    244     CHECK(
    245         kit_obj_symbol_by_name(f, KIT_SLICE_LIT("entry"), &si) == KIT_NOT_FOUND,
    246         "old 'entry' symbol survived rename");
    247 
    248     /* keep_global was localized; reads back as SB_LOCAL. */
    249     CHECK(
    250         kit_obj_symbol_by_name(f, KIT_SLICE_LIT("keep_global"), &si) == KIT_OK,
    251         "keep_global lost");
    252     CHECK(si.bind == KIT_SB_LOCAL, "keep_global bind=%d, want LOCAL=%d",
    253           (int)si.bind, (int)KIT_SB_LOCAL);
    254 
    255     /* Spurious extern was pruned by the sweep. */
    256     CHECK(kit_obj_symbol_by_name(f, KIT_SLICE_LIT("spurious_extern"), &si) ==
    257               KIT_NOT_FOUND,
    258           "spurious_extern survived sweep");
    259 
    260     /* foo_ref was the target of a reloc, but its containing section
    261      * (.data) was removed — the reloc is gone, but is foo_ref itself
    262      * still referenced enough to survive? The sweep marks it referenced
    263      * when the relocation is created, BUT the reloc is then dropped at
    264      * sweep. The symbol's `referenced` flag remains set, so foo_ref survives
    265      * as a plain UNDEF. */
    266     CHECK(kit_obj_symbol_by_name(f, KIT_SLICE_LIT("foo_ref"), &si) == KIT_OK,
    267           "foo_ref UNDEF should survive");
    268 
    269     /* No relocs should remain — all were in the removed .data. */
    270     KitObjRelocIter* rit = NULL;
    271     CHECK(kit_obj_reliter_new(f, &rit) == KIT_OK, "reliter_new");
    272     int nrel = 0;
    273     KitObjReloc r;
    274     while (kit_obj_reliter_next(rit, &r) == KIT_ITER_ITEM) ++nrel;
    275     kit_obj_reliter_free(rit);
    276     CHECK(nrel == 0, "expected 0 relocs after removing .data, got %d", nrel);
    277 
    278     kit_obj_free(f);
    279   }
    280 
    281   free(roundtrip);
    282   kit_obj_builder_free(ob);
    283   kit_compiler_free(cc);
    284   kit_target_free(kt);
    285 
    286   if (g_failures) {
    287     fprintf(stderr, "%d failure(s)\n", g_failures);
    288     return 1;
    289   }
    290   fputs("mutate: OK\n", stderr);
    291   return 0;
    292 }