coff_weak_alias_test.c (5971B)
1 /* COFF WEAK_EXTERNAL alias capture in read_coff. 2 * 3 * mingw's x86_64 <setjmp.h> expands setjmp() to the intrinsic `_setjmp`, which 4 * libucrt.a provides as a COFF weak-external ALIAS to `__intrinsic_setjmp` (a 5 * DLL short-import). kit used to drop the aux TagIndex and lean on a single- 6 * underscore naming heuristic that can't derive `__intrinsic_setjmp` from 7 * `_setjmp`, so the cross-link failed with `undefined reference to '_setjmp'`. 8 * read_coff now records the alias target (when Characteristics is 9 * IMAGE_WEAK_EXTERN_SEARCH_ALIAS) so link_resolve can follow it by name. 10 * 11 * This pins the read-side decode: hand-build a minimal AMD64 COFF object whose 12 * symbol table carries the `_setjmp` -> `__intrinsic_setjmp` alias declarator, 13 * read it, and assert obj_get_weak_alias recovers the target. A negative case 14 * (a plain weak external with the SEARCH_LIBRARY policy kit's own emitter uses) 15 * confirms non-alias weak symbols record nothing. 16 * 17 * Exit 0 = pass; non-zero = fail. */ 18 19 #include <stdint.h> 20 #include <string.h> 21 22 #include <kit/core.h> 23 24 #include "core/core.h" 25 #include "core/pool.h" 26 #include "lib/kit_unit.h" 27 #include "obj/obj.h" 28 29 static KitUnit g_u; 30 #define EXPECT(cond, ...) CU_EXPECT(&g_u, cond, __VA_ARGS__) 31 32 #define IMAGE_FILE_MACHINE_AMD64 0x8664u 33 #define SC_EXTERNAL 2u 34 #define SC_WEAK_EXTERNAL 105u 35 #define SEARCH_LIBRARY 2u 36 #define SEARCH_ALIAS 3u 37 38 static void wr16(u8* p, u32 v) { 39 p[0] = (u8)v; 40 p[1] = (u8)(v >> 8); 41 } 42 static void wr32(u8* p, u32 v) { 43 p[0] = (u8)v; 44 p[1] = (u8)(v >> 8); 45 p[2] = (u8)(v >> 16); 46 p[3] = (u8)(v >> 24); 47 } 48 49 /* Build a 3-record symbol-table COFF object (no sections): 50 * idx 0: `__intrinsic_setjmp` (undef EXTERNAL, long name via strtab) 51 * idx 1: `weak_name` (WEAK_EXTERNAL, short name, 1 aux) 52 * idx 2: aux -> TagIndex 0 (`__intrinsic_setjmp`), `characteristics` 53 * `weak_name` must be <= 8 bytes (short name). Returns the byte length. */ 54 static size_t build_coff(u8* out, const char* weak_name, u32 characteristics) { 55 static const char kTarget[] = "__intrinsic_setjmp"; 56 const u32 nsyms = 3; 57 const u32 symtab_off = 20; 58 const u32 strtab_off = symtab_off + nsyms * 18u; 59 /* string table: 4-byte inclusive size + the target name + NUL */ 60 const u32 target_len = (u32)sizeof kTarget; /* includes NUL */ 61 const u32 strtab_size = 4u + target_len; 62 63 memset(out, 0, strtab_off + strtab_size); 64 65 /* ---- file header ---- */ 66 wr16(out + 0, IMAGE_FILE_MACHINE_AMD64); /* Machine */ 67 wr16(out + 2, 0); /* NumberOfSections */ 68 wr32(out + 4, 0); /* TimeDateStamp */ 69 wr32(out + 8, symtab_off); /* PointerToSymbolTable */ 70 wr32(out + 12, nsyms); /* NumberOfSymbols */ 71 wr16(out + 16, 0); /* SizeOfOptionalHeader */ 72 wr16(out + 18, 0); /* Characteristics */ 73 74 /* ---- symbol 0: __intrinsic_setjmp (long name) ---- */ 75 u8* s0 = out + symtab_off; 76 wr32(s0 + 0, 0); /* long-name marker */ 77 wr32(s0 + 4, 4); /* strtab offset (strings start at strtab+4) */ 78 wr32(s0 + 8, 0); 79 wr16(s0 + 12, 0); /* SectionNumber = UNDEFINED */ 80 wr16(s0 + 14, 0); /* Type */ 81 s0[16] = (u8)SC_EXTERNAL; 82 s0[17] = 0; /* naux */ 83 84 /* ---- symbol 1: weak_name (short name, weak external) ---- */ 85 u8* s1 = out + symtab_off + 18u; 86 size_t wn = strlen(weak_name); 87 memcpy(s1, weak_name, wn); /* zero-padded to 8 by the memset above */ 88 wr32(s1 + 8, 0); 89 wr16(s1 + 12, 0); /* SectionNumber = UNDEFINED */ 90 wr16(s1 + 14, 0); 91 s1[16] = (u8)SC_WEAK_EXTERNAL; 92 s1[17] = 1; /* one aux record */ 93 94 /* ---- symbol 2: weak-external aux ---- */ 95 u8* aux = out + symtab_off + 36u; 96 wr32(aux + 0, 0); /* TagIndex -> symbol 0 */ 97 wr32(aux + 4, characteristics); /* IMAGE_WEAK_EXTERN_SEARCH_* */ 98 99 /* ---- string table ---- */ 100 u8* st = out + strtab_off; 101 wr32(st + 0, strtab_size); 102 memcpy(st + 4, kTarget, target_len); 103 104 return strtab_off + strtab_size; 105 } 106 107 static Sym intern(Compiler* c, const char* s) { 108 return pool_intern_slice(c->global, 109 (Slice){.s = s, .len = (u32)strlen(s)}); 110 } 111 112 int main(void) { 113 KitCompiler* kc = NULL; 114 u8 buf[160]; 115 kit_unit_init(&g_u); 116 117 KitTargetSpec t = 118 kit_unit_target(KIT_ARCH_X86_64, KIT_OS_WINDOWS, KIT_OBJ_COFF); 119 if (kit_unit_compiler_new(&g_u, t, &kc) != KIT_OK || !kc) { 120 fprintf(stderr, "compiler_new failed\n"); 121 return 2; 122 } 123 Compiler* c = kc; /* KitCompiler IS the internal Compiler */ 124 125 /* Positive: a genuine alias declaration records its target. */ 126 { 127 size_t len = build_coff(buf, "_setjmp", SEARCH_ALIAS); 128 ObjBuilder* ob = read_coff(c, "weak_alias.o", buf, len); 129 EXPECT(ob != NULL, "read_coff returned NULL"); 130 if (ob) { 131 ObjSymId id = obj_symbol_find(ob, intern(c, "_setjmp")); 132 EXPECT(id != OBJ_SYM_NONE, "_setjmp symbol not found after read"); 133 Sym target = obj_get_weak_alias(ob, id); 134 EXPECT(target != 0, "alias target not recorded for _setjmp"); 135 if (target != 0) { 136 Slice s = pool_slice(c->global, target); 137 EXPECT(s.len == strlen("__intrinsic_setjmp") && 138 memcmp(s.s, "__intrinsic_setjmp", s.len) == 0, 139 "alias target = '%.*s', want '__intrinsic_setjmp'", (int)s.len, 140 s.s); 141 } 142 obj_free(ob); 143 } 144 } 145 146 /* Negative: SEARCH_LIBRARY (kit's own STB_WEAK emit shape) is not an alias. */ 147 { 148 size_t len = build_coff(buf, "wlib", SEARCH_LIBRARY); 149 ObjBuilder* ob = read_coff(c, "weak_lib.o", buf, len); 150 EXPECT(ob != NULL, "read_coff (lib) returned NULL"); 151 if (ob) { 152 ObjSymId id = obj_symbol_find(ob, intern(c, "wlib")); 153 EXPECT(id != OBJ_SYM_NONE, "wlib symbol not found after read"); 154 EXPECT(obj_get_weak_alias(ob, id) == 0, 155 "SEARCH_LIBRARY weak should not record an alias target"); 156 obj_free(ob); 157 } 158 } 159 160 kit_unit_summary(&g_u, "coff_weak_alias_test"); 161 return kit_unit_status(&g_u); 162 }