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 }