kit

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

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 }