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