From cccbbfc74c6dd9392c9de16d280ec7c05dd7a46f Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Thu, 12 Jun 2025 14:45:24 -0700 Subject: [PATCH] do not patch debug section symbols and patch replaced COMDATs in separate pass --- src/linker/lnk.c | 144 ++++++++++++++++++++++++++++++--- src/linker/lnk_section_table.c | 33 ++++---- 2 files changed, 151 insertions(+), 26 deletions(-) diff --git a/src/linker/lnk.c b/src/linker/lnk.c index fe820f8e..f51f30a1 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -176,6 +176,7 @@ lnk_config_from_argcv(Arena *arena, int argc, char **argv) if (lnk_cmd_line_has_switch(cmd_line, LNK_CmdSwitch_Dll)) { lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_SubSystem, "%S", pe_string_from_subsystem(PE_WindowsSubsystem_WINDOWS_GUI)); } + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_FunctionPadMin, ""); lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_HighEntropyVa, ""); lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_ManifestUac, "\"level='asInvoker' uiAccess='false'\""); lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_NxCompat, ""); @@ -1445,7 +1446,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) continue; } - String8 full_path = os_full_path_from_path(scratch.arena, input->dedup_id); + String8 full_path = input->dedup_id.size ? os_full_path_from_path(scratch.arena, input->dedup_id) : str8_zero(); B32 was_full_path_used = hash_table_search_path_u64(loaded_obj_ht, full_path, 0); if (was_full_path_used) { continue; @@ -1918,7 +1919,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) // warn about unused delayloads if (config->flags & LNK_ConfigFlag_CheckUnusedDelayLoadDll) { for (String8Node *dll_name_n = config->delay_load_dll_list.first; dll_name_n != 0; dll_name_n = dll_name_n->next) { - if (!hash_table_search_string_raw(delayed_imports, dll_name_n->string, 0)) { + if (!hash_table_search_path_raw(delayed_imports, dll_name_n->string)) { lnk_error(LNK_Warning_UnusedDelayLoadDll, "/DELAYLOAD: %S found no imports", dll_name_n->string); } } @@ -2376,6 +2377,25 @@ THREAD_POOL_TASK_FUNC(lnk_gather_section_contribs_task) scratch_end(scratch); } +internal +THREAD_POOL_TASK_FUNC(lnk_flag_debug_symbols_task) +{ + LNK_BuildImageTask *task = raw_task; + U64 obj_idx = task_id; + LNK_Obj *obj = task->objs[obj_idx]; + + COFF_ParsedSymbol symbol; + for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { + symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + if (interp == COFF_SymbolValueInterp_Regular) { + if (lnk_is_coff_section_debug(obj, symbol.section_number-1)) { + task->u.patch_symtabs.was_symbol_patched[obj_idx][symbol_idx] = 1; + } + } + } +} + internal THREAD_POOL_TASK_FUNC(lnk_set_comdat_leaders_task) { @@ -2388,7 +2408,7 @@ THREAD_POOL_TASK_FUNC(lnk_set_comdat_leaders_task) for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - if (interp == COFF_SymbolValueInterp_Regular && symbol.storage_class == COFF_SymStorageClass_External && symbol.value == 0) { + if (interp == COFF_SymbolValueInterp_Regular && symbol.storage_class == COFF_SymStorageClass_External) { COFF_SectionHeader *sect_header = lnk_coff_section_header_from_section_number(obj, symbol.section_number); if (sect_header->flags & COFF_SectionFlag_LnkCOMDAT) { LNK_Symbol *defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol.name); @@ -2401,18 +2421,21 @@ THREAD_POOL_TASK_FUNC(lnk_set_comdat_leaders_task) } internal -THREAD_POOL_TASK_FUNC(lnk_patch_comdats_task) +THREAD_POOL_TASK_FUNC(lnk_patch_comdat_leaders_task) { LNK_BuildImageTask *task = raw_task; U64 obj_idx = task_id; LNK_Obj *obj = task->objs[obj_idx]; - ProfBeginV("Patch COMDATs [%S]", obj->path); + ProfBeginV("Patch COMDAT Leaders [%S]", obj->path); COFF_ParsedSymbol symbol; for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); + + if (task->u.patch_symtabs.was_symbol_patched[obj_idx][symbol_idx]) { continue; } + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - if (interp == COFF_SymbolValueInterp_Regular && symbol.storage_class == COFF_SymStorageClass_External && symbol.value == 0) { + if (interp == COFF_SymbolValueInterp_Regular && symbol.storage_class == COFF_SymStorageClass_External) { COFF_SectionHeader *sect_header = lnk_coff_section_header_from_section_number(obj, symbol.section_number); if (sect_header->flags & COFF_SectionFlag_LnkCOMDAT) { LNK_Symbol *defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol.name); @@ -2437,6 +2460,87 @@ THREAD_POOL_TASK_FUNC(lnk_patch_comdats_task) ProfEnd(); } +internal +THREAD_POOL_TASK_FUNC(lnk_patch_replaced_comdats_task) +{ + Temp scratch = scratch_begin(&arena, 1); + + LNK_BuildImageTask *task = raw_task; + U64 obj_idx = task_id; + LNK_Obj *obj = task->objs[obj_idx]; + + ProfBeginV("%S", obj->path); + + LNK_Symbol **symlinks = push_array(scratch.arena, LNK_Symbol *, obj->header.section_count_no_null+1); + + ProfBegin("Build Symlinks"); + { + COFF_ParsedSymbol symbol; + for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { + symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + if (interp == COFF_SymbolValueInterp_Regular && symbol.aux_symbol_count == 0 && symbol.storage_class == COFF_SymStorageClass_External) { + COFF_SectionHeader *sect_header = lnk_coff_section_header_from_section_number(obj, symbol.section_number); + if (sect_header->flags & COFF_SectionFlag_LnkCOMDAT) { + if (symlinks[symbol.section_number] == 0 || symbol.value == 0) { + symlinks[symbol.section_number] = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol.name); + } + } + } + } + } + ProfEnd(); + + ProfBegin("Patch"); + { + COFF_ParsedSymbol symbol; + for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { + symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); + + if (task->u.patch_symtabs.was_symbol_patched[obj_idx][symbol_idx]) { continue; } + + COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + if (interp == COFF_SymbolValueInterp_Regular) { + COFF_SectionHeader *sect_header = lnk_coff_section_header_from_section_number(obj, symbol.section_number); + if (sect_header->flags & COFF_SectionFlag_LnkCOMDAT) { + + + LNK_Symbol *comdat_leader = symlinks[symbol.section_number]; + COFF_ParsedSymbol comdat_leader_parsed; + LNK_SectionContrib *comdat_leader_sc; + if (comdat_leader) { + B32 is_this_leader_symbol = comdat_leader->u.defined.obj == obj && comdat_leader->u.defined.symbol_idx == symbol_idx; + if (is_this_leader_symbol) continue; + + comdat_leader_parsed = lnk_parsed_symbol_from_coff_symbol_idx(comdat_leader->u.defined.obj, comdat_leader->u.defined.symbol_idx); + comdat_leader_sc = task->sect_map[comdat_leader->u.defined.obj->input_idx][comdat_leader_parsed.section_number-1]; + } else { + Assert(symbol.storage_class != COFF_SymStorageClass_External); + comdat_leader_sc = task->sect_map[obj_idx][symbol.section_number-1]; + } + + if (obj->header.is_big_obj) { + COFF_Symbol32 *symbol32 = symbol.raw_symbol; + symbol32->section_number = comdat_leader_sc->u.sect_idx+1; + symbol32->value = safe_cast_u32(comdat_leader_sc->u.off + symbol.value); + } else { + COFF_Symbol16 *symbol16 = symbol.raw_symbol; + symbol16->section_number = safe_cast_u16(comdat_leader_sc->u.sect_idx+1); + symbol16->value = safe_cast_u32(comdat_leader_sc->u.off + symbol.value); + } + + task->u.patch_symtabs.was_symbol_patched[obj_idx][symbol_idx] = 1; + } + } + } + } + ProfEnd(); + + scratch_end(scratch); + + ProfEnd(); +} + internal int lnk_section_contrib_ptr_is_before(void *raw_a, void *raw_b) { @@ -2528,8 +2632,10 @@ THREAD_POOL_TASK_FUNC(lnk_patch_regular_symbols_task) COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); if (interp == COFF_SymbolValueInterp_Regular) { COFF_SectionHeader *sect_header = lnk_coff_section_header_from_section_number(obj, symbol.section_number); + Assert(~sect_header->flags & COFF_SectionFlag_LnkCOMDAT); LNK_SectionContrib *sc = task->sect_map[obj_idx][symbol.section_number-1]; + U16 section_number; U32 value; if (sc == &g_null_sc) { @@ -3013,8 +3119,10 @@ THREAD_POOL_TASK_FUNC(lnk_flag_hotpatch_contribs_task) COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); if (interp == COFF_SymbolValueInterp_Regular && COFF_SymbolType_IsFunc(symbol.type)) { COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, symbol.section_number); - LNK_SectionContrib *sc = task->sect_map[obj_idx][symbol.section_number-1]; - sc->hotpatch = !!(section_header->flags & COFF_SectionFlag_CntCode); + LNK_SectionContrib *sc = task->sect_map[obj_idx][symbol.section_number-1]; + if (sc != &g_null_sc) { + sc->hotpatch = !!(section_header->flags & COFF_SectionFlag_CntCode); + } } } } @@ -4185,6 +4293,15 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT { ProfBegin("Finalize Sections Layout"); + // Grouped Sections (PE Format) + // "All contributions with the same object-section name are allocated contiguously in the image, + // and the blocks of contributions are sorted in lexical order by object-section name." + ProfBegin("Sort Sections"); + for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { + lnk_sort_section_contribs(§_n->data); + } + ProfEnd(); + // merge sections if (config->flags & LNK_ConfigFlag_Merge) { lnk_section_table_merge(sectab, config->merge_list); @@ -4253,12 +4370,21 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT task.u.patch_symtabs.common_block_contribs = common_block_contribs; ProfEnd(); + // flag debug symbols to prevent them from being patched in subsequent passes + ProfBegin("Flag Debug Symbols"); + tp_for_parallel(tp, 0, objs_count, lnk_flag_debug_symbols_task, &task); + ProfEnd(); + ProfBegin("Set COMDAT Leaders"); tp_for_parallel(tp, 0, objs_count, lnk_set_comdat_leaders_task, &task); ProfEnd(); + ProfBegin("Patch replaced COMDATs"); + tp_for_parallel(tp, 0, objs_count, lnk_patch_replaced_comdats_task, &task); + ProfEnd(); + ProfBegin("Patch COMDAT Leaders"); - tp_for_parallel(tp, 0, objs_count, lnk_patch_comdats_task, &task); + tp_for_parallel(tp, 0, objs_count, lnk_patch_comdat_leaders_task, &task); ProfEnd(); ProfBegin("Patch Common Block Leaders"); diff --git a/src/linker/lnk_section_table.c b/src/linker/lnk_section_table.c index 565e2490..227f7c77 100644 --- a/src/linker/lnk_section_table.c +++ b/src/linker/lnk_section_table.c @@ -319,27 +319,28 @@ lnk_section_contrib_chunk_is_before(void *raw_a, void *raw_b) } internal void -lnk_finalize_section_layout(LNK_Section *sect, U64 file_align, U64 pad_size) +lnk_sort_section_contribs(LNK_Section *sect) { + ProfBeginFunction(); Temp scratch = scratch_begin(0,0); - ProfBegin("Sort Section Contribs"); - { - // Grouped Sections (PE Format) - // "All contributions with the same object-section name are allocated contiguously in the image, - // and the blocks of contributions are sorted in lexical order by object-section name." - LNK_SectionContribChunk **chunks = lnk_array_from_section_contrib_chunk_list(scratch.arena, sect->contribs); - radsort(chunks, sect->contribs.chunk_count, lnk_section_contrib_chunk_is_before); + LNK_SectionContribChunk **chunks = lnk_array_from_section_contrib_chunk_list(scratch.arena, sect->contribs); + radsort(chunks, sect->contribs.chunk_count, lnk_section_contrib_chunk_is_before); - // repopulate chunk list in sorted order - sect->contribs.first = 0; - sect->contribs.last = 0; - for (U64 chunk_idx = 0; chunk_idx < sect->contribs.chunk_count; chunk_idx += 1) { - SLLQueuePush(sect->contribs.first, sect->contribs.last, chunks[chunk_idx]); - } + // repopulate chunk list in sorted order + sect->contribs.first = 0; + sect->contribs.last = 0; + for (U64 chunk_idx = 0; chunk_idx < sect->contribs.chunk_count; chunk_idx += 1) { + SLLQueuePush(sect->contribs.first, sect->contribs.last, chunks[chunk_idx]); } - ProfEnd(); + scratch_end(scratch); + ProfEnd(); +} + +internal void +lnk_finalize_section_layout(LNK_Section *sect, U64 file_align, U64 pad_size) +{ ProfBegin("Layout Contribs"); U64 cursor = 0; for (LNK_SectionContribChunk *sc_chunk = sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { @@ -366,8 +367,6 @@ lnk_finalize_section_layout(LNK_Section *sect, U64 file_align, U64 pad_size) sect->fsize = AlignPow2(cursor, file_align); } sect->vsize = cursor; - - scratch_end(scratch); } internal void