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 }