link_exe_runner.c (8938B)
1 /* link_exe_runner — Path E harness driver. 2 * 3 * Usage: 4 * link_exe_runner [--gc-sections] [--entry NAME] [--linker-script <path>] 5 * [--symbols <path>] 6 * -o <out.exe> 7 * [--archive [--whole-archive] <lib.a>] <in.o> ... 8 * 9 * Reads inputs, calls kit_link_exe, writes the ELF executable. 10 * Exit 0 on success, 1 on link error, 2 on I/O/setup error. 11 * compiler_panic exits the process directly (via exit/abort) — the 12 * harness treats any non-zero exit as a link failure for link_fail cases. 13 * 14 * Compiled against libkit.a with -I$(ROOT)/include. */ 15 16 #include <fcntl.h> 17 #include <kit/core.h> 18 #include <kit/link.h> 19 #include <stdarg.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <sys/stat.h> 24 #include <unistd.h> 25 26 #include "lib/kit_test_target.h" 27 28 static void* h_alloc(KitHeap* h, size_t n, size_t a) { 29 (void)h; 30 (void)a; 31 return malloc(n); 32 } 33 static void* h_realloc(KitHeap* h, void* p, size_t o, size_t n, size_t a) { 34 (void)h; 35 (void)o; 36 (void)a; 37 return realloc(p, n); 38 } 39 static void h_free(KitHeap* h, void* p, size_t n) { 40 (void)h; 41 (void)n; 42 free(p); 43 } 44 static KitHeap g_heap = {h_alloc, h_realloc, h_free, NULL}; 45 46 static void diag_fn(KitDiagSink* s, KitDiagKind k, KitSrcLoc loc, 47 const char* fmt, va_list ap) { 48 static const char* names[] = {"note", "warning", "error", "fatal"}; 49 (void)s; 50 (void)loc; 51 fprintf(stderr, "%s: ", names[k]); 52 vfprintf(stderr, fmt, ap); 53 fputc('\n', stderr); 54 } 55 static KitDiagSink g_diag = {diag_fn, NULL, 0, 0}; 56 57 static void free_compiler_target(KitCompiler* compiler, KitTarget* target) { 58 kit_compiler_free(compiler); 59 kit_target_free(target); 60 } 61 62 static int slurp(const char* path, uint8_t** out, size_t* len) { 63 int fd = open(path, O_RDONLY); 64 if (fd < 0) return -1; 65 struct stat sb; 66 if (fstat(fd, &sb) < 0) { 67 close(fd); 68 return -1; 69 } 70 size_t n = (size_t)sb.st_size; 71 uint8_t* buf = malloc(n ? n : 1); 72 if (!buf) { 73 close(fd); 74 return -1; 75 } 76 size_t got = 0; 77 while (got < n) { 78 ssize_t k = read(fd, buf + got, n - got); 79 if (k <= 0) { 80 free(buf); 81 close(fd); 82 return -1; 83 } 84 got += (size_t)k; 85 } 86 close(fd); 87 *out = buf; 88 *len = n; 89 return 0; 90 } 91 92 static int write_exe(const char* path, const uint8_t* data, size_t len) { 93 /* Unlink before create so the new file has a fresh inode. macOS 94 * AMFI caches "this inode failed to load" decisions and refuses to 95 * re-execute the same inode even after we overwrite its contents 96 * (O_TRUNC preserves the inode). A fresh inode sidesteps the cache. */ 97 (void)unlink(path); 98 int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0755); 99 if (fd < 0) return -1; 100 size_t w = 0; 101 while (w < len) { 102 ssize_t k = write(fd, data + w, len - w); 103 if (k <= 0) { 104 close(fd); 105 return -1; 106 } 107 w += (size_t)k; 108 } 109 close(fd); 110 return 0; 111 } 112 113 static int write_data(const char* path, const uint8_t* data, size_t len) { 114 int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); 115 size_t w = 0; 116 if (fd < 0) return -1; 117 while (w < len) { 118 ssize_t k = write(fd, data + w, len - w); 119 if (k <= 0) { 120 close(fd); 121 return -1; 122 } 123 w += (size_t)k; 124 } 125 close(fd); 126 return 0; 127 } 128 129 int main(int argc, char** argv) { 130 const char* out_path = NULL; 131 const char* entry_name = "_start"; 132 const char* script_path = NULL; 133 const char* symbols_path = NULL; 134 int gc_sections = 0; 135 int next_archive = 0; 136 int next_whole = 0; 137 138 KitSlice objs[64]; 139 const char* obj_names[64]; 140 KitLinkArchiveInput archives[16]; 141 KitSlice dsos[16]; 142 const char* dso_names[16]; 143 uint32_t nobj = 0, narc = 0, ndso = 0; 144 uint8_t* bufs[96]; 145 int nbufs = 0; 146 int next_dso = 0; 147 148 for (int i = 1; i < argc; i++) { 149 if (!strcmp(argv[i], "--gc-sections")) { 150 gc_sections = 1; 151 } else if (!strcmp(argv[i], "--archive")) { 152 next_archive = 1; 153 } else if (!strcmp(argv[i], "--whole-archive")) { 154 next_whole = 1; 155 next_archive = 1; 156 } else if (!strcmp(argv[i], "--dso")) { 157 next_dso = 1; 158 } else if (!strcmp(argv[i], "--entry") && i + 1 < argc) { 159 entry_name = argv[++i]; 160 } else if (!strcmp(argv[i], "--linker-script") && i + 1 < argc) { 161 script_path = argv[++i]; 162 } else if (!strcmp(argv[i], "--symbols") && i + 1 < argc) { 163 symbols_path = argv[++i]; 164 } else if (!strcmp(argv[i], "-o") && i + 1 < argc) { 165 out_path = argv[++i]; 166 } else { 167 uint8_t* data; 168 size_t len; 169 if (slurp(argv[i], &data, &len)) { 170 fprintf(stderr, "link-exe-runner: cannot read %s\n", argv[i]); 171 return 2; 172 } 173 bufs[nbufs++] = data; 174 if (next_dso) { 175 dso_names[ndso] = argv[i]; 176 KitSlice* d = &dsos[ndso++]; 177 memset(d, 0, sizeof(*d)); 178 d->data = data; 179 d->len = len; 180 next_dso = 0; 181 } else if (next_archive) { 182 KitLinkArchiveInput* a = &archives[narc++]; 183 memset(a, 0, sizeof(*a)); 184 a->name = kit_slice_cstr(argv[i]); 185 a->bytes.data = data; 186 a->bytes.len = len; 187 a->whole_archive = (uint8_t)next_whole; 188 next_archive = 0; 189 next_whole = 0; 190 } else { 191 obj_names[nobj] = argv[i]; 192 KitSlice* o = &objs[nobj++]; 193 memset(o, 0, sizeof(*o)); 194 o->data = data; 195 o->len = len; 196 } 197 } 198 } 199 if (!out_path) { 200 fprintf(stderr, "link-exe-runner: missing -o\n"); 201 return 2; 202 } 203 204 KitTargetSpec target; 205 if (kit_test_target_init(&target) != 0) { 206 fprintf(stderr, "link_exe_runner: kit_test_target_init failed\n"); 207 return 2; 208 } 209 210 KitContext ctx; 211 memset(&ctx, 0, sizeof(ctx)); 212 ctx.heap = &g_heap; 213 ctx.diag = &g_diag; 214 ctx.now = -1; 215 216 KitTargetOptions target_opts; 217 memset(&target_opts, 0, sizeof target_opts); 218 target_opts.spec = target; 219 KitTarget* kt = NULL; 220 if (kit_target_new(&ctx, &target_opts, &kt) != KIT_OK || !kt) { 221 fprintf(stderr, "link-exe-runner: target_new failed\n"); 222 return 2; 223 } 224 KitCompiler* c = NULL; 225 if (kit_compiler_new(kt, &ctx, &c) != KIT_OK || !c) { 226 fprintf(stderr, "link-exe-runner: compiler_new failed\n"); 227 kit_target_free(kt); 228 return 2; 229 } 230 231 KitLinkSessionOptions opts; 232 KitLinkSession* link = NULL; 233 KitLinkScript* script = NULL; 234 memset(&opts, 0, sizeof(opts)); 235 opts.output_kind = KIT_LINK_OUTPUT_EXE; 236 opts.entry = kit_slice_cstr(entry_name); 237 opts.gc_sections = gc_sections; 238 239 if (script_path) { 240 uint8_t* sbytes; 241 size_t slen; 242 if (slurp(script_path, &sbytes, &slen)) { 243 fprintf(stderr, "link-exe-runner: cannot read %s\n", script_path); 244 free_compiler_target(c, kt); 245 return 2; 246 } 247 KitSlice script_text = {.s = (const char*)sbytes, .len = slen}; 248 KitStatus prc = kit_link_script_parse(&ctx, script_text, &script); 249 free(sbytes); 250 if (prc != KIT_OK) { 251 fprintf(stderr, "link-exe-runner: linker script parse failed: %s\n", 252 script_path); 253 free_compiler_target(c, kt); 254 return 1; 255 } 256 opts.linker_script = script; 257 } 258 259 KitWriter* w = NULL; 260 if (kit_writer_mem(&g_heap, &w) != KIT_OK || !w) { 261 free_compiler_target(c, kt); 262 return 2; 263 } 264 265 KitStatus rc = kit_link_session_new(c, &opts, &link); 266 for (uint32_t i = 0; rc == KIT_OK && i < nobj; ++i) 267 rc = kit_link_session_add_obj_bytes(link, kit_slice_cstr(obj_names[i]), 268 &objs[i]); 269 for (uint32_t i = 0; rc == KIT_OK && i < narc; ++i) 270 rc = kit_link_session_add_archive_bytes(link, &archives[i]); 271 for (uint32_t i = 0; rc == KIT_OK && i < ndso; ++i) 272 rc = kit_link_session_add_dso_bytes(link, kit_slice_cstr(dso_names[i]), 273 &dsos[i]); 274 if (rc == KIT_OK) rc = kit_link_session_emit(link, w); 275 276 if (rc != KIT_OK) { 277 kit_link_session_free(link); 278 if (script) kit_link_script_free(&ctx, script); 279 for (int i = 0; i < nbufs; i++) free(bufs[i]); 280 kit_writer_close(w); 281 free_compiler_target(c, kt); 282 return 1; 283 } 284 for (int i = 0; i < nbufs; i++) free(bufs[i]); 285 286 size_t out_len; 287 const uint8_t* out_bytes = kit_writer_mem_bytes(w, &out_len); 288 int wrc = write_exe(out_path, out_bytes, out_len); 289 if (wrc == 0 && symbols_path) { 290 KitWriter* sw = NULL; 291 if (kit_writer_mem(&g_heap, &sw) != KIT_OK || !sw || 292 kit_link_session_write_symbols(link, KIT_LINK_SYMBOLS_NM, sw) != 293 KIT_OK || 294 kit_writer_status(sw) != KIT_OK) { 295 wrc = -1; 296 } else { 297 size_t symbols_len = 0; 298 const uint8_t* symbols = kit_writer_mem_bytes(sw, &symbols_len); 299 wrc = write_data(symbols_path, symbols, symbols_len); 300 } 301 if (sw) kit_writer_close(sw); 302 } 303 kit_link_session_free(link); 304 if (script) kit_link_script_free(&ctx, script); 305 kit_writer_close(w); 306 free_compiler_target(c, kt); 307 if (wrc) { 308 fprintf(stderr, "link-exe-runner: output write failed\n"); 309 return 2; 310 } 311 return 0; 312 }