kit

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

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 }