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 }