kit

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

kit-roundtrip-macho.c (6342B)


      1 /* kit-roundtrip-macho: read a Mach-O object via libkit's public object
      2  * reader, then re-emit via the public object builder API. Round-trip oracle
      3  * for the Mach-O writer.
      4  *
      5  * Usage: kit-roundtrip-macho [--require-atom-subordinate] <in.o> <out.o>
      6  *
      7  * Behavior: kit_obj_open detects and parses the input into a KitObjFile;
      8  * kit_obj_file_builder exposes the already-finalized builder; subordinate
      9  * symbols are checked against their owning atoms; and kit_obj_builder_emit
     10  * writes the canonical re-emit to out.o. Diagnostics go to stderr via the
     11  * libc heap + stderr diag sink. */
     12 
     13 #include <fcntl.h>
     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 #include <sys/stat.h>
     21 #include <unistd.h>
     22 
     23 #include "obj/obj.h"
     24 
     25 static void* heap_alloc(KitHeap* h, size_t n, size_t a) {
     26   (void)h;
     27   (void)a;
     28   return n ? malloc(n) : NULL;
     29 }
     30 static void* heap_realloc(KitHeap* h, void* p, size_t o, size_t n, size_t a) {
     31   (void)h;
     32   (void)o;
     33   (void)a;
     34   return realloc(p, n);
     35 }
     36 static void heap_free(KitHeap* h, void* p, size_t n) {
     37   (void)h;
     38   (void)n;
     39   free(p);
     40 }
     41 static KitHeap g_heap = {heap_alloc, heap_realloc, heap_free, NULL};
     42 
     43 static void diag_emit(KitDiagSink* s, KitDiagKind k, KitSrcLoc loc,
     44                       const char* fmt, va_list ap) {
     45   static const char* names[] = {"note", "warning", "error", "fatal"};
     46   (void)s;
     47   (void)loc;
     48   fprintf(stderr, "%s: ", names[k]);
     49   vfprintf(stderr, fmt, ap);
     50   fputc('\n', stderr);
     51 }
     52 static KitDiagSink g_diag = {diag_emit, NULL, 0, 0};
     53 
     54 static int read_file(const char* path, uint8_t** data_out, size_t* len_out) {
     55   int fd = open(path, O_RDONLY);
     56   if (fd < 0) return -1;
     57 
     58   struct stat sb;
     59   if (fstat(fd, &sb) < 0) {
     60     close(fd);
     61     return -1;
     62   }
     63   size_t n = (size_t)sb.st_size;
     64 
     65   uint8_t* buf = (uint8_t*)malloc(n ? n : 1);
     66   if (!buf) {
     67     close(fd);
     68     return -1;
     69   }
     70 
     71   size_t got = 0;
     72   while (got < n) {
     73     ssize_t k = read(fd, buf + got, n - got);
     74     if (k <= 0) {
     75       free(buf);
     76       close(fd);
     77       return -1;
     78     }
     79     got += (size_t)k;
     80   }
     81   close(fd);
     82 
     83   *data_out = buf;
     84   *len_out = n;
     85   return 0;
     86 }
     87 
     88 static int write_file(const char* path, const uint8_t* data, size_t len) {
     89   int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
     90   if (fd < 0) return -1;
     91   size_t off = 0;
     92   while (off < len) {
     93     ssize_t k = write(fd, data + off, len - off);
     94     if (k < 0) {
     95       close(fd);
     96       return -1;
     97     }
     98     off += (size_t)k;
     99   }
    100   close(fd);
    101   return 0;
    102 }
    103 
    104 static int verify_atom_subordinates(KitObjBuilder* ob, int require_one) {
    105   ObjSymIter* it = obj_symiter_new(ob);
    106   ObjSymEntry e;
    107   unsigned count = 0;
    108   int failed = 0;
    109   if (!it) {
    110     fprintf(stderr, "error: cannot inspect Mach-O symbols\n");
    111     return -1;
    112   }
    113   while (obj_symiter_next(it, &e)) {
    114     const ObjSym* sym = e.sym;
    115     const ObjAtom* atom;
    116     ObjAtomId aid;
    117     if (!sym || sym->removed || !sym->atom_subordinate) continue;
    118     ++count;
    119     if (sym->section_id == OBJ_SEC_NONE || sym->kind == SK_UNDEF ||
    120         sym->kind == SK_COMMON || sym->kind == SK_ABS) {
    121       fprintf(stderr,
    122               "error: atom-subordinate symbol %u is not a section "
    123               "definition\n",
    124               (unsigned)e.id);
    125       failed = 1;
    126       continue;
    127     }
    128     aid = obj_atom_find_symbol(ob, e.id);
    129     atom = obj_atom_get(ob, aid);
    130     if (!atom) {
    131       fprintf(stderr,
    132               "error: atom-subordinate symbol %u has no owning atom\n",
    133               (unsigned)e.id);
    134       failed = 1;
    135     }
    136     for (aid = 1; aid < obj_atom_count(ob); ++aid) {
    137       atom = obj_atom_get(ob, aid);
    138       if (atom && !atom->removed && atom->signature == e.id) {
    139         fprintf(stderr,
    140                 "error: atom-subordinate symbol %u was reconstructed as an "
    141                 "independent atom\n",
    142                 (unsigned)e.id);
    143         failed = 1;
    144         break;
    145       }
    146     }
    147   }
    148   obj_symiter_free(it);
    149   if (require_one && count == 0) {
    150     fprintf(stderr, "error: no atom-subordinate symbol survived roundtrip\n");
    151     failed = 1;
    152   }
    153   return failed ? -1 : 0;
    154 }
    155 
    156 int main(int argc, char** argv) {
    157   int require_subordinate = 0;
    158   int argi = 1;
    159   if (argc > 1 && strcmp(argv[1], "--require-atom-subordinate") == 0) {
    160     require_subordinate = 1;
    161     ++argi;
    162   }
    163   if (argc - argi != 2) {
    164     fprintf(stderr,
    165             "usage: kit-roundtrip-macho [--require-atom-subordinate] "
    166             "<in.o> <out.o>\n");
    167     return 2;
    168   }
    169   const char* in_path = argv[argi];
    170   const char* out_path = argv[argi + 1];
    171 
    172   uint8_t* in_data = NULL;
    173   size_t in_len = 0;
    174   if (read_file(in_path, &in_data, &in_len) != 0) {
    175     fprintf(stderr, "error: cannot read %s\n", in_path);
    176     return 1;
    177   }
    178 
    179   KitTargetSpec target;
    180   if (kit_detect_target(in_data, in_len, &target) != KIT_OK ||
    181       target.obj != KIT_OBJ_MACHO) {
    182     fprintf(stderr, "error: %s: not a recognized Mach-O object file\n",
    183             in_path);
    184     free(in_data);
    185     return 1;
    186   }
    187 
    188   KitContext ctx;
    189   memset(&ctx, 0, sizeof ctx);
    190   ctx.heap = &g_heap;
    191   ctx.diag = &g_diag;
    192   ctx.now = -1;
    193 
    194   KitObjFile* obj = NULL;
    195   KitSlice name = kit_slice_cstr(in_path);
    196   KitSlice input = {.data = in_data, .len = in_len};
    197   KitStatus st = kit_obj_open(&ctx, name, &input, &obj);
    198   if (st != KIT_OK || !obj) {
    199     free(in_data);
    200     return 2;
    201   }
    202 
    203   KitObjBuilder* ob = kit_obj_file_builder(obj);
    204   if (!ob) {
    205     fprintf(stderr, "error: kit_obj_file_builder failed\n");
    206     kit_obj_free(obj);
    207     free(in_data);
    208     return 1;
    209   }
    210   if (verify_atom_subordinates(ob, require_subordinate) != 0) {
    211     kit_obj_free(obj);
    212     free(in_data);
    213     return 1;
    214   }
    215 
    216   KitWriter* w = NULL;
    217   if (kit_writer_mem(&g_heap, &w) != KIT_OK || !w) {
    218     fprintf(stderr, "error: kit_writer_mem failed\n");
    219     kit_obj_free(obj);
    220     free(in_data);
    221     return 1;
    222   }
    223 
    224   st = kit_obj_builder_emit(ob, w);
    225   if (st != KIT_OK) {
    226     fprintf(stderr, "error: kit_obj_builder_emit failed\n");
    227     kit_writer_close(w);
    228     kit_obj_free(obj);
    229     free(in_data);
    230     return 1;
    231   }
    232 
    233   size_t out_len = 0;
    234   const uint8_t* out_data = kit_writer_mem_bytes(w, &out_len);
    235 
    236   int rc = write_file(out_path, out_data, out_len);
    237   if (rc != 0) fprintf(stderr, "error: cannot write %s\n", out_path);
    238 
    239   kit_writer_close(w);
    240   kit_obj_free(obj);
    241   free(in_data);
    242   return rc == 0 ? 0 : 1;
    243 }