From e1e8ba826de45a3726b1260d7013f2704eda0046 Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Thu, 14 May 2026 16:43:22 -0700 Subject: [PATCH] fix duplicate defer import link --- src/linker/hash_table.c | 13 ++ src/linker/hash_table.h | 1 + src/linker/lnk.c | 112 ++++++++---- src/linker/lnk.h | 4 +- src/torture/torture_radlink.c | 322 +++++++++++++++++++++++++++++++++- 5 files changed, 410 insertions(+), 42 deletions(-) diff --git a/src/linker/hash_table.c b/src/linker/hash_table.c index 6e33adee..a47e8929 100644 --- a/src/linker/hash_table.c +++ b/src/linker/hash_table.c @@ -757,6 +757,19 @@ hash_map_push_path_raw(Arena *arena, HashMap *hm, String8 path, void *value) //////////////////////////////// +internal void * +hash_map_search_stringf_raw(HashMap *hm, char *fmt, ...) +{ + Temp scratch = scratch_begin(0,0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + void *raw = hash_map_search_string_raw(hm, string); + va_end(args); + scratch_end(scratch); + return raw; +} + internal void * hash_map_search_string_raw(HashMap *hm, String8 key) { diff --git a/src/linker/hash_table.h b/src/linker/hash_table.h index 1750a8c3..39e46a1d 100644 --- a/src/linker/hash_table.h +++ b/src/linker/hash_table.h @@ -175,6 +175,7 @@ internal HashMapNode * hash_map_push_path_u64 (Arena *arena, HashMap *hm, St internal HashMapNode * hash_map_push_path_string (Arena *arena, HashMap *hm, String8 path, String8 value); internal HashMapNode * hash_map_push_path_raw (Arena *arena, HashMap *hm, String8 path, void *value); +internal void * hash_map_search_stringf_raw(HashMap *hm, char *fmt, ...); internal void * hash_map_search_string_raw(HashMap *hm, String8 key); internal U32 * hash_map_search_string_u32(HashMap *hm, String8 key); internal U64 * hash_map_search_string_u64(HashMap *hm, String8 key); diff --git a/src/linker/lnk.c b/src/linker/lnk.c index 943e4605..d4f1f46d 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -1487,7 +1487,14 @@ lnk_load_inputs(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer } internal void -lnk_queue_lib_member(Arena *arena, LNK_LibMemberRefList *queued_members, LNK_Symbol *link_symbol, LNK_Lib *lib, LNK_LibMemberInfo *member_infos, U32 member_idx) +lnk_queue_lib_member(Arena *arena, + HashMap *imports_hm, + HashMap lib_member_info_hm, + LNK_LibMemberRefList *queued_members, + LNK_Symbol *link_symbol, + LNK_Lib *lib, + LNK_LibMemberInfo *member_infos, + U32 member_idx) { // associate link symbol to lib member for (LNK_Symbol *leader = link_symbol;;) { @@ -1511,22 +1518,42 @@ lnk_queue_lib_member(Arena *arena, LNK_LibMemberRefList *queued_members, LNK_Sym } } - B32 was_linked; + LNK_LibMemberRef *is_thunk_import; + LNK_LibMemberRef *is_addr_import; if (str8_starts_with(link_symbol->name, str8_lit("__imp_"))) { - U8 member_flags = ins_atomic_u8_or(&member_infos[member_idx].flags, LNK_LibMemberFlag_LinkedImp); - was_linked = !(member_flags & LNK_LibMemberFlag_LinkedImp); + is_thunk_import = hash_map_search_string_raw(imports_hm, str8_skip(link_symbol->name, 6)); + is_addr_import = hash_map_search_string_raw(imports_hm, link_symbol->name); } else { - U8 flag = LNK_LibMemberFlag_LinkedRegular; - U8 member_flags = ins_atomic_u8_or(&member_infos[member_idx].flags, LNK_LibMemberFlag_LinkedRegular); - was_linked = !(member_flags & LNK_LibMemberFlag_LinkedRegular); + is_thunk_import = hash_map_search_string_raw(imports_hm, link_symbol->name); + is_addr_import = hash_map_search_stringf_raw(imports_hm, "__imp_%S", link_symbol->name); } - if (was_linked) { - LNK_LibMemberRef *member_ref = push_array(arena, LNK_LibMemberRef, 1); - member_ref->lib = lib; - member_ref->member_idx = member_idx; - member_ref->link_symbol = link_symbol; - lnk_lib_member_ref_list_push_node(queued_members, member_ref); + LNK_LibMemberRef *is_queued_import = is_thunk_import ? is_thunk_import : + is_addr_import ? is_addr_import : 0; + + if (is_queued_import) { + // do not queue second import member link -- flag member and continue + U8 flag = str8_starts_with(link_symbol->name, str8_lit("__imp_")) ? LNK_LibMemberFlag_LinkedImp : LNK_LibMemberFlag_LinkedRegular; + LNK_LibMemberInfo *import_member_infos = hash_map_search_raw_raw(&lib_member_info_hm, is_queued_import->lib); + ins_atomic_u8_or(&import_member_infos[member_idx].flags, flag); + } else { + B32 do_queue; + if (str8_starts_with(link_symbol->name, str8_lit("__imp_"))) { + U8 member_flags = ins_atomic_u8_or(&member_infos[member_idx].flags, LNK_LibMemberFlag_LinkedImp); + do_queue = !(member_flags & LNK_LibMemberFlag_LinkedImp); + } else { + U8 flag = LNK_LibMemberFlag_LinkedRegular; + U8 member_flags = ins_atomic_u8_or(&member_infos[member_idx].flags, LNK_LibMemberFlag_LinkedRegular); + do_queue = !(member_flags & LNK_LibMemberFlag_LinkedRegular); + } + + if (do_queue) { + LNK_LibMemberRef *member_ref = push_array(arena, LNK_LibMemberRef, 1); + member_ref->lib = lib; + member_ref->member_idx = member_idx; + member_ref->link_symbol = link_symbol; + lnk_lib_member_ref_list_push_node(queued_members, member_ref); + } } } @@ -1550,14 +1577,14 @@ THREAD_POOL_TASK_FUNC(lnk_search_lib_task) if (symbol_interp == COFF_SymbolValueInterp_Undefined) { U32 member_idx; if (lnk_search_lib(lib, symbol->name, &member_idx)) { - lnk_queue_lib_member(arena, member_ref_list, symbol, lib, lib_member_infos, member_idx); + lnk_queue_lib_member(arena, task->imports_hm, task->link->lib_member_infos_hm, member_ref_list, symbol, lib, lib_member_infos, member_idx); } } else if (symbol_interp == COFF_SymbolValueInterp_Weak) { COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(symbol_parsed, symbol_ref.obj->header.is_big_obj); if (weak_ext->characteristics == COFF_WeakExt_SearchLibrary) { U32 member_idx; if (lnk_search_lib(lib, symbol->name, &member_idx)) { - lnk_queue_lib_member(arena, member_ref_list, symbol, lib, lib_member_infos, member_idx); + lnk_queue_lib_member(arena, task->imports_hm, task->link->lib_member_infos_hm, member_ref_list, symbol, lib, lib_member_infos, member_idx); } } else if (weak_ext->characteristics == COFF_WeakExt_AntiDependency) { if (search_anti_deps) { @@ -1568,7 +1595,7 @@ THREAD_POOL_TASK_FUNC(lnk_search_lib_task) if (dep_interp == COFF_SymbolValueInterp_Weak) { U32 member_idx; if (lnk_search_lib(lib, symbol_parsed.name, &member_idx)) { - lnk_queue_lib_member(arena, member_ref_list, symbol, lib, lib_member_infos, member_idx); + lnk_queue_lib_member(arena, task->imports_hm, task->link->lib_member_infos_hm, member_ref_list, symbol, lib, lib_member_infos, member_idx); } } } @@ -1624,6 +1651,8 @@ lnk_link_inputs(TP_Context *tp, ProfBeginFunction(); Temp scratch = scratch_begin(arena->v, arena->count); + HashMap imports_hm = {0}; + LNK_LibMemberRefList *member_ref_lists = push_array(scratch.arena, LNK_LibMemberRefList, tp->worker_count); B32 search_anti_deps = 0; for (U64 resolved_members_count = 0; ; resolved_members_count = 0) { @@ -1683,10 +1712,10 @@ lnk_link_inputs(TP_Context *tp, } } - LNK_LibMemberInfo *lib_member_infos = hash_table_search_raw_raw(link->lib_member_infos_ht, lib); + LNK_LibMemberInfo *lib_member_infos = hash_map_search_raw_raw(&link->lib_member_infos_hm, lib); if (lib_member_infos == 0) { lib_member_infos = push_array(link->arena, LNK_LibMemberInfo, lib->member_count); - hash_table_push_raw_raw(link->arena, link->lib_member_infos_ht, lib, lib_member_infos); + hash_map_push_raw_raw(link->arena, &link->lib_member_infos_hm, lib, lib_member_infos); } B32 link_whole_archive = config->whole_archive_all; @@ -1708,12 +1737,20 @@ lnk_link_inputs(TP_Context *tp, } LNK_LibMemberRef *member_refs = push_array(scratch.arena, LNK_LibMemberRef, lib->member_count); for EachIndex(member_idx, lib->member_count) { - lnk_queue_lib_member(arena->v[0], &member_ref_lists[0], null_symbol, lib, lib_member_infos, member_idx); + lnk_queue_lib_member(arena->v[0], &imports_hm, link->lib_member_infos_hm, &member_ref_lists[0], null_symbol, lib, lib_member_infos, member_idx); } } else { // search symbols in lib MemoryZeroTyped(member_ref_lists, tp->worker_count); - LNK_SearchLibTask search_task = { .search_anti_deps = search_anti_deps, .lib = lib, .symtab = symtab, .lib_member_infos = lib_member_infos, .member_ref_lists = member_ref_lists }; + LNK_SearchLibTask search_task = { + .search_anti_deps = search_anti_deps, + .link = link, + .imports_hm = &imports_hm, + .lib = lib, + .symtab = symtab, + .lib_member_infos = lib_member_infos, + .member_ref_lists = member_ref_lists + }; tp_for_parallel(tp, arena, tp->worker_count, lnk_search_lib_task, &search_task); } @@ -1765,6 +1802,25 @@ lnk_link_inputs(TP_Context *tp, switch (member_type) { case COFF_DataType_Import: { + { + LNK_LibMemberRef *is_thunk_import; + LNK_LibMemberRef *is_addr_import; + if (str8_starts_with(member_ref->link_symbol->name, str8_lit("__imp_"))) { + is_thunk_import = hash_map_search_string_raw(&imports_hm, str8_skip(member_ref->link_symbol->name, 6)); + is_addr_import = hash_map_search_string_raw(&imports_hm, member_ref->link_symbol->name); + } else { + is_thunk_import = hash_map_search_string_raw(&imports_hm, member_ref->link_symbol->name); + is_addr_import = hash_map_search_stringf_raw(&imports_hm, "__imp_%S", member_ref->link_symbol->name); + } + if (is_thunk_import != 0 || is_addr_import != 0) { + lnk_invalid_path("duplicate import member queue detected"); + break; + } + } + + // store lib member ref to import + hash_map_push_string_raw(scratch.arena, &imports_hm, member_ref->link_symbol->name, member_ref); + // find import stub LNK_Symbol *import_stub = lnk_symbol_table_search(symtab, str8_lit(LNK_IMPORT_STUB)); @@ -1775,10 +1831,8 @@ lnk_link_inputs(TP_Context *tp, member_ref->link_symbol->refs = import_stub->refs; // push import member for import obj generation - if (!(lib_member_infos[member_ref->member_idx].flags & LNK_LibMemberFlag_WasGenQueued)) { - lib_member_infos[member_ref->member_idx].flags |= LNK_LibMemberFlag_WasGenQueued; - lnk_lib_member_ref_list_push_node(&link->imports, member_ref); - } + lnk_lib_member_ref_list_push_node(&link->imports, member_ref); + lib_member_infos[member_ref->member_idx].flags |= LNK_LibMemberFlag_WasGenQueued; } break; case COFF_DataType_BigObj: case COFF_DataType_Obj: { @@ -1867,7 +1921,6 @@ lnk_link_image(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer link->last_default_lib = &config->input_default_lib_list.first; link->last_obj_lib = &config->input_obj_lib_list.first; link->last_cmd_lib = &config->input_list[LNK_Input_Lib].first; - link->lib_member_infos_ht = hash_table_init(link->arena, Max(config->input_list[LNK_Input_Lib].node_count * 2, 1024)); link->try_to_resolve_entry_point = 1; // input :null_obj @@ -1909,7 +1962,7 @@ lnk_link_image(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer for EachNode(member_ref, LNK_LibMemberRef, link->imports.first) { LNK_Lib *lib = member_ref->lib; U64 member_idx = member_ref->member_idx; - LNK_LibMemberInfo *member_infos = hash_table_search_raw_raw(link->lib_member_infos_ht, lib); + LNK_LibMemberInfo *member_infos = hash_map_search_raw_raw(&link->lib_member_infos_hm, lib); LNK_Symbol *link_symbol = member_infos[member_idx].link; U32 member_offset = memory_read32(lib->member_offsets + member_idx); @@ -1944,13 +1997,8 @@ lnk_link_image(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer hash_table_push_path_raw(scratch.arena, imports_ht, import_header.dll_name, import_symbols); } - B32 make_jump_thunk = 1; - if (str8_starts_with(link_symbol->name, str8_lit("__imp_"))) { - LNK_Symbol *thunk_symbol = lnk_symbol_table_search(symtab, str8_skip(link_symbol->name, str8_lit("__imp_").size)); - make_jump_thunk = thunk_symbol != 0; - } - // push make import info + B32 make_jump_thunk = !!(member_infos[member_idx].flags & LNK_LibMemberFlag_LinkedRegular); pe_make_import_header_list_push(scratch.arena, import_symbols, (PE_MakeImport){ .header = member_info.data, .make_jump_thunk = make_jump_thunk }); } AssertAlways(delayed_dll_names.node_count == delayed_imports_ht->count); diff --git a/src/linker/lnk.h b/src/linker/lnk.h index e5a24863..b0685482 100644 --- a/src/linker/lnk.h +++ b/src/linker/lnk.h @@ -99,7 +99,7 @@ typedef struct LNK_Link String8Node **last_cmd_lib; String8Node **last_default_lib; String8Node **last_obj_lib; - HashTable *lib_member_infos_ht; + HashMap lib_member_infos_hm; LNK_LibMemberRefList imports; B32 try_to_resolve_entry_point; B32 asan_libs_resolved; @@ -204,6 +204,8 @@ typedef struct LNK_BaseRelocPageArray typedef struct { B32 search_anti_deps; + LNK_Link *link; + HashMap *imports_hm; LNK_SymbolTable *symtab; LNK_Symbol *import_stub; LNK_Lib *lib; diff --git a/src/torture/torture_radlink.c b/src/torture/torture_radlink.c index b937fac5..1c0bbfab 100644 --- a/src/torture/torture_radlink.c +++ b/src/torture/torture_radlink.c @@ -1,15 +1,17 @@ // Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) -internal T_Linker -t_id_linker(void) -{ - String8 name = str8_chop_last_dot(str8_skip_last_slash(g_linker)); - if (str8_match(name, str8_lit("radlink"), StringMatchFlag_CaseInsensitive)) { return T_Linker_RAD; } - if (str8_match(name, str8_lit("link"), StringMatchFlag_CaseInsensitive)) { return T_Linker_MSVC; } - if (str8_match(name, str8_lit("lld-link"), StringMatchFlag_CaseInsensitive)) { return T_Linker_LLVM; } - return T_Linker_Null; -} +// TODO: +// [x] defer_duplicate_imp_link +// [ ] opt_ref_comdat_undef_section +// [ ] opt_ref_weak_alias_comdat +// [ ] reloc_apply_off_out_of_bounds +// [ ] lib_member_reloc_apply_off_out_of_bounds +// [ ] fold_two_funcs +// [ ] same_but_different +// [ ] fold_diamond +// [ ] cyclic_icf +// [ ] fold_with_largest_align //////////////////////////////// // Def -> COFF @@ -4553,6 +4555,118 @@ TEST(defer_impl_link_to_second_search_pass) T_Ok(g_last_exit_code == 0); } +TEST(defer_duplicate_imp_link) +{ + T_COFF_DefLib bar_lib_any = { + .members = (T_COFF_DefLibMember[]){ + { + .type = T_COFF_DefLibMember_DllImportStatic, + .dll_import = { .name = "bar.dll" } + }, + { + .type = T_COFF_DefLibMember_Import, + .import = { "bar.dll", "bar", COFF_ImportBy_Name, COFF_ImportHeader_Code, .hit_or_ordinal = 0 } + }, + { + .type = T_COFF_DefLibMember_Obj, + .obj = { + .machine = T_COFF_DefSetMachine(X64), + .sections = (T_COFF_DefSection[]){ + { + "text", ".text", str8_lit_comp("\xff\x25\x00\x00\x00\x00"), .flags = "rx:code" + }, + {0} + }, + .symbols = (T_COFF_DefSymbol[]){ + T_COFF_DefSymbol_Undef("__imp_bar"), + T_COFF_DefSymbol_ExternFunc("qwe", "text", 0), + {0}, + } + } + }, + {0} + } + }; + + T_COFF_DefLib foo_lib_any = { + .members = (T_COFF_DefLibMember[]){ + { + .type = T_COFF_DefLibMember_DllImportStatic, + .dll_import = { .name = "foo.dll" } + }, + { + .type = T_COFF_DefLibMember_Import, + .import = { "foo.dll", "bar", COFF_ImportBy_Name, COFF_ImportHeader_Code, .hit_or_ordinal = 0 } + }, + { + .type = T_COFF_DefLibMember_Obj, + .obj = { + .machine = T_COFF_DefSetMachine(X64), + .sections = (T_COFF_DefSection[]){ + { + "text", ".text", + str8_lit_comp("\xff\x25\x00\x00\x00\x00"), + .flags = "rx:code", + .relocs = (T_COFF_DefReloc[]){ + T_COFF_DefReloc(X64_Rel32, 2, "bar"), + {0} + } + }, + {0} + }, + .symbols = (T_COFF_DefSymbol[]){ + T_COFF_DefSymbol_Undef("bar"), + T_COFF_DefSymbol_Undef("qwe"), + T_COFF_DefSymbol_ExternFunc("thunk", "text", 0), + {0}, + } + } + }, + {0} + } + }; + + String8 bar_lib = t_coff_from_def_lib(arena, bar_lib_any); + String8 foo_lib = t_coff_from_def_lib(arena, foo_lib_any); + + T_Ok(t_write_file(str8_lit("bar.lib"), bar_lib)); + T_Ok(t_write_file(str8_lit("foo.lib"), foo_lib)); + T_Ok(t_write_entry_obj()); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe bar.lib foo.lib entry.obj /include:thunk"); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(arena, exe); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; + PE_ParsedStaticImportTable static_imptab = pe_static_imports_from_data(arena, pe.is_pe32, pe.section_count, section_table, exe, pe.data_dir_franges[PE_DataDirectoryIndex_IMPORT]); + COFF_SectionHeader *data_sect = coff_section_header_from_name(str8_zero(), section_table, pe.section_count, str8_lit(".data")); + + T_Ok(static_imptab.count == 1); + + PE_ParsedStaticDLLImport *dll = &static_imptab.v[0]; + T_Ok(str8_matchi(dll->name, str8_lit("foo.dll"))); + + T_Ok(dll->import_count == 1); + PE_ParsedImport *imp = &dll->imports[0]; + T_Ok(imp->type == PE_ParsedImport_Name); + T_Ok(str8_match(imp->u.name.string, str8_lit("bar"), 0)); + + U64 iat_foff = pe_foff_from_voff(exe, &pe, dll->import_address_table_voff); + U64 ilt_foff = pe_foff_from_voff(exe, &pe, dll->import_name_table_voff); + + U64 bar_idx = 0; + U64 bar_iat_addr = 0; + U64 bar_ilt_addr = 0; + str8_deserial_read_struct(exe, iat_foff + bar_idx * sizeof(U64), &bar_iat_addr); + str8_deserial_read_struct(exe, ilt_foff + bar_idx * sizeof(U64), &bar_ilt_addr); + + T_Ok(bar_iat_addr != 0); + T_Ok(bar_ilt_addr != 0); + T_Ok(bar_iat_addr == bar_ilt_addr); + T_Ok(data_sect->voff <= bar_iat_addr && bar_iat_addr < data_sect->voff + data_sect->vsize); +} + TEST(opt_ref_dangling_section) { T_Ok(t_write_def_obj("entry.obj", (T_COFF_DefObj){ @@ -6893,4 +7007,194 @@ TEST(fold_with_largest_align) #endif +#if 0 +TEST(reloc_apply_off_out_of_bounds) +{ + T_Ok(t_write_def_obj("bad.obj", (T_COFF_DefObj){ + .machine = T_COFF_DefSetMachine(X64), + .sections = (T_COFF_DefSection[]){ + { + "text", ".text", str8_lit_comp("\x00\x00\x00\x00"), .flags = "rx:code@1", + .relocs = (T_COFF_DefReloc[]){ + T_COFF_DefReloc(X64_Addr32, max_U32, "target"), + {0} + } + }, + {0} + }, + .symbols = (T_COFF_DefSymbol[]){ + T_COFF_DefSymbol_AbsStatic("target", 0), + T_COFF_DefSymbol_Extern("entry", "text", 0), + {0} + } + })); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe bad.obj"); + T_Ok(g_last_exit_code != 0); +} +#endif + +#if 0 +TEST(lib_member_reloc_apply_off_out_of_bounds) +{ + T_Ok(t_write_def_lib("bad.lib", (T_COFF_DefLib){ + .emit_second_member = 1, + .members = (T_COFF_DefLibMember[]){ + { + .type = T_COFF_DefLibMember_Obj, + .obj = { + .path = str8_lit("bad_member.obj"), + .machine = T_COFF_DefSetMachine(X64), + .sections = (T_COFF_DefSection[]){ + { + "data", ".data", str8_lit_comp("\x00\x00\x00\x00"), .flags = "rw:data@1", + .relocs = (T_COFF_DefReloc[]){ + T_COFF_DefReloc(X64_Addr32, max_U32, "target"), + {0} + } + }, + {0} + }, + .symbols = (T_COFF_DefSymbol[]){ + T_COFF_DefSymbol_AbsStatic("target", 0), + T_COFF_DefSymbol_Extern("bad", "data", 0), + {0} + } + } + }, + {0} + } + })); + + T_Ok(t_write_def_obj("entry.obj", (T_COFF_DefObj){ + .machine = T_COFF_DefSetMachine(X64), + .sections = (T_COFF_DefSection[]){ + { "text", ".text", str8_lit_comp("\xC3"), .flags = "rx:code@1" }, + {0} + }, + .symbols = (T_COFF_DefSymbol[]){ + T_COFF_DefSymbol_Extern("entry", "text", 0), + {0} + } + })); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /include:bad entry.obj bad.lib"); + T_Ok(g_last_exit_code != 0); +} +#endif + +#if 0 +TEST(opt_ref_comdat_undef_section) +{ + T_Ok(t_write_def_obj("entry.obj", (T_COFF_DefObj){ + .machine = T_COFF_DefSetMachine(X64), + .sections = (T_COFF_DefSection[]){ + { + "text", ".text", str8_lit_comp("\x48\xC7\xC0\x00\x00\x00\x00\xC3"), .flags = "rx:code@1", + .relocs = (T_COFF_DefReloc[]){ + T_COFF_DefReloc(X64_Addr32Nb, 3, "caller"), + {0} + } + }, + {0} + }, + .symbols = (T_COFF_DefSymbol[]){ + T_COFF_DefSymbol_Extern("entry", "text", 0), + T_COFF_DefSymbol_Undef("caller"), + {0} + } + })); + + T_Ok(t_write_def_obj("caller.obj", (T_COFF_DefObj){ + .machine = T_COFF_DefSetMachine(X64), + .sections = (T_COFF_DefSection[]){ + { + "caller", ".caller", str8_lit_comp("\x00\x00\x00\x00"), .flags = "rw:data@1", .raw_flags = COFF_SectionFlag_LnkCOMDAT, + .relocs = (T_COFF_DefReloc[]){ + T_COFF_DefReloc(X64_Addr32Nb, 0, ".target"), + {0} + } + }, + {0} + }, + .symbols = (T_COFF_DefSymbol[]){ + T_COFF_DefSymbol_Secdef("caller", COFF_ComdatSelect_Any), + T_COFF_DefSymbol_Extern("caller", "caller", 0), + T_COFF_DefSymbol_UndefSec(".target", COFF_SectionFlag_CntInitializedData|COFF_SectionFlag_MemRead), + {0} + } + })); + + T_Ok(t_write_def_obj("target.obj", (T_COFF_DefObj){ + .machine = T_COFF_DefSetMachine(X64), + .sections = (T_COFF_DefSection[]){ + { "target", ".target", str8_lit("target"), .flags = "r:data@1", .raw_flags = COFF_SectionFlag_LnkCOMDAT }, + {0} + }, + .symbols = (T_COFF_DefSymbol[]){ + T_COFF_DefSymbol_Secdef("target", COFF_ComdatSelect_Any), + {0} + } + })); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj caller.obj target.obj"); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(arena, exe); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; + String8 string_table = str8_substr(exe, pe.string_table_range); + COFF_SectionHeader *target_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".target")); + T_Ok(target_sect != 0); +} +#endif + +#if 0 +TEST(opt_ref_weak_alias_comdat) +{ + T_Ok(t_write_def_obj("weak.obj", (T_COFF_DefObj){ + .machine = T_COFF_DefSetMachine(X64), + .sections = (T_COFF_DefSection[]){ + { "target", ".target", str8_lit("target"), .flags = "r:data@1", .raw_flags = COFF_SectionFlag_LnkCOMDAT }, + {0} + }, + .symbols = (T_COFF_DefSymbol[]){ + T_COFF_DefSymbol_Secdef("target", COFF_ComdatSelect_Any), + T_COFF_DefSymbol_Extern("target", "target", 0), + T_COFF_DefSymbol_Weak("weak_target", COFF_WeakExt_SearchAlias, "target"), + {0} + } + })); + + T_Ok(t_write_def_obj("entry.obj", (T_COFF_DefObj){ + .machine = T_COFF_DefSetMachine(X64), + .sections = (T_COFF_DefSection[]){ + { + "text", ".text", str8_lit_comp("\x48\xC7\xC0\x00\x00\x00\x00\xC3"), .flags = "rx:code@1", + .relocs = (T_COFF_DefReloc[]){ + T_COFF_DefReloc(X64_Addr32Nb, 3, "weak_target"), + {0} + } + }, + {0} + }, + .symbols = (T_COFF_DefSymbol[]){ + T_COFF_DefSymbol_Extern("entry", "text", 0), + T_COFF_DefSymbol_Undef("weak_target"), + {0} + } + })); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj weak.obj"); + T_Ok(g_last_exit_code == 0); + + String8 exe = t_read_file(arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(arena, exe); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; + String8 string_table = str8_substr(exe, pe.string_table_range); + COFF_SectionHeader *target_sect = coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".target")); + T_Ok(target_sect != 0); +} +#endif + #undef T_Group