coff_archive_fixpoint_test.c (6273B)
1 /* COFF archive global fixed-point regression. 2 * 3 * Rust/MinGW links can create demand for an earlier library from a later 4 * library. ELF/Mach-O preserve positional archive semantics, but COFF linkers 5 * search the library set as a fixed point. This test builds: 6 * 7 * input.o -> undef late 8 * libearly.a -> defines early 9 * liblate.a -> defines late, undef early 10 * 11 * Positional scanning leaves `early` unpulled; COFF global fixed point pulls 12 * liblate.a first, then revisits libearly.a. */ 13 14 #include <kit/archive.h> 15 #include <kit/core.h> 16 #include <string.h> 17 18 #include "core/core.h" 19 #include "core/pool.h" 20 #include "lib/kit_unit.h" 21 #include "link/link.h" 22 #include "link/link_internal.h" 23 #include "obj/obj.h" 24 25 static KitUnit g_u; 26 #define EXPECT(cond, ...) CU_EXPECT(&g_u, cond, __VA_ARGS__) 27 28 static Sym intern(Compiler* c, const char* s) { 29 return pool_intern_slice(c->global, (Slice){.s = s, .len = (u32)strlen(s)}); 30 } 31 32 static ObjBuilder* make_obj(Compiler* c, Sym def, Sym undef) { 33 ObjBuilder* ob = obj_new(c); 34 ObjSecId data; 35 ObjSymId usym; 36 static const u8 byte = 1; 37 if (!ob) return NULL; 38 if (def != 0) { 39 data = obj_section(ob, intern(c, ".data"), SEC_DATA, SF_ALLOC | SF_WRITE, 40 1); 41 obj_write(ob, data, &byte, sizeof byte); 42 obj_symbol(ob, def, SB_GLOBAL, SK_OBJ, data, 0, sizeof byte); 43 } 44 if (undef != 0) { 45 usym = obj_symbol_ex(ob, undef, SB_GLOBAL, SV_DEFAULT, SK_UNDEF, 46 OBJ_SEC_NONE, 0, 0, 0); 47 obj_sym_mark_referenced(ob, usym); 48 } 49 obj_finalize(ob); 50 return ob; 51 } 52 53 static u8* copy_writer_bytes(KitWriter* w, size_t* len_out) { 54 size_t n = 0; 55 const u8* src = kit_writer_mem_bytes(w, &n); 56 u8* dst = (u8*)g_u.heap.alloc(&g_u.heap, n ? n : 1u, _Alignof(u8)); 57 if (dst && n) memcpy(dst, src, n); 58 if (len_out) *len_out = n; 59 return dst; 60 } 61 62 static u8* emit_member(Compiler* c, Sym def, Sym undef, size_t* len_out) { 63 ObjBuilder* ob = make_obj(c, def, undef); 64 KitWriter* w = NULL; 65 u8* bytes = NULL; 66 if (!ob) return NULL; 67 if (kit_writer_mem(&g_u.heap, &w) == KIT_OK && w) { 68 emit_coff(c, ob, w); 69 bytes = copy_writer_bytes(w, len_out); 70 kit_writer_close(w); 71 } 72 obj_free(ob); 73 return bytes; 74 } 75 76 static u8* make_archive_bytes(const char* member_name, const u8* member, 77 size_t member_len, size_t* len_out) { 78 KitArInput in; 79 KitArWriteOptions opts; 80 KitWriter* w = NULL; 81 u8* bytes = NULL; 82 memset(&in, 0, sizeof in); 83 memset(&opts, 0, sizeof opts); 84 in.name = kit_slice_cstr(member_name); 85 in.bytes.data = member; 86 in.bytes.len = member_len; 87 if (kit_writer_mem(&g_u.heap, &w) != KIT_OK || !w) return NULL; 88 if (kit_ar_write(w, &in, 1, &opts) == KIT_OK) 89 bytes = copy_writer_bytes(w, len_out); 90 kit_writer_close(w); 91 return bytes; 92 } 93 94 static int input_has_defined(Linker* l, Sym name) { 95 u32 i; 96 for (i = 0; i < LinkInputs_count(&l->inputs); ++i) { 97 LinkInput* in = LinkInputs_at(&l->inputs, i); 98 ObjSymIter* it; 99 ObjSymEntry e; 100 if (!in->obj) continue; 101 it = obj_symiter_new(in->obj); 102 while (obj_symiter_next(it, &e)) { 103 const ObjSym* s = e.sym; 104 if (s->name == name && s->kind != SK_UNDEF && 105 (s->bind == SB_GLOBAL || s->bind == SB_WEAK)) { 106 obj_symiter_free(it); 107 return 1; 108 } 109 } 110 obj_symiter_free(it); 111 } 112 return 0; 113 } 114 115 int main(void) { 116 KitCompiler* kc = NULL; 117 Compiler* c; 118 Linker* l = NULL; 119 ObjBuilder* seed = NULL; 120 Sym early, late; 121 u8 *early_member = NULL, *late_member = NULL; 122 u8 *early_archive = NULL, *late_archive = NULL; 123 size_t early_member_len = 0, late_member_len = 0; 124 size_t early_archive_len = 0, late_archive_len = 0; 125 int status; 126 127 kit_unit_init(&g_u); 128 { 129 KitTargetSpec t = 130 kit_unit_target(KIT_ARCH_X86_64, KIT_OS_WINDOWS, KIT_OBJ_COFF); 131 if (kit_unit_compiler_new(&g_u, t, &kc) != KIT_OK || !kc) { 132 fprintf(stderr, "compiler_new failed\n"); 133 return 2; 134 } 135 } 136 c = (Compiler*)kc; 137 early = intern(c, "early"); 138 late = intern(c, "late"); 139 140 seed = make_obj(c, 0, late); 141 early_member = emit_member(c, early, 0, &early_member_len); 142 late_member = emit_member(c, late, early, &late_member_len); 143 EXPECT(seed != NULL, "seed object allocation failed"); 144 EXPECT(early_member != NULL && early_member_len > 0, 145 "early member emit failed"); 146 EXPECT(late_member != NULL && late_member_len > 0, "late member emit failed"); 147 148 if (early_member && late_member) { 149 early_archive = make_archive_bytes("early.o", early_member, 150 early_member_len, &early_archive_len); 151 late_archive = 152 make_archive_bytes("late.o", late_member, late_member_len, 153 &late_archive_len); 154 } 155 EXPECT(early_archive != NULL && early_archive_len > 0, 156 "early archive build failed"); 157 EXPECT(late_archive != NULL && late_archive_len > 0, 158 "late archive build failed"); 159 160 l = link_new(c); 161 EXPECT(l != NULL, "link_new failed"); 162 if (l && seed && early_archive && late_archive) { 163 link_add_obj(l, seed); 164 link_add_archive_bytes(l, "libearly.a", early_archive, early_archive_len, 165 0, 0, 0); 166 link_add_archive_bytes(l, "liblate.a", late_archive, late_archive_len, 0, 167 0, 0); 168 link_ingest_archives(l); 169 EXPECT(input_has_defined(l, late), "late archive member was not pulled"); 170 EXPECT(input_has_defined(l, early), 171 "COFF global archive fixed point did not revisit libearly.a"); 172 } 173 174 if (l) link_free(l); 175 if (seed) obj_free(seed); 176 if (early_member) 177 g_u.heap.free(&g_u.heap, early_member, early_member_len ? early_member_len 178 : 1u); 179 if (late_member) 180 g_u.heap.free(&g_u.heap, late_member, late_member_len ? late_member_len 181 : 1u); 182 if (early_archive) 183 g_u.heap.free(&g_u.heap, early_archive, 184 early_archive_len ? early_archive_len : 1u); 185 if (late_archive) 186 g_u.heap.free(&g_u.heap, late_archive, 187 late_archive_len ? late_archive_len : 1u); 188 189 kit_unit_summary(&g_u, "coff_archive_fixpoint_test"); 190 status = kit_unit_status(&g_u); 191 return status; 192 }