From 1dc72b2bcff369b769660670dd814dde70a885a9 Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Fri, 15 May 2026 14:32:45 -0700 Subject: [PATCH] replace single-hop associative section walk to multi-hop --- src/base/base_core.h | 2 + src/linker/hash_table.c | 19 ++ src/linker/hash_table.h | 3 + src/linker/lnk.c | 470 ++++++++++++++++++---------------- src/linker/lnk.h | 10 +- src/linker/lnk_debug_info.c | 13 +- src/linker/lnk_log.h | 2 + src/linker/lnk_symbol_table.c | 46 ++++ src/linker/lnk_symbol_table.h | 1 + src/pe/pe_make_import_table.c | 11 +- 10 files changed, 343 insertions(+), 234 deletions(-) diff --git a/src/base/base_core.h b/src/base/base_core.h index 493519ab..01e56b26 100644 --- a/src/base/base_core.h +++ b/src/base/base_core.h @@ -267,6 +267,7 @@ # define ins_atomic_u32_add_eval(x,c) InterlockedAdd((LONG *)(x), (c)) # define ins_atomic_u8_eval_assign(x,c) InterlockedExchange8((CHAR *)(x), (c)) # define ins_atomic_u8_or(x,c) InterlockedOr8((char *)(x), (char)c) +# define ins_atomic_u32_or(x,c) InterlockedOr((LONG *)(x), (LONG)c) # else # error Atomic intrinsics not defined for this compiler / architecture combination. # endif @@ -286,6 +287,7 @@ # define ins_atomic_u32_eval_cond_assign(x,k,c) ({ U32 _new = (c); __atomic_compare_exchange_n((U32 *)(x),&_new,(k),0,__ATOMIC_SEQ_CST,__ATOMIC_SEQ_CST); _new; }) # define ins_atomic_u8_eval_assign(x,c) __atomic_exchange_n((x), (c), __ATOMIC_SEQ_CST) # define ins_atomic_u8_or(x,c) __atomic_fetch_or((U8 *)(x), (U8)(c), __ATOMIC_SEQ_CST) +# define ins_atomic_u32_or(x,c) __atomic_fetch_or((U32 *)(x), (U32)(c), __ATOMIC_SEQ_CST) #else # error Atomic intrinsics not defined for this compiler / architecture. #endif diff --git a/src/linker/hash_table.c b/src/linker/hash_table.c index 7280bfc3..6e33adee 100644 --- a/src/linker/hash_table.c +++ b/src/linker/hash_table.c @@ -725,6 +725,18 @@ hash_map_push_u64_u64(Arena *arena, HashMap *hm, U64 key, U64 value) return hash_map_push(arena, hm, hash_map_hasher(str8_struct(&key)), (HashMapKeyValue){ .key = { .key_u64 = key }, .value = { .value_u64 = value } }, hash_map_match_u64); } +internal HashMapNode * +hash_map_push_raw_raw(Arena *arena, HashMap *hm, void *key, void *value) +{ + return hash_map_push(arena, hm, hash_map_hasher(str8_struct(&key)), (HashMapKeyValue){ .key = { .key_raw = key }, .value = { .value_raw = value } }, hash_map_match_raw); +} + +internal HashMapNode * +hash_map_push_raw_u64(Arena *arena, HashMap *hm, void *key, U64 value) +{ + return hash_map_push(arena, hm, hash_map_hasher(str8_struct(&key)), (HashMapKeyValue){ .key = { .key_raw = key }, .value = { .value_u64 = value } }, hash_map_match_u64); +} + internal HashMapNode * hash_map_push_path_u64(Arena *arena, HashMap *hm, String8 path, U64 value) { @@ -794,6 +806,13 @@ hash_map_search_raw_raw(HashMap *hm, void *key) return n ? n->v.value.value_raw : 0; } +internal U64 * +hash_map_search_raw_u64(HashMap *hm, void *key) +{ + HashMapNode *n = hash_map_search(hm, hash_map_hasher(str8_struct(&key)), (HashMapKey){ .key_raw = key }, hash_map_match_raw); + return n ? &n->v.value.value_u64 : 0; +} + //////////////////////////////// internal B32 hash_map_purge_u32(HashMap *hm, U32 key) { return hash_map_purge_item(hm, hash_map_hasher(str8_struct(&key)), (HashMapKey){ .key_u32 = key }, hash_map_match_u32); } diff --git a/src/linker/hash_table.h b/src/linker/hash_table.h index 3f9337b5..1750a8c3 100644 --- a/src/linker/hash_table.h +++ b/src/linker/hash_table.h @@ -169,6 +169,8 @@ internal HashMapNode * hash_map_push_u32_string (Arena *arena, HashMap *hm, U3 internal HashMapNode * hash_map_push_u64_raw (Arena *arena, HashMap *hm, U64 key, void *value); internal HashMapNode * hash_map_push_u64_string (Arena *arena, HashMap *hm, U64 key, String8 value); internal HashMapNode * hash_map_push_u64_u64 (Arena *arena, HashMap *hm, U64 key, U64 value); +internal HashMapNode * hash_map_push_raw_raw (Arena *arena, HashMap *hm, void *key, void *value); +internal HashMapNode * hash_map_push_raw_u64 (Arena *arena, HashMap *hm, void *key, U64 value); internal HashMapNode * hash_map_push_path_u64 (Arena *arena, HashMap *hm, String8 path, U64 value); 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); @@ -180,6 +182,7 @@ internal void * hash_map_search_path_raw (HashMap *hm, String8 key); internal void * hash_map_search_u64_raw (HashMap *hm, U64 key); internal U64 * hash_map_search_u64_u64 (HashMap *hm, U64 key); internal void * hash_map_search_raw_raw (HashMap *hm, void *key); +internal U64 * hash_map_search_raw_u64 (HashMap *hm, void *key); internal HashMapKeyValue * key_value_from_hash_map (Arena *arena, HashMap *hm); internal U32 * keys_from_hash_map_u32 (Arena *arena, HashMap *hm); diff --git a/src/linker/lnk.c b/src/linker/lnk.c index 1f2368cf..943e4605 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -1968,7 +1968,7 @@ lnk_link_image(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer PE_MakeImportList *imports = hash_table_search_path_raw(delayed_imports_ht, dll_name_n->string); String8 import_debug_symbols = lnk_make_dll_import_debug_symbols(scratch.arena, config->machine, dll_name_n->string); String8 import_obj = pe_make_import_dll_obj_delayed(arena->v[0], time_stamp, config->machine, dll_name_n->string, config->delay_load_helper_name, import_debug_symbols, *imports, emit_biat, emit_uiat); - lnk_inputer_push_obj(inputer, 0, dll_name_n->string, import_obj); + lnk_inputer_push_obj(inputer, 0, str8f(inputer->arena, "Import:%S", dll_name_n->string), import_obj); } String8 linker_debug_symbols = lnk_make_linker_debug_symbols(arena->v[0], config->machine); @@ -1990,7 +1990,7 @@ lnk_link_image(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer PE_MakeImportList *imports = hash_table_search_path_raw(static_imports_ht, dll_name_n->string); String8 import_debug_symbols = lnk_make_dll_import_debug_symbols(scratch.arena, config->machine, dll_name_n->string); String8 import_obj = pe_make_import_dll_obj_static(arena->v[0], time_stamp, config->machine, dll_name_n->string, import_debug_symbols, *imports); - lnk_inputer_push_obj(inputer, 0, dll_name_n->string, import_obj); + lnk_inputer_push_obj(inputer, 0, str8f(inputer->arena, "Import:%S", dll_name_n->string), import_obj); } String8 linker_debug_symbols = lnk_make_linker_debug_symbols(scratch.arena, config->machine); @@ -2395,133 +2395,272 @@ internal THREAD_POOL_TASK_FUNC(lnk_walk_relocs_and_mark_ref_sections_task) { ProfBeginFunction(); - Temp scratch = scratch_begin(0,0); + + Temp scratch = scratch_begin(0,0); + Temp scratch2 = scratch_begin(&scratch.arena, 1); LNK_OptRefTask *task = raw_task; LNK_SymbolTable *symtab = task->symtab; + LNK_Config *config = task->config; + LNK_ObjList objs = task->objs; + + U8 **is_live = 0; + U64 *active_thread_count = 0; + LNK_RelocRefsList *ref_queue = 0; + LNK_RelocRefsList free_list = {0}; + if (task_id == 0) { + active_thread_count = push_array(scratch.arena, U64, 1); + ref_queue = push_array(scratch.arena, LNK_RelocRefsList, 1); + + // alloc live flags for each section + is_live = push_array_no_zero(scratch.arena, U8 *, objs.count); + { + U64 obj_idx = 0; + for EachNode(n, LNK_ObjNode, task->objs.first) { + is_live[obj_idx] = push_array(scratch.arena, U8, n->data.header.section_count_no_null + 1); + + COFF_SectionHeader *section_table = lnk_coff_section_table_from_obj(&n->data); + for EachIndex(sect_idx, n->data.header.section_count_no_null) { + COFF_SectionHeader *header = §ion_table[sect_idx]; + is_live[obj_idx][sect_idx + 1] = !(header->flags & COFF_SectionFlag_LnkCOMDAT); + } + + obj_idx += 1; + } + } + + // define roots + { + // tls + LNK_Symbol *tls_symbol = lnk_symbol_table_searchf(symtab, MSCRT_TLS_SYMBOL_NAME); + if (tls_symbol) { + lnk_include_symbol(config, str8_lit(MSCRT_TLS_SYMBOL_NAME), 0); + } + + // push tasks for each root symbol + for EachNode(root_n, LNK_IncludeSymbolNode, config->include_symbol_list.first) { + LNK_Symbol *root = lnk_symbol_table_search(symtab, root_n->v.name); + LNK_ObjSymbolRef root_ref = lnk_ref_from_symbol(root); + + LNK_RelocRefs *r = push_array(scratch.arena, LNK_RelocRefs, 1); + r->obj = root_ref.obj; + r->relocs.count = 1; + r->relocs.v = push_array(scratch.arena, COFF_Reloc, 1); + r->relocs.v[0].isymbol = root_ref.symbol_idx; + + lnk_reloc_refs_list_push(scratch.arena, ref_queue, r); + } + + // push task for every non-COMDAT section + for EachNode(obj_n, LNK_ObjNode, objs.first) { + LNK_Obj *obj = &obj_n->data; + for EachIndex(sect_idx, obj->header.section_count_no_null) { + U32 section_number = sect_idx+1; + COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, section_number); + + // is section eligible for walking? + if (section_header->flags & COFF_SectionFlag_LnkRemove) { continue; } + if (section_header->flags & COFF_SectionFlag_LnkCOMDAT) { continue; } + if (section_header->flags & COFF_SectionFlag_LnkInfo) { continue; } + if (section_header->flags & LNK_SECTION_FLAG_DEBUG) { continue; } + + // divide relocs and push task for each reloc block + COFF_RelocArray relocs = lnk_coff_reloc_info_from_section_number(obj, section_number); + U64 new_task_count = CeilIntegerDiv(relocs.count, LNK_RELOCS_PER_TASK); + LNK_RelocRefs *new_tasks = push_array(scratch.arena, LNK_RelocRefs, new_task_count); + for EachIndex(new_task_idx, new_task_count) { + LNK_RelocRefs *r = new_tasks + new_task_idx; + r->obj = obj; + r->relocs.count = Min(LNK_RELOCS_PER_TASK, relocs.count - (new_task_idx * LNK_RELOCS_PER_TASK)); + r->relocs.v = relocs.v + (new_task_idx * LNK_RELOCS_PER_TASK); + + lnk_reloc_refs_list_push(scratch.arena, ref_queue, r); + } + } + } + } + } + tp_broadcast(&is_live); + tp_broadcast(&ref_queue); + tp_broadcast(&active_thread_count); - LNK_RelocRefsList free_list = {0}; for (;;) { // update active thread count - ins_atomic_u32_inc_eval(&task->active_thread_count); + ins_atomic_u32_inc_eval(active_thread_count); for (;;) { // pop head node - LNK_RelocRefsNode *node = lnk_reloc_refs_list_pop_node_atomic(task->reloc_refs); + LNK_RelocRefsNode *node = lnk_reloc_refs_list_pop_node_atomic(ref_queue); if (!node) { break; } LNK_RelocRefs *reloc_refs = node->v; - LNK_RelocRefsNode *first_node = 0, *last_node = 0; + for EachIndex(reloc_idx, reloc_refs->relocs.count) { - COFF_Reloc *reloc = &reloc_refs->relocs.v[reloc_idx]; - LNK_ObjSymbolRef reloc_defn = (LNK_ObjSymbolRef){ .obj = reloc_refs->obj, .symbol_idx = reloc->isymbol }; - COFF_ParsedSymbol reloc_parsed = lnk_parsed_symbol_from_coff_symbol_idx(reloc_defn.obj, reloc_defn.symbol_idx); - COFF_SymbolValueInterpType reloc_interp = coff_interp_from_parsed_symbol(reloc_parsed); + COFF_Reloc *reloc = &reloc_refs->relocs.v[reloc_idx]; - LNK_ObjSymbolRef ref_symbol = reloc_defn; - for (;;) { - COFF_ParsedSymbol ref_parsed = lnk_parsed_symbol_from_coff_symbol_idx(ref_symbol.obj, ref_symbol.symbol_idx); - COFF_SymbolValueInterpType ref_interp = coff_interp_from_parsed_symbol(ref_parsed); - - LNK_ObjSymbolRef next_ref = {0}; - if (ref_interp == COFF_SymbolValueInterp_Regular) { - LNK_Symbol *symlink = lnk_obj_get_comdat_symlink(ref_symbol.obj, ref_parsed.section_number); - if (symlink) { - ref_symbol = lnk_ref_from_symbol(symlink); - } - break; - } else if (ref_interp == COFF_SymbolValueInterp_Undefined) { - if (reloc_parsed.storage_class == COFF_SymStorageClass_External) { - LNK_Symbol *defn = lnk_symbol_table_search(symtab, ref_parsed.name); - next_ref = lnk_ref_from_symbol(defn); + // reloc -> symbol + LNK_ObjSymbolRef ref_symbol = (LNK_ObjSymbolRef){ .obj = reloc_refs->obj, .symbol_idx = reloc->isymbol }; + { + Temp temp = temp_begin(scratch2.arena); + HashMap seen_hm = {0}; + B32 keep_walking = 1; + do { + // detect cyclic chains + U64 symbol_key = ((U64)ref_symbol.obj->input_idx << 32ull) | (U64)ref_symbol.symbol_idx; + if (hash_map_search_u64_u64(&seen_hm, symbol_key) == 0) { + hash_map_push_u64_u64(temp.arena, &seen_hm, symbol_key, 1); } else { + COFF_ParsedSymbol reloc_parsed = lnk_parsed_symbol_from_coff_symbol_idx(reloc_refs->obj, reloc->isymbol); + lnk_error_obj(LNK_Warning_CyclicSymbol, reloc_refs->obj, "symbol %S forms a cyclic chain (/OPT:REF)", reloc_parsed.name); MemoryZeroStruct(&ref_symbol); break; } - } else if (ref_interp == COFF_SymbolValueInterp_Weak) { - LNK_Symbol *defn = lnk_symbol_table_search(symtab, ref_parsed.name); - next_ref = lnk_ref_from_symbol(defn); - } else { - break; - } - if (MemoryMatchStruct(&next_ref, &ref_symbol)) { - MemoryZeroStruct(&ref_symbol); - break; - } - ref_symbol = next_ref; + // unpack symbol + COFF_ParsedSymbol ref_parsed = lnk_parsed_symbol_from_coff_symbol_idx(ref_symbol.obj, ref_symbol.symbol_idx); + COFF_SymbolValueInterpType ref_interp = coff_interp_from_parsed_symbol(ref_parsed); + + // resolve symbol + LNK_ObjSymbolRef next_ref = {0}; + if (lnk_resolve_symbol(symtab, ref_symbol, &next_ref)) { + keep_walking = (ref_interp == COFF_SymbolValueInterp_Weak || ref_interp == COFF_SymbolValueInterp_Undefined); + ref_symbol = next_ref; + } else { + keep_walking = 0; + } + } while (keep_walking); + temp_end(temp); } // skip unresolved symbol if (ref_symbol.obj == 0) { continue; } + // unpack resolved symbol COFF_ParsedSymbol ref_parsed = lnk_parsed_symbol_from_coff_symbol_idx(ref_symbol.obj, ref_symbol.symbol_idx); COFF_SymbolValueInterpType ref_interp = coff_interp_from_parsed_symbol(ref_parsed); - LNK_Obj *ref_obj = ref_symbol.obj; if (ref_interp == COFF_SymbolValueInterp_Regular) { - // make section number list (reloc section + associates) - U32Node *section_number_list = push_array(scratch.arena, U32Node, 1); - section_number_list->data = ref_parsed.section_number; - section_number_list->next = ref_obj->associated_sections[ref_parsed.section_number]; + Temp temp = temp_begin(scratch2.arena); - // push section headers relocations to the task stack - for EachNode(section_number_n, U32Node, section_number_list) { - COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(ref_obj, section_number_n->data); + HashMap visited_sections_hm = {0}; + U32Node *stack = push_array(temp.arena, U32Node, 1); + stack->data = ref_parsed.section_number; + do { + U32 section_number = stack->data; + SLLStackPop(stack); + + // is section number valid? + if (section_number == 0 || section_number > ref_symbol.obj->header.section_count_no_null) { continue; } + + // detect cyclic associative sections + if (hash_map_search_u64_u64(&visited_sections_hm, section_number)) { continue; } + hash_map_push_u64_u64(temp.arena, &visited_sections_hm, section_number, 1); + + // push associated section + for EachNode(associated_n, U32Node, ref_symbol.obj->associated_sections[section_number]) { + if (hash_map_search_u64_u64(&visited_sections_hm, associated_n->data)) { continue; } + U32Node *stack_n = push_array(temp.arena, U32Node, 1); + stack_n->data = associated_n->data; + SLLStackPush(stack, stack_n); + } + + COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(ref_symbol.obj, section_number); // is section eligible for walking? if (section_header->flags & COFF_SectionFlag_LnkRemove) { continue; } if (section_header->flags & COFF_SectionFlag_LnkInfo) { continue; } if (section_header->flags & LNK_SECTION_FLAG_DEBUG) { continue; } - if (section_header->flags & LNK_SECTION_FLAG_LIVE) { continue; } - // mark section live - section_header->flags |= LNK_SECTION_FLAG_LIVE; + // on first section visit set live flag and queue new section to walk + U8 was_visited = ins_atomic_u8_eval_assign(&is_live[ref_symbol.obj->input_idx][section_number], 1); + if (was_visited) { continue; } LNK_RelocRefsNode *node; if (free_list.head.node) { node = lnk_reloc_refs_list_pop_node(&free_list); } else { node = push_array(scratch.arena, LNK_RelocRefsNode, 1); - node->v = push_array(scratch.arena, LNK_RelocRefs, 1); + node->v = push_array(scratch.arena, LNK_RelocRefs, 1); } - node->v->obj = ref_obj; - node->v->relocs = lnk_coff_reloc_info_from_section_number(ref_obj, section_number_n->data); + node->v->obj = ref_symbol.obj; + node->v->relocs = lnk_coff_reloc_info_from_section_number(ref_symbol.obj, section_number); if (first_node == 0) { - first_node = node; - last_node = node; + first_node = node, last_node = node; } else { node->next = first_node; first_node = node; } - } + } while (stack); + + temp_end(temp); } } + // free walk node lnk_reloc_refs_list_push_node(&free_list, node); + // queue new walks if (first_node && last_node) { - lnk_reloc_refs_list_concat_in_place(task->reloc_refs, first_node, last_node); + lnk_reloc_refs_list_concat_in_place(ref_queue, first_node, last_node); } } // are all threads done walking? - U32 active_thread_count = ins_atomic_u32_dec_eval(&task->active_thread_count); - if (active_thread_count == 0 && ins_atomic_ptr_eval(&task->reloc_refs->head.node) == 0) { - break; + { + U32 c = ins_atomic_u32_dec_eval(active_thread_count); + if (c == 0 && ins_atomic_ptr_eval(&ref_queue->head.node) == 0) { + break; + } } // comprehensive solution to the waiting problem - for (; ins_atomic_ptr_eval(&task->reloc_refs->head.node) == 0; ) { + for (; ins_atomic_ptr_eval(&ref_queue->head.node) == 0; ) { // was signaled to exit? - if (ins_atomic_u64_eval(&task->active_thread_count) == 0) { goto exit; } + if (ins_atomic_u32_eval(active_thread_count) == 0) { goto exit; } } } exit:; + barrier_wait(tp->barrier); + // TODO: thread + if (task_id == 0) { + ProfBegin("Remove Unreachable Sections"); + + U64 discard_vsize = 0; + U64 discard_fsize = 0; + U64 discard_count = 0; + + for EachNode(obj_n, LNK_ObjNode, objs.first) { + LNK_Obj *obj = &obj_n->data; + + for EachIndex(sect_idx, obj->header.section_count_no_null) { + U32 section_number = sect_idx+1; + if (is_live[obj->input_idx][section_number]) { continue; } + + COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, section_number); + section_header->flags |= COFF_SectionFlag_LnkRemove; + + if (section_header->flags & COFF_SectionFlag_CntUninitializedData) { + discard_vsize += section_header->vsize; + } else { + discard_fsize += section_header->fsize; + } + discard_count += 1; + } + } + + lnk_log(LNK_Log_Debug, "/OPT:REF total virt size discard: %M", (unsigned long long)discard_vsize); + lnk_log(LNK_Log_Debug, "/OPT:REF total file size discard: %M", (unsigned long long)discard_fsize); + lnk_log(LNK_Log_Debug, "/OPT:REF total section discard : %llu", (unsigned long long)discard_count); + + ProfEnd(); + } + barrier_wait(tp->barrier); + + scratch_end(scratch2); scratch_end(scratch); ProfEnd(); } @@ -2529,161 +2668,12 @@ THREAD_POOL_TASK_FUNC(lnk_walk_relocs_and_mark_ref_sections_task) internal void lnk_opt_ref(TP_Context *tp, LNK_SymbolTable *symtab, LNK_Config *config, LNK_ObjList objs) { - ProfBeginFunction(); - Temp scratch = scratch_begin(0,0); - - LNK_RelocRefsList reloc_refs = {0}; - - // - // reset live flag on sections - // - for EachNode(obj_n, LNK_ObjNode, objs.first) { - for EachIndex(sect_idx, obj_n->data.header.section_count_no_null) { - COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(&obj_n->data, sect_idx+1); - section_header->flags &= ~LNK_SECTION_FLAG_LIVE; - } - } - - // - // define roots - // - { - // tls - LNK_Symbol *tls_symbol = lnk_symbol_table_searchf(symtab, MSCRT_TLS_SYMBOL_NAME); - if (tls_symbol) { - lnk_include_symbol(config, str8_lit(MSCRT_TLS_SYMBOL_NAME), 0); - } - - // push tasks for each root symbol - for EachNode(root_n, LNK_IncludeSymbolNode, config->include_symbol_list.first) { - LNK_Symbol *root = lnk_symbol_table_search(symtab, root_n->v.name); - LNK_ObjSymbolRef root_ref = lnk_ref_from_symbol(root); - - LNK_RelocRefs *r = push_array(scratch.arena, LNK_RelocRefs, 1); - r->obj = root_ref.obj; - r->relocs.count = 1; - r->relocs.v = push_array(scratch.arena, COFF_Reloc, 1); - r->relocs.v[0].isymbol = root_ref.symbol_idx; - - lnk_reloc_refs_list_push(scratch.arena, &reloc_refs, r); - } - - // push task for every non-COMDAT section - for EachNode(obj_n, LNK_ObjNode, objs.first) { - LNK_Obj *obj = &obj_n->data; - for EachIndex(sect_idx, obj->header.section_count_no_null) { - U32 section_number = sect_idx+1; - COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, section_number); - - // is section eligible for walking? - if (section_header->flags & COFF_SectionFlag_LnkRemove) { continue; } - if (section_header->flags & COFF_SectionFlag_LnkCOMDAT) { continue; } - if (section_header->flags & COFF_SectionFlag_LnkInfo) { continue; } - if (section_header->flags & LNK_SECTION_FLAG_DEBUG) { continue; } - - // divide relocs and push task for each reloc block - COFF_RelocArray relocs = lnk_coff_reloc_info_from_section_number(obj, section_number); - U64 new_task_count = CeilIntegerDiv(relocs.count, LNK_RELOCS_PER_TASK); - LNK_RelocRefs *new_tasks = push_array(scratch.arena, LNK_RelocRefs, new_task_count); - for EachIndex(new_task_idx, new_task_count) { - LNK_RelocRefs *r = new_tasks + new_task_idx; - r->obj = obj; - r->relocs.count = Min(LNK_RELOCS_PER_TASK, relocs.count - (new_task_idx * LNK_RELOCS_PER_TASK)); - r->relocs.v = relocs.v + (new_task_idx * LNK_RELOCS_PER_TASK); - - lnk_reloc_refs_list_push(scratch.arena, &reloc_refs, r); - } - } - } - } - - // - // walk relocations and mark referenced sections with live flag - // - LNK_OptRefTask task = {0}; - task.symtab = symtab; - task.reloc_refs = &reloc_refs; - tp_for_parallel_prof(tp, 0, tp->worker_count, lnk_walk_relocs_and_mark_ref_sections_task, &task, "Mark Live Sections"); - - ProfBegin("Remove Unreachable Sections"); - for EachNode(obj_n, LNK_ObjNode, objs.first) { - LNK_Obj *obj = &obj_n->data; - - for EachIndex(sect_idx, obj->header.section_count_no_null) { - U32 section_number = sect_idx+1; - COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, section_number); - - if (section_header->flags & LNK_SECTION_FLAG_DEBUG) { continue; } - - // remove unreferenced sections - if (~section_header->flags & LNK_SECTION_FLAG_LIVE && section_header->flags & COFF_SectionFlag_LnkCOMDAT) { - section_header->flags |= COFF_SectionFlag_LnkRemove; - } - - // remove associated sections - if (section_header->flags & COFF_SectionFlag_LnkRemove) { - for EachNode(section_number_n, U32Node, obj->associated_sections[section_number]) { - COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, section_number_n->data); - section_header->flags |= COFF_SectionFlag_LnkRemove; - } - } - - // TODO: Reset reserved flag so it does not get propagated to the image sections. - // We need to mask out reserved flags when gathering section definitions to actually - // prevent propagation. - section_header->flags &= ~LNK_SECTION_FLAG_LIVE; - } - } - ProfEnd(); - - scratch_end(scratch); - ProfEnd(); -} - -internal B32 -lnk_resolve_symbol(LNK_SymbolTable *symtab, LNK_ObjSymbolRef symbol, LNK_ObjSymbolRef *symbol_out) -{ - B32 is_resolved = 1; - COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_coff_symbol_idx(symbol.obj, symbol.symbol_idx); - COFF_SymbolValueInterpType symbol_interp = coff_interp_symbol(symbol_parsed.section_number, symbol_parsed.value, symbol_parsed.storage_class); - switch (symbol_interp) { - case COFF_SymbolValueInterp_Regular: { - LNK_Symbol *symlink = lnk_obj_get_comdat_symlink(symbol.obj, symbol_parsed.section_number); - *symbol_out = symlink ? lnk_ref_from_symbol(symlink) : symbol; - } break; - case COFF_SymbolValueInterp_Weak: { - LNK_Symbol *defn = lnk_symbol_table_search(symtab, symbol_parsed.name); - COFF_ParsedSymbol defn_parsed = lnk_parsed_from_symbol(defn); - COFF_SymbolValueInterpType defn_interp = lnk_interp_from_symbol(defn); - if (defn_interp != COFF_SymbolValueInterp_Undefined) { - *symbol_out = lnk_ref_from_symbol(defn); - } else { - is_resolved = 0; - } - } break; - case COFF_SymbolValueInterp_Undefined: { - LNK_Symbol *defn = lnk_symbol_table_search(symtab, symbol_parsed.name); - if (defn) { - *symbol_out = lnk_ref_from_symbol(defn); - } else { - is_resolved = 0; - } - } break; - case COFF_SymbolValueInterp_Common: { - LNK_Symbol *defn = lnk_symbol_table_search(symtab, symbol_parsed.name); - *symbol_out = lnk_ref_from_symbol(defn); - } break; - case COFF_SymbolValueInterp_Abs: { - if (symbol_parsed.storage_class == COFF_SymStorageClass_External) { - LNK_Symbol *defn = lnk_symbol_table_search(symtab, symbol_parsed.name); - *symbol_out = lnk_ref_from_symbol(defn); - } else { - *symbol_out = symbol; - } - } break; - case COFF_SymbolValueInterp_Debug: { *symbol_out = symbol; } break; - } - return is_resolved; + ProfScope("Mark Live Sections") + tp_for_parallel(tp, + 0, + tp->worker_count, + lnk_walk_relocs_and_mark_ref_sections_task, + &(LNK_OptRefTask){ .symtab = symtab, .config = config, .objs = objs }); } internal @@ -3728,8 +3718,26 @@ THREAD_POOL_TASK_FUNC(lnk_patch_section_symbols_task) COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); if (interp == COFF_SymbolValueInterp_Undefined) { if (symbol.storage_class == COFF_SymStorageClass_Section) { + B32 is_referenced = 0; + COFF_SectionHeader *section_table = lnk_coff_section_table_from_obj(obj); + for EachIndex(sect_idx, obj->header.section_count_no_null) { + COFF_SectionHeader *section_header = §ion_table[sect_idx]; + if (section_header->flags & COFF_SectionFlag_LnkRemove) { continue; } + if (section_header->flags & COFF_SectionFlag_LnkInfo) { continue; } + if (section_header->flags & LNK_SECTION_FLAG_DEBUG) { continue; } + COFF_RelocArray relocs = lnk_coff_relocs_from_section_header(obj, section_header); + for EachIndex(reloc_idx, relocs.count) { + if (relocs.v[reloc_idx].isymbol == symbol_idx) { + is_referenced = 1; + break; + } + } + if (is_referenced) { break; } + } + if (!is_referenced) { continue; } + LNK_Section *sect = lnk_section_table_search(task->sectab, symbol.name, symbol.value); - if (sect) { + if (sect && (~sect->flags & COFF_SectionFlag_LnkRemove)) { if (~sect->flags & COFF_SectionFlag_MemDiscardable) { LNK_SectionContrib *first_sc = lnk_get_first_section_contrib(sect); if (obj->header.is_big_obj) { @@ -3747,7 +3755,32 @@ THREAD_POOL_TASK_FUNC(lnk_patch_section_symbols_task) lnk_error_obj(LNK_Error_SectRefsDiscardedMemory, obj, "symbol %S (No. 0x%llx) references section with discard flag", symbol.name, symbol_idx); } } else { - lnk_error_obj(LNK_Error_UnresolvedSymbol, obj, "undefined section symbol %S (No 0x%llx) refers to an image section that doesn't exist", symbol.name, symbol_idx); + U64 fallback_voff = 0; + U64 fallback_align = Max(task->sect_align, KB(4)); + for EachIndex(sect_idx, task->image_sects.count) { + LNK_Section *image_sect = task->image_sects.v[sect_idx]; + U64 image_sect_size = AlignPow2(Max(image_sect->vsize, image_sect->fsize), fallback_align); + if (image_sect_size == 0) { image_sect_size = fallback_align; } + fallback_voff = Max(fallback_voff, image_sect->voff + image_sect_size); + } + fallback_voff = AlignPow2(fallback_voff, fallback_align); + + LNK_Section *fallback_sect = task->image_sects.v[task->image_sects.count-1]; + U32 fallback_section_number = safe_cast_u32(fallback_sect->sect_idx + 1); + U32 fallback_section_offset = safe_cast_u32(fallback_voff - fallback_sect->voff); + if (obj->header.is_big_obj) { + COFF_Symbol32 *symbol32 = symbol.raw_symbol; + symbol32->section_number = fallback_section_number; + symbol32->value = fallback_section_offset; + symbol32->storage_class = COFF_SymStorageClass_Static; + } else { + COFF_Symbol16 *symbol16 = symbol.raw_symbol; + symbol16->section_number = safe_cast_u16(fallback_section_number); + symbol16->value = fallback_section_offset; + symbol16->storage_class = COFF_SymStorageClass_Static; + } + + lnk_error_obj(LNK_Warning_UndefinedSectionSymbol, obj, "undefined section symbol %S (No. 0x%llx) refers to an image section that doesn't exist; patching to %#llx", symbol.name, symbol_idx, fallback_voff); } } } @@ -4275,6 +4308,7 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT .objs = objs, .function_pad_min = config->function_pad_min, .default_align = coff_default_align_from_machine(config->machine), + .sect_align = config->sect_align, .null_sc = push_array(arena->v[0], LNK_SectionContrib, 1), }; diff --git a/src/linker/lnk.h b/src/linker/lnk.h index 7ce1ebe3..e5a24863 100644 --- a/src/linker/lnk.h +++ b/src/linker/lnk.h @@ -73,8 +73,7 @@ typedef struct LNK_Inputer #define LNK_IMPORT_STUB "*** RAD_IMPORT_STUB ***" #define LNK_NULL_SYMBOL "*** RAD_NULL_SYMBOL ***" -#define LNK_SECTION_FLAG_LIVE (1 << 0) -#define LNK_SECTION_FLAG_DEBUG (1 << 1) +#define LNK_SECTION_FLAG_DEBUG (1 << 0) typedef U8 LNK_LibMemberFlags; enum @@ -214,9 +213,9 @@ typedef struct typedef struct { - LNK_SymbolTable *symtab; - U32 active_thread_count; - LNK_RelocRefsList *reloc_refs; + LNK_SymbolTable *symtab; + LNK_Config *config; + LNK_ObjList objs; } LNK_OptRefTask; typedef struct @@ -263,6 +262,7 @@ typedef struct LNK_Obj **objs; U64 function_pad_min; U64 default_align; + U64 sect_align; LNK_SectionContrib *null_sc; LNK_SectionContrib ***sect_map; HashTable *contribs_ht; diff --git a/src/linker/lnk_debug_info.c b/src/linker/lnk_debug_info.c index b6d998d1..a88682dc 100644 --- a/src/linker/lnk_debug_info.c +++ b/src/linker/lnk_debug_info.c @@ -2407,14 +2407,16 @@ THREAD_POOL_TASK_FUNC(lnk_push_dbi_sec_contrib_task) if (obj_sect_header->flags & COFF_SectionFlag_LnkRemove) { continue; } if (obj_sect_header->flags & LNK_SECTION_FLAG_DEBUG) { continue; } + String8 header_name = str8_cstring_capped(obj_sect_header->name, obj_sect_header->name + sizeof(obj_sect_header->name)); + if (str8_match(header_name, str8_lit(".pdata"), 0)) { continue; } + U64 sect_number; String8 sect_data; U32 sect_off; U32 data_crc; if (obj_sect_header->flags & COFF_SectionFlag_CntUninitializedData) { - if (obj_sect_header->vsize == 0) { - continue; - } + if (obj_sect_header->vsize == 0) { continue; } + U64 sect_num = rng1u64_array_num_from_value__binary_search(&task->image_section_virt_ranges, obj_sect_header->voff); sect_number = sect_num-1; Assert(sect_number < task->image_section_virt_ranges.count); @@ -2422,9 +2424,8 @@ THREAD_POOL_TASK_FUNC(lnk_push_dbi_sec_contrib_task) sect_off = obj_sect_header->voff - task->image_section_virt_ranges.v[sect_number].min; data_crc = 0; } else { - if (obj_sect_header->fsize == 0) { - continue; - } + if (obj_sect_header->fsize == 0) { continue; } + U64 sect_num = rng1u64_array_num_from_value__binary_search(&task->image_section_file_ranges, obj_sect_header->foff); sect_number = sect_num-1; Assert(sect_number < task->image_section_file_ranges.count); diff --git a/src/linker/lnk_log.h b/src/linker/lnk_log.h index 781a4a20..dc94d47f 100644 --- a/src/linker/lnk_log.h +++ b/src/linker/lnk_log.h @@ -109,6 +109,8 @@ typedef enum LNK_Warning_TryingToExportEntryPoint, LNK_Warning_InferAsanFail, LNK_Warning_GHash, + LNK_Warning_UndefinedSectionSymbol, + LNK_Warning_CyclicSymbol, LNK_Warning_Last, LNK_Error_Count diff --git a/src/linker/lnk_symbol_table.c b/src/linker/lnk_symbol_table.c index dc9f37a0..4b945827 100644 --- a/src/linker/lnk_symbol_table.c +++ b/src/linker/lnk_symbol_table.c @@ -708,6 +708,52 @@ exit:; return is_resolved; } +internal B32 +lnk_resolve_symbol(LNK_SymbolTable *symtab, LNK_ObjSymbolRef symbol, LNK_ObjSymbolRef *symbol_out) +{ + B32 is_resolved = 1; + COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_coff_symbol_idx(symbol.obj, symbol.symbol_idx); + COFF_SymbolValueInterpType symbol_interp = coff_interp_symbol(symbol_parsed.section_number, symbol_parsed.value, symbol_parsed.storage_class); + switch (symbol_interp) { + case COFF_SymbolValueInterp_Regular: { + LNK_Symbol *symlink = lnk_obj_get_comdat_symlink(symbol.obj, symbol_parsed.section_number); + *symbol_out = symlink ? lnk_ref_from_symbol(symlink) : symbol; + } break; + case COFF_SymbolValueInterp_Weak: { + LNK_Symbol *defn = lnk_symbol_table_search(symtab, symbol_parsed.name); + COFF_ParsedSymbol defn_parsed = lnk_parsed_from_symbol(defn); + COFF_SymbolValueInterpType defn_interp = lnk_interp_from_symbol(defn); + if (defn_interp != COFF_SymbolValueInterp_Undefined) { + *symbol_out = lnk_ref_from_symbol(defn); + } else { + is_resolved = 0; + } + } break; + case COFF_SymbolValueInterp_Undefined: { + LNK_Symbol *defn = lnk_symbol_table_search(symtab, symbol_parsed.name); + if (defn) { + *symbol_out = lnk_ref_from_symbol(defn); + } else { + is_resolved = 0; + } + } break; + case COFF_SymbolValueInterp_Common: { + LNK_Symbol *defn = lnk_symbol_table_search(symtab, symbol_parsed.name); + *symbol_out = lnk_ref_from_symbol(defn); + } break; + case COFF_SymbolValueInterp_Abs: { + if (symbol_parsed.storage_class == COFF_SymStorageClass_External) { + LNK_Symbol *defn = lnk_symbol_table_search(symtab, symbol_parsed.name); + *symbol_out = lnk_ref_from_symbol(defn); + } else { + *symbol_out = symbol; + } + } break; + case COFF_SymbolValueInterp_Debug: { *symbol_out = symbol; } break; + } + return is_resolved; +} + internal THREAD_POOL_TASK_FUNC(lnk_replace_weak_with_default_symbol_task) { diff --git a/src/linker/lnk_symbol_table.h b/src/linker/lnk_symbol_table.h index 7ca55350..cdf38304 100644 --- a/src/linker/lnk_symbol_table.h +++ b/src/linker/lnk_symbol_table.h @@ -134,6 +134,7 @@ internal U64 lnk_foff_from_symbol(COFF_SectionHeader **image_section_table, // --- Weak Symbol ------------------------------------------------------------- internal B32 lnk_resolve_weak_symbol(LNK_SymbolTable *symtab, LNK_ObjSymbolRef symbol, LNK_ObjSymbolRef *resolved_symbol_out); +internal B32 lnk_resolve_symbol(LNK_SymbolTable *symtab, LNK_ObjSymbolRef symbol, LNK_ObjSymbolRef *symbol_out); internal void lnk_replace_weak_with_default_symbols(TP_Context *tp, LNK_SymbolTable *symtab); diff --git a/src/pe/pe_make_import_table.c b/src/pe/pe_make_import_table.c index 450b4a60..8bc39203 100644 --- a/src/pe/pe_make_import_table.c +++ b/src/pe/pe_make_import_table.c @@ -268,7 +268,8 @@ pe_make_import_dll_obj_static(Arena *arena, COFF_TimeStamp time_stamp, COFF_Mach COFF_ObjSection *iat_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$5"), PE_IDATA_SECTION_FLAGS|import_align, str8_zero()); COFF_ObjSection *int_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$6"), PE_IDATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, str8_zero()); COFF_ObjSection *dll_name_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".idata$7"), PE_IDATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, dll_name_cstr); - COFF_ObjSection *code_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text$zz"), PE_TEXT_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, str8_zero()); + COFF_ObjSection *code_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text$zz"), PE_TEXT_SECTION_FLAGS |COFF_SectionFlag_Align1Bytes, str8_zero()); + COFF_ObjSection *debug_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".debug$S"), PE_DEBUG_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, debug_symbols); COFF_ObjSymbol *ilt_symbol = coff_obj_writer_push_symbol_static(obj_writer, ilt_sect->name, 0, ilt_sect); COFF_ObjSymbol *iat_symbol = coff_obj_writer_push_symbol_static(obj_writer, iat_sect->name, 0, iat_sect); @@ -352,10 +353,10 @@ pe_make_import_dll_obj_delayed(Arena *arena, COFF_TimeStamp time_stamp, COFF_Mac // DLL handle U64 handle_size = coff_word_size_from_machine(machine); - U8 *handle = push_array(obj_writer->arena, U8, handle_size); + U8 *handle = push_array(obj_writer->arena, U8, handle_size); // import align - U64 import_size = coff_word_size_from_machine(machine); + U64 import_size = coff_word_size_from_machine(machine); COFF_SectionFlags import_align = coff_section_flag_from_align_size(import_size); // push sections @@ -364,8 +365,8 @@ pe_make_import_dll_obj_delayed(Arena *arena, COFF_TimeStamp time_stamp, COFF_Mac COFF_ObjSection *iat_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".didat$5"), PE_IDATA_SECTION_FLAGS|import_align, str8_zero()); COFF_ObjSection *int_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".didat$6"), PE_IDATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, str8_zero()); COFF_ObjSection *dll_name_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".didat$7"), PE_IDATA_SECTION_FLAGS|COFF_SectionFlag_Align2Bytes, dll_name_cstr); - COFF_ObjSection *code_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text$"), PE_TEXT_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, str8_zero()); - COFF_ObjSection *handle_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".data$"), PE_DATA_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, str8_array(handle, handle_size)); + COFF_ObjSection *code_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text$"), PE_TEXT_SECTION_FLAGS |COFF_SectionFlag_Align1Bytes, str8_zero()); + COFF_ObjSection *handle_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".data$"), PE_DATA_SECTION_FLAGS |COFF_SectionFlag_Align1Bytes, str8_array(handle, handle_size)); COFF_ObjSection *debug_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".debug$S"), PE_DEBUG_SECTION_FLAGS|COFF_SectionFlag_Align1Bytes, debug_symbols); COFF_ObjSection *biat_sect = 0; COFF_ObjSection *uiat_sect = 0;