kit

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

archive.c (9485B)


      1 #include <string.h>
      2 
      3 #include "core/arena.h"
      4 #include "core/core.h"
      5 #include "core/pool.h"
      6 #include "core/slice.h"
      7 #include "obj/format.h"
      8 #include "obj/obj.h"
      9 
     10 /* mingw import archives store import thunks as ordinary COFF members with
     11  * .idata$N sections. kit's PE emitter synthesizes .idata itself, so the
     12  * format hook below rewrites per-symbol members into DSO-shaped shims and
     13  * drops archive head/trailer members before the generic linker reads them. */
     14 
     15 typedef enum CoffArMemberClass {
     16   COFF_AR_KEEP = 0,
     17   COFF_AR_SHIM = 1,
     18   COFF_AR_SKIP = 2,
     19 } CoffArMemberClass;
     20 
     21 static const char kCoffImpPrefix_[] = "__imp_";
     22 static const u32 kCoffImpPrefixLen_ = (u32)(sizeof kCoffImpPrefix_ - 1u);
     23 static const char kCoffHeadPrefix_[] = "_head_";
     24 static const u32 kCoffHeadPrefixLen_ = (u32)(sizeof kCoffHeadPrefix_ - 1u);
     25 static const char kCoffInameSuffix_[] = "_iname";
     26 static const u32 kCoffInameSuffixLen_ = (u32)(sizeof kCoffInameSuffix_ - 1u);
     27 
     28 int coff_classify_obj_input(Compiler* c, ObjBuilder* ob, Sym* soname_out) {
     29   Sym dll = 0;
     30   (void)c;
     31   if (!obj_get_coff_import_dll(ob, &dll) || !dll) return 0;
     32   if (soname_out) *soname_out = dll;
     33   return 1;
     34 }
     35 
     36 Sym coff_archive_hint(Compiler* c, const char* path) {
     37   const char* base;
     38   const char* p;
     39   size_t n;
     40   size_t out_len;
     41   char* out;
     42   if (!c || !path || !*path) return 0;
     43   base = path;
     44   for (p = path; *p; ++p)
     45     if (*p == '/' || *p == '\\') base = p + 1;
     46   n = slice_from_cstr(base).len;
     47   if (n >= 6 && memcmp(base + n - 6, ".dll.a", 6) == 0)
     48     n -= 6;
     49   else if (n >= 2 && memcmp(base + n - 2, ".a", 2) == 0)
     50     n -= 2;
     51   else if (n >= 4 && memcmp(base + n - 4, ".lib", 4) == 0)
     52     n -= 4;
     53   if (n >= 3 && memcmp(base, "lib", 3) == 0) {
     54     base += 3;
     55     n -= 3;
     56   }
     57   if (n == 0) return 0;
     58   out_len = n + 4u;
     59   out = (char*)arena_array(c->scratch, char, out_len);
     60   memcpy(out, base, n);
     61   memcpy(out + n, ".dll", 4);
     62   return pool_intern_slice(c->global, (Slice){.s = out, .len = (u32)out_len});
     63 }
     64 
     65 static Sym derive_dll_name_from_archive_member(Compiler* c,
     66                                                const char* member_name,
     67                                                Sym fallback) {
     68   const char* base;
     69   const char* p;
     70   size_t n;
     71   if (!member_name || !*member_name) return fallback;
     72   base = member_name;
     73   for (p = member_name; *p; ++p)
     74     if (*p == '/' || *p == '\\') base = p + 1;
     75   n = slice_from_cstr(base).len;
     76   if (n >= 4 && memcmp(base + n - 4, ".dll", 4) == 0)
     77     return pool_intern_slice(c->global, (Slice){.s = base, .len = (u32)n});
     78   if (n >= 4 && memcmp(base + n - 4, ".DLL", 4) == 0)
     79     return pool_intern_slice(c->global, (Slice){.s = base, .len = (u32)n});
     80   return fallback;
     81 }
     82 
     83 static void coff_resolve_sym_name_(const u8* rec, const u8* strtab,
     84                                    u32 strtab_size, const char** name_out,
     85                                    u32* len_out) {
     86   u32 z = (u32)rec[0] | ((u32)rec[1] << 8) | ((u32)rec[2] << 16) |
     87           ((u32)rec[3] << 24);
     88   if (z == 0) {
     89     u32 off = (u32)rec[4] | ((u32)rec[5] << 8) | ((u32)rec[6] << 16) |
     90               ((u32)rec[7] << 24);
     91     const char* s;
     92     u32 max;
     93     u32 n = 0;
     94     if (!strtab || off >= strtab_size) {
     95       *name_out = "";
     96       *len_out = 0;
     97       return;
     98     }
     99     s = (const char*)(strtab + off);
    100     max = strtab_size - off;
    101     while (n < max && s[n] != '\0') ++n;
    102     *name_out = s;
    103     *len_out = n;
    104     return;
    105   }
    106   {
    107     u32 n = 0;
    108     while (n < 8 && rec[n] != '\0') ++n;
    109     *name_out = (const char*)rec;
    110     *len_out = n;
    111   }
    112 }
    113 
    114 #define COFF_SYM_REC_SIZE_ 18u
    115 #define COFF_FILE_HDR_SIZE_ 20u
    116 #define COFF_SYM_CLASS_EXTERNAL_ 2u
    117 
    118 static CoffArMemberClass classify_coff_archive_member_bytes(Compiler* c,
    119                                                             const u8* data,
    120                                                             size_t len,
    121                                                             Sym* out_name) {
    122   u32 ptr_to_symtab;
    123   u32 nsymbols;
    124   u16 nsections;
    125   const u8* sym_base;
    126   const u8* strtab;
    127   u32 strtab_size;
    128   int has_imp = 0;
    129   int has_idata = 0;
    130   int has_head_def = 0;
    131   int has_iname_def = 0;
    132   Sym imp_bare_name = 0;
    133   u32 i;
    134   *out_name = 0;
    135   if (len < COFF_FILE_HDR_SIZE_) return COFF_AR_KEEP;
    136   nsections = (u16)((u32)data[2] | ((u32)data[3] << 8));
    137   ptr_to_symtab = (u32)data[8] | ((u32)data[9] << 8) | ((u32)data[10] << 16) |
    138                   ((u32)data[11] << 24);
    139   nsymbols = (u32)data[12] | ((u32)data[13] << 8) | ((u32)data[14] << 16) |
    140              ((u32)data[15] << 24);
    141   if (nsymbols == 0 || ptr_to_symtab == 0) return COFF_AR_KEEP;
    142   if ((u64)COFF_FILE_HDR_SIZE_ + (u64)nsections * 40u <= (u64)len) {
    143     u32 si;
    144     for (si = 0; si < (u32)nsections; ++si) {
    145       const u8* sh = data + COFF_FILE_HDR_SIZE_ + (u64)si * 40u;
    146       if (memcmp(sh, ".idata$", 7) == 0) {
    147         has_idata = 1;
    148         break;
    149       }
    150     }
    151   }
    152   if ((u64)ptr_to_symtab + (u64)nsymbols * (u64)COFF_SYM_REC_SIZE_ > (u64)len)
    153     return COFF_AR_KEEP;
    154   sym_base = data + ptr_to_symtab;
    155   {
    156     u64 symtab_end =
    157         (u64)ptr_to_symtab + (u64)nsymbols * (u64)COFF_SYM_REC_SIZE_;
    158     if (symtab_end + 4u <= (u64)len) {
    159       u32 declared = (u32)data[symtab_end] | ((u32)data[symtab_end + 1] << 8) |
    160                      ((u32)data[symtab_end + 2] << 16) |
    161                      ((u32)data[symtab_end + 3] << 24);
    162       if (declared < 4u || symtab_end + (u64)declared > (u64)len) {
    163         strtab = NULL;
    164         strtab_size = 0;
    165       } else {
    166         strtab = data + symtab_end;
    167         strtab_size = declared;
    168       }
    169     } else {
    170       strtab = NULL;
    171       strtab_size = 0;
    172     }
    173   }
    174   i = 0;
    175   while (i < nsymbols) {
    176     const u8* p = sym_base + (u64)i * COFF_SYM_REC_SIZE_;
    177     u16 sec_num = (u16)((u32)p[12] | ((u32)p[13] << 8));
    178     u8 sclass = p[16];
    179     u8 naux = p[17];
    180     const char* nm = NULL;
    181     u32 nlen = 0;
    182     if (sclass == COFF_SYM_CLASS_EXTERNAL_ && sec_num != 0) {
    183       coff_resolve_sym_name_(p, strtab, strtab_size, &nm, &nlen);
    184       if (nlen > kCoffImpPrefixLen_ &&
    185           memcmp(nm, kCoffImpPrefix_, kCoffImpPrefixLen_) == 0) {
    186         has_imp = 1;
    187         if (imp_bare_name == 0) {
    188           const char* tail = nm + kCoffImpPrefixLen_;
    189           u32 tail_len = nlen - kCoffImpPrefixLen_;
    190           imp_bare_name =
    191               pool_intern_slice(c->global, (Slice){.s = tail, .len = tail_len});
    192         }
    193       } else if (nlen > kCoffHeadPrefixLen_ &&
    194                  memcmp(nm, kCoffHeadPrefix_, kCoffHeadPrefixLen_) == 0) {
    195         has_head_def = 1;
    196       } else if (nlen > kCoffInameSuffixLen_ &&
    197                  memcmp(nm + nlen - kCoffInameSuffixLen_, kCoffInameSuffix_,
    198                         kCoffInameSuffixLen_) == 0) {
    199         has_iname_def = 1;
    200       }
    201     }
    202     i += 1u + (u32)naux;
    203   }
    204   if (has_imp && has_idata) {
    205     *out_name = imp_bare_name;
    206     return COFF_AR_SHIM;
    207   }
    208   if (has_head_def || has_iname_def) return COFF_AR_SKIP;
    209   return COFF_AR_KEEP;
    210 }
    211 
    212 static ObjBuilder* build_coff_long_import_shim(Compiler* c, Sym bare_name,
    213                                                Sym dll_name) {
    214   ObjBuilder* ob;
    215   const char* bare;
    216   size_t bare_len = 0;
    217   u32 imp_len;
    218   char* imp_buf;
    219   Sym imp_sn;
    220   ObjSymId id;
    221   ObjSymId imp_id;
    222   if (bare_name == 0 || dll_name == 0) return NULL;
    223   {
    224     Slice bare_s = pool_slice(c->global, bare_name);
    225     bare = bare_s.s;
    226     bare_len = bare_s.len;
    227   }
    228   if (!bare || bare_len == 0) return NULL;
    229   ob = obj_new(c);
    230   if (!ob) return NULL;
    231   id = obj_symbol_ex(ob, bare_name, SB_GLOBAL, SV_DEFAULT, SK_FUNC,
    232                      OBJ_SEC_NONE, 0, 0, 0);
    233   obj_sym_mark_referenced(ob, id);
    234   imp_len = kCoffImpPrefixLen_ + (u32)bare_len;
    235   imp_buf = (char*)arena_array(c->scratch, char, imp_len);
    236   memcpy(imp_buf, kCoffImpPrefix_, kCoffImpPrefixLen_);
    237   memcpy(imp_buf + kCoffImpPrefixLen_, bare, bare_len);
    238   imp_sn = pool_intern_slice(c->global, (Slice){.s = imp_buf, .len = imp_len});
    239   imp_id = obj_symbol_ex(ob, imp_sn, SB_GLOBAL, SV_DEFAULT, SK_OBJ,
    240                          OBJ_SEC_NONE, 0, 0, 0);
    241   obj_sym_mark_referenced(ob, imp_id);
    242   obj_set_coff_import_dll(ob, dll_name);
    243   obj_finalize(ob);
    244   return ob;
    245 }
    246 
    247 static int coff_skip_long_import_shim_bare(Compiler* c, Sym bare_name) {
    248   const char* s;
    249   size_t n = 0;
    250   if (!bare_name) return 0;
    251   {
    252     Slice s_s = pool_slice(c->global, bare_name);
    253     s = s_s.s;
    254     n = s_s.len;
    255   }
    256   if (!s) return 0;
    257   return (n == 13 && memcmp(s, "__getmainargs", 13) == 0) ||
    258          (n == 13 && memcmp(s, "__p___initenv", 13) == 0);
    259 }
    260 
    261 ObjFormatArchiveAction coff_archive_member(Compiler* c,
    262                                            const ObjFormatArchiveMember* mem,
    263                                            ObjBuilder** out) {
    264   CoffArMemberClass cls;
    265   Sym bare = 0;
    266   if (out) *out = NULL;
    267   if (!c || !mem || mem->bin_fmt != KIT_BIN_COFF || mem->archive_hint == 0)
    268     return OBJ_FORMAT_ARCHIVE_KEEP;
    269   cls = classify_coff_archive_member_bytes(c, mem->data, mem->len, &bare);
    270   if (cls == COFF_AR_SHIM) {
    271     if (coff_skip_long_import_shim_bare(c, bare)) {
    272       return OBJ_FORMAT_ARCHIVE_SKIP;
    273     } else {
    274       Sym member_dll = derive_dll_name_from_archive_member(c, mem->member_name,
    275                                                            mem->archive_hint);
    276       if (out) *out = build_coff_long_import_shim(c, bare, member_dll);
    277       return OBJ_FORMAT_ARCHIVE_REPLACE;
    278     }
    279   }
    280   if (cls == COFF_AR_SKIP) return OBJ_FORMAT_ARCHIVE_SKIP;
    281   return OBJ_FORMAT_ARCHIVE_KEEP;
    282 }