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 }