kit

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

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 }