kit

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

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 }