x64_disasm_annotations.c (9497B)
1 /* x86_64 ELF relocation/disassembly annotation coverage. 2 * 3 * x86_64 relocation offsets usually point at an instruction's disp/imm field 4 * rather than at byte 0 of the instruction. This test builds a tiny ELF object 5 * whose relocations sit inside call, RIP-relative load, and jump encodings, 6 * then checks that object disassembly annotates the owning instruction. */ 7 8 #include <kit/core.h> 9 #include <kit/disasm.h> 10 #include <kit/object.h> 11 #include <stdarg.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 16 static void* heap_alloc(KitHeap* h, size_t n, size_t a) { 17 (void)h; 18 (void)a; 19 return n ? malloc(n) : NULL; 20 } 21 static void* heap_realloc(KitHeap* h, void* p, size_t o, size_t n, size_t a) { 22 (void)h; 23 (void)o; 24 (void)a; 25 return realloc(p, n); 26 } 27 static void heap_free(KitHeap* h, void* p, size_t n) { 28 (void)h; 29 (void)n; 30 free(p); 31 } 32 static KitHeap g_heap = {heap_alloc, heap_realloc, heap_free, NULL}; 33 34 static void diag_emit(KitDiagSink* s, KitDiagKind k, KitSrcLoc loc, 35 const char* fmt, va_list ap) { 36 static const char* names[] = {"note", "warning", "error", "fatal"}; 37 (void)s; 38 (void)loc; 39 fprintf(stderr, "%s: ", names[k]); 40 vfprintf(stderr, fmt, ap); 41 fputc('\n', stderr); 42 } 43 static KitDiagSink g_diag = {diag_emit, NULL, 0, 0}; 44 45 static int g_failures; 46 #define CHECK(cond, ...) \ 47 do { \ 48 if (!(cond)) { \ 49 fprintf(stderr, "FAIL %s:%d: ", __FILE__, __LINE__); \ 50 fprintf(stderr, __VA_ARGS__); \ 51 fputc('\n', stderr); \ 52 g_failures++; \ 53 } \ 54 } while (0) 55 56 static KitTargetSpec x64_elf_target(void) { 57 KitTargetSpec t; 58 memset(&t, 0, sizeof t); 59 t.arch = KIT_ARCH_X86_64; 60 t.os = KIT_OS_LINUX; 61 t.obj = KIT_OBJ_ELF; 62 t.ptr_size = 8; 63 t.ptr_align = 8; 64 return t; 65 } 66 67 static KitObjBuilder* build_input(KitCompiler* c, KitTargetSpec target) { 68 KitObjBuilder* ob = NULL; 69 KitObjSection sec_text = KIT_SECTION_NONE; 70 CHECK(kit_obj_builder_new(c, &ob) == KIT_OK && ob, "kit_obj_builder_new"); 71 if (!ob) return NULL; 72 73 KitObjSectionDesc text_desc = { 74 .name = kit_sym_intern(c, KIT_SLICE_LIT(".text")), 75 .kind = KIT_SEC_TEXT, 76 .flags = KIT_SF_ALLOC | KIT_SF_EXEC, 77 .align = 16, 78 .entsize = 0, 79 }; 80 CHECK(kit_obj_builder_section(ob, &text_desc, &sec_text) == KIT_OK, 81 "section .text"); 82 83 static const uint8_t text_bytes[] = { 84 0xe8, 0x00, 0x00, 0x00, 0x00, /* call rel32 */ 85 0x48, 0x8b, 0x05, 0x00, 0x00, 0x00, 0x00, /* mov disp32(%rip), %rax */ 86 0xe9, 0x00, 0x00, 0x00, 0x00, /* jmp rel32 */ 87 0xc3, /* ret */ 88 }; 89 CHECK(kit_obj_builder_write(ob, sec_text, text_bytes, sizeof text_bytes) == 90 KIT_OK, 91 "write .text"); 92 93 KitObjSymbol sym_start = KIT_OBJ_SYMBOL_NONE; 94 KitObjSymbolDesc start_desc = { 95 .name = kit_sym_intern(c, KIT_SLICE_LIT("_start")), 96 .bind = KIT_SB_GLOBAL, 97 .kind = KIT_SK_FUNC, 98 .section = sec_text, 99 .value = 0, 100 .size = sizeof text_bytes, 101 }; 102 CHECK(kit_obj_builder_symbol(ob, &start_desc, &sym_start) == KIT_OK, 103 "symbol _start"); 104 105 KitObjSymbol sym_load_data = KIT_OBJ_SYMBOL_NONE; 106 KitObjSymbolDesc load_data_desc = { 107 .name = kit_sym_intern(c, KIT_SLICE_LIT("load_data")), 108 .bind = KIT_SB_LOCAL, 109 .kind = KIT_SK_NOTYPE, 110 .section = sec_text, 111 .value = 5, 112 .size = 0, 113 }; 114 CHECK(kit_obj_builder_symbol(ob, &load_data_desc, &sym_load_data) == KIT_OK, 115 "symbol load_data"); 116 117 KitObjSymbol sym_foo = KIT_OBJ_SYMBOL_NONE; 118 KitObjSymbolDesc foo_desc = { 119 .name = kit_sym_intern(c, KIT_SLICE_LIT("foo")), 120 .bind = KIT_SB_GLOBAL, 121 .kind = KIT_SK_UNDEF, 122 .section = KIT_SECTION_NONE, 123 .value = 0, 124 .size = 0, 125 }; 126 CHECK(kit_obj_builder_symbol(ob, &foo_desc, &sym_foo) == KIT_OK, 127 "symbol foo"); 128 129 KitObjSymbol sym_bar = KIT_OBJ_SYMBOL_NONE; 130 KitObjSymbolDesc bar_desc = { 131 .name = kit_sym_intern(c, KIT_SLICE_LIT("bar")), 132 .bind = KIT_SB_GLOBAL, 133 .kind = KIT_SK_UNDEF, 134 .section = KIT_SECTION_NONE, 135 .value = 0, 136 .size = 0, 137 }; 138 CHECK(kit_obj_builder_symbol(ob, &bar_desc, &sym_bar) == KIT_OK, 139 "symbol bar"); 140 141 KitObjSymbol sym_data = KIT_OBJ_SYMBOL_NONE; 142 KitObjSymbolDesc data_desc = { 143 .name = kit_sym_intern(c, KIT_SLICE_LIT("global_data")), 144 .bind = KIT_SB_GLOBAL, 145 .kind = KIT_SK_UNDEF, 146 .section = KIT_SECTION_NONE, 147 .value = 0, 148 .size = 0, 149 }; 150 CHECK(kit_obj_builder_symbol(ob, &data_desc, &sym_data) == KIT_OK, 151 "symbol global_data"); 152 153 KitObjRelocDesc rel_foo = { 154 .section = sec_text, 155 .offset = 1, 156 .kind = {.arch = target.arch, 157 .obj_fmt = target.obj, 158 .code = KIT_RELOC_X64_PLT32}, 159 .symbol = sym_foo, 160 .addend = -4, 161 }; 162 KitObjRelocDesc rel_data = { 163 .section = sec_text, 164 .offset = 8, 165 .kind = {.arch = target.arch, 166 .obj_fmt = target.obj, 167 .code = KIT_RELOC_PC32}, 168 .symbol = sym_data, 169 .addend = -4, 170 }; 171 KitObjRelocDesc rel_bar = { 172 .section = sec_text, 173 .offset = 13, 174 .kind = {.arch = target.arch, 175 .obj_fmt = target.obj, 176 .code = KIT_RELOC_X64_PLT32}, 177 .symbol = sym_bar, 178 .addend = -4, 179 }; 180 CHECK(kit_obj_builder_reloc(ob, &rel_foo) == KIT_OK, "reloc foo"); 181 CHECK(kit_obj_builder_reloc(ob, &rel_data) == KIT_OK, "reloc global_data"); 182 CHECK(kit_obj_builder_reloc(ob, &rel_bar) == KIT_OK, "reloc bar"); 183 184 CHECK(kit_obj_builder_finalize(ob) == KIT_OK, "finalize"); 185 return ob; 186 } 187 188 static void check_reloc_names(const KitSlice* bytes, const KitContext* ctx) { 189 KitObjFile* f = NULL; 190 KitObjRelocIter* it = NULL; 191 KitObjReloc r; 192 int saw_plt32 = 0; 193 int saw_pc32 = 0; 194 195 CHECK(kit_obj_open(ctx, KIT_SLICE_LIT("x64_disasm_annotations.o"), bytes, 196 &f) == KIT_OK && 197 f, 198 "kit_obj_open"); 199 if (!f) return; 200 CHECK(kit_obj_reliter_new(f, &it) == KIT_OK && it, "reliter_new"); 201 if (!it) { 202 kit_obj_free(f); 203 return; 204 } 205 while (kit_obj_reliter_next(it, &r) == KIT_ITER_ITEM) { 206 if (r.offset == 1 || r.offset == 13) { 207 CHECK(kit_slice_eq_cstr(r.kind_name, "R_X86_64_PLT32"), 208 "PLT32 kind name at offset %llu is %.*s", 209 (unsigned long long)r.offset, KIT_SLICE_ARG(r.kind_name)); 210 saw_plt32++; 211 } 212 if (r.offset == 8) { 213 CHECK(kit_slice_eq_cstr(r.kind_name, "R_X86_64_PC32"), 214 "PC32 kind name is %.*s", KIT_SLICE_ARG(r.kind_name)); 215 saw_pc32 = 1; 216 } 217 } 218 CHECK(saw_plt32 == 2, "saw %d PLT32 relocs", saw_plt32); 219 CHECK(saw_pc32, "missing PC32 reloc"); 220 kit_obj_reliter_free(it); 221 kit_obj_free(f); 222 } 223 224 static void check_disasm_annotations(const KitSlice* bytes, 225 const KitContext* ctx) { 226 KitWriter* w = NULL; 227 const uint8_t* out; 228 size_t len = 0; 229 char* text; 230 231 CHECK(kit_writer_mem(&g_heap, &w) == KIT_OK && w, "writer_mem"); 232 if (!w) return; 233 CHECK(kit_disasm_obj_bytes(ctx, bytes, w) == KIT_OK, "disasm_obj_bytes"); 234 out = kit_writer_mem_bytes(w, &len); 235 text = (char*)malloc(len + 1); 236 CHECK(text != NULL, "malloc disasm copy"); 237 if (text) { 238 memcpy(text, out, len); 239 text[len] = '\0'; 240 CHECK(strstr(text, "0000000000000000 <_start>:") != NULL, 241 "missing _start label\n%s", text); 242 CHECK(strstr(text, "0000000000000005 <load_data>:") != NULL, 243 "missing load_data label\n%s", text); 244 CHECK(strstr(text, "call") && strstr(text, "foo-4"), 245 "missing call reloc annotation\n%s", text); 246 CHECK(strstr(text, "mov") && strstr(text, "global_data-4"), 247 "missing RIP-relative reloc annotation\n%s", text); 248 CHECK(strstr(text, "jmp") && strstr(text, "bar-4"), 249 "missing jmp reloc annotation\n%s", text); 250 free(text); 251 } 252 kit_writer_close(w); 253 } 254 255 int main(void) { 256 KitTargetSpec target = x64_elf_target(); 257 KitContext ctx; 258 KitCompiler* cc = NULL; 259 KitObjBuilder* ob; 260 KitWriter* w = NULL; 261 const uint8_t* obj_data; 262 size_t obj_len = 0; 263 KitSlice bytes; 264 265 memset(&ctx, 0, sizeof ctx); 266 ctx.heap = &g_heap; 267 ctx.diag = &g_diag; 268 ctx.now = -1; 269 270 KitTargetOptions target_opts; 271 memset(&target_opts, 0, sizeof target_opts); 272 target_opts.spec = target; 273 KitTarget* kt = NULL; 274 if (kit_target_new(&ctx, &target_opts, &kt) != KIT_OK || !kt) { 275 fprintf(stderr, "FAIL: kit_target_new\n"); 276 return 1; 277 } 278 if (kit_compiler_new(kt, &ctx, &cc) != KIT_OK || !cc) { 279 fprintf(stderr, "FAIL: kit_compiler_new\n"); 280 kit_target_free(kt); 281 return 1; 282 } 283 284 ob = build_input(cc, target); 285 CHECK(kit_writer_mem(&g_heap, &w) == KIT_OK && w, "writer_mem object"); 286 CHECK(kit_obj_builder_emit(ob, w) == KIT_OK, "emit object"); 287 obj_data = kit_writer_mem_bytes(w, &obj_len); 288 bytes.data = obj_data; 289 bytes.len = obj_len; 290 291 check_reloc_names(&bytes, &ctx); 292 check_disasm_annotations(&bytes, &ctx); 293 294 kit_writer_close(w); 295 kit_obj_builder_free(ob); 296 kit_compiler_free(cc); 297 kit_target_free(kt); 298 299 if (g_failures) { 300 fprintf(stderr, "%d failure(s)\n", g_failures); 301 return 1; 302 } 303 fputs("x64_disasm_annotations: OK\n", stderr); 304 return 0; 305 }