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 }