From c1958809ebede89002bbe2095bc449f218938521 Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Wed, 30 Jul 2025 15:21:10 -0700 Subject: [PATCH] make use of undocumented anti-dependency search property on weak symbols to implement alternate name using weak symbols --- src/coff/coff.h | 7 +-- src/linker/lnk.c | 36 ++++++++++++--- src/linker/lnk_config.c | 27 ++++++++--- src/linker/lnk_config.h | 3 ++ src/linker/lnk_symbol_table.c | 84 ++++++++++++++++++++++++++--------- src/linker/lnk_symbol_table.h | 7 ++- 6 files changed, 123 insertions(+), 41 deletions(-) diff --git a/src/coff/coff.h b/src/coff/coff.h index 1648df0e..68038f0c 100644 --- a/src/coff/coff.h +++ b/src/coff/coff.h @@ -294,9 +294,10 @@ typedef struct COFF_Symbol32 typedef U32 COFF_WeakExtType; enum { - COFF_WeakExt_NoLibrary = 1, - COFF_WeakExt_SearchLibrary = 2, - COFF_WeakExt_SearchAlias = 3 + COFF_WeakExt_NoLibrary = 1, + COFF_WeakExt_SearchLibrary = 2, + COFF_WeakExt_SearchAlias = 3, + COFF_WeakExt_AntiDependency = 4, // default symbol must not reference a weak symbol }; // storage class: External diff --git a/src/linker/lnk.c b/src/linker/lnk.c index 212ae949..fcdd68ab 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -1076,6 +1076,7 @@ THREAD_POOL_TASK_FUNC(lnk_weak_symbol_finder) case COFF_WeakExt_NoLibrary: { // NOLIBRARY means weak symbol should be resolved in case where strong definition pulls in lib member. } break; + case COFF_WeakExt_AntiDependency: case COFF_WeakExt_SearchLibrary: { member_symbol = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Lib, symbol->name); } break; @@ -1550,9 +1551,32 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) ProfEnd(); } break; case State_InputAlternateNames: { + ProfBegin("Input Alternate Names"); + COFF_ObjWriter *obj_writer = 0; for (; *last_alt_name; last_alt_name = &(*last_alt_name)->next) { - lnk_symbol_table_push_alt_name(symtab, 0, (*last_alt_name)->data.from, (*last_alt_name)->data.to); + // make object writer if it was reset + if (obj_writer == 0) { + obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_Unknown); + } + + // append weak symbol + COFF_ObjSymbol *tag = coff_obj_writer_push_symbol_undef(obj_writer, (*last_alt_name)->data.to); + coff_obj_writer_push_symbol_weak(obj_writer, (*last_alt_name)->data.from, COFF_WeakExt_AntiDependency, tag); + + // flush on last directive or next directive is issued from a different obj + if ((*last_alt_name)->next == 0 || (*last_alt_name)->data.obj != (*last_alt_name)->next->data.obj) { + LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); + input->path = (*last_alt_name)->data.obj ? (*last_alt_name)->data.obj->path : str8_lit("RADLINK"); + input->dedup_id = push_str8f(scratch.arena, "* ALTERNATE NAMES FOR %S *", input->path); + input->data = coff_obj_writer_serialize(tp_arena->v[0], obj_writer); + input->lib = (*last_alt_name)->data.obj ? (*last_alt_name)->data.obj->lib : 0; + + // reset obj writer + coff_obj_writer_release(&obj_writer); + obj_writer = 0; + } } + ProfEnd(); } break; case State_PushDllHelperUndefSymbol: { ProfBegin("Push Dll Helper Undef Symbol"); @@ -1997,6 +2021,10 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) state_list_push(scratch.arena, state_list, State_InputSymbols); continue; } + if (*last_alt_name != 0) { + state_list_push(scratch.arena, state_list, State_InputAlternateNames); + continue; + } if (input_obj_list.count) { state_list_push(scratch.arena, state_list, State_InputObjs); continue; @@ -2014,10 +2042,6 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) continue; } } - if (*last_alt_name != 0) { - state_list_push(scratch.arena, state_list, State_InputAlternateNames); - continue; - } if (lookup_undef_list.count) { state_list_push(scratch.arena, state_list, State_LookupUndef); continue; @@ -2054,7 +2078,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) } // pass over symbol table and replace weak symbols without a strong definition with fallback definitions - lnk_finalize_weak_symbols(tp, symtab); + lnk_finalize_weak_symbols(tp_arena, tp, symtab); // log { diff --git a/src/linker/lnk_config.c b/src/linker/lnk_config.c index 657bdfbe..5cf2475d 100644 --- a/src/linker/lnk_config.c +++ b/src/linker/lnk_config.c @@ -609,6 +609,7 @@ lnk_parse_alt_name_directive(String8 string, LNK_Obj *obj, LNK_AltName *alt_out) if (pair.node_count == 2) { alt_out->from = pair.first->string; alt_out->to = pair.last->string; + alt_out->obj = obj; is_parse_ok = 1; } else { lnk_error_cmd_switch(LNK_Error_Cmdl, obj, LNK_CmdSwitch_AlternateName, "syntax error in \"%S\", expected format \"FROM=TO\"", string); @@ -1092,12 +1093,25 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam if (value_strings.node_count == 1) { LNK_AltName alt_name; if (lnk_parse_alt_name_directive(value_strings.first->string, obj, &alt_name)) { - alt_name.from = push_str8_copy(arena, alt_name.from); - alt_name.to = push_str8_copy(arena, alt_name.to); - LNK_AltNameNode *alt_name_n = push_array(arena, LNK_AltNameNode, 1); - alt_name_n->data = alt_name; - SLLQueuePush(config->alt_name_list.first, config->alt_name_list.last, alt_name_n); - config->alt_name_list.count += 1; + String8 to_extant = {0}; + if (hash_table_search_string_string(config->alt_name_ht, alt_name.from, &to_extant)) { + if (str8_match(to_extant, alt_name.to, 0)) { + // ignore, duplicate + } else { + lnk_error_obj(LNK_Error_AlternateNameConflict, obj, "conflicting alternative name: existing '%S=%S' vs. new '%S=%S'", alt_name.from, to_extant, alt_name.from, alt_name.to); + } + } else { + hash_table_push_string_string(arena, config->alt_name_ht, alt_name.from, alt_name.to); + + alt_name.from = push_str8_copy(arena, alt_name.from); + alt_name.to = push_str8_copy(arena, alt_name.to); + + LNK_AltNameNode *alt_name_n = push_array(arena, LNK_AltNameNode, 1); + alt_name_n->data = alt_name; + + SLLQueuePush(config->alt_name_list.first, config->alt_name_list.last, alt_name_n); + config->alt_name_list.count += 1; + } } } else { lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "invalid number of parameters"); @@ -1973,6 +1987,7 @@ lnk_config_from_cmd_line(Arena *arena, String8List raw_cmd_line, LNK_CmdLine cmd config->pdb_hash_type_name_length = 8; config->data_dir_count = PE_DataDirectoryIndex_COUNT; config->export_ht = hash_table_init(scratch.arena, max_U16/2); + config->alt_name_ht = hash_table_init(scratch.arena, 0x100); // process command line switches for (LNK_CmdOption *cmd = cmd_line.first_option; cmd != 0; cmd = cmd->next) { diff --git a/src/linker/lnk_config.h b/src/linker/lnk_config.h index 960fee52..07eee00c 100644 --- a/src/linker/lnk_config.h +++ b/src/linker/lnk_config.h @@ -253,6 +253,7 @@ typedef struct LNK_AltName { String8 from; String8 to; + struct LNK_Obj *obj; } LNK_AltName; typedef struct LNK_AltNameNode @@ -392,6 +393,7 @@ typedef struct LNK_Config String8List remove_sections; LNK_IO_Flags io_flags; HashTable *export_ht; + HashTable *alt_name_ht; } LNK_Config; // --- MSVC Error Codes -------------------------------------------------------- @@ -563,6 +565,7 @@ internal B32 lnk_parse_merge_directive (String8 string, struct LNK_Obj *obj, internal B32 lnk_parse_export_directive (Arena *arena, String8 directive, struct LNK_Obj *obj, PE_ExportParse *export_out); internal B32 lnk_parse_export_directive_ex(Arena *arena, String8List directive, struct LNK_Obj *obj, PE_ExportParse *export_out); +internal LNK_AltNameNode * lnk_alt_name_list_push(Arena *arena, LNK_AltNameList *list, LNK_AltName data); internal LNK_MergeDirectiveNode * lnk_merge_directive_list_push(Arena *arena, LNK_MergeDirectiveList *list, LNK_MergeDirective data); // --- Getters ----------------------------------------------------------------- diff --git a/src/linker/lnk_symbol_table.c b/src/linker/lnk_symbol_table.c index 89888a75..babde339 100644 --- a/src/linker/lnk_symbol_table.c +++ b/src/linker/lnk_symbol_table.c @@ -75,6 +75,12 @@ lnk_symbol_list_concat_in_place(LNK_SymbolList *list, LNK_SymbolList *to_concat) SLLConcatInPlace(list, to_concat); } +internal void +lnk_symbol_concat_in_place_array(LNK_SymbolList *list, LNK_SymbolList *to_concat, U64 to_concat_count) +{ + SLLConcatInPlaceArray(list, to_concat, to_concat_count); +} + internal LNK_SymbolList lnk_symbol_list_from_array(Arena *arena, LNK_SymbolArray arr) { @@ -467,7 +473,6 @@ lnk_symbol_table_init(TP_Arena *arena) for (U64 i = 0; i < LNK_SymbolScope_Count; ++i) { symtab->chunk_lists[i] = push_array(arena->v[0], LNK_SymbolHashTrieChunkList, arena->count); } - symtab->alt_names = hash_table_init(arena->v[0], 1024); return symtab; } @@ -487,15 +492,8 @@ lnk_symbol_table_push(LNK_SymbolTable *symtab, LNK_SymbolScope scope, LNK_Symbol internal LNK_Symbol * lnk_symbol_table_search(LNK_SymbolTable *symtab, LNK_SymbolScope scope, String8 name) { - U64 hash = lnk_symbol_hash(name); + U64 hash = lnk_symbol_hash(name); LNK_SymbolHashTrie *trie = lnk_symbol_hash_trie_search(symtab->scopes[scope], hash, name); - if (trie == 0) { - String8 alt_name = {0}; - if (hash_table_search_string_string(symtab->alt_names, name, &alt_name)) { - U64 alt_hash = lnk_symbol_hash(alt_name); - trie = lnk_symbol_hash_trie_search(symtab->scopes[scope], alt_hash, alt_name); - } - } return trie ? trie->symbol : 0; } @@ -514,16 +512,39 @@ lnk_symbol_table_searchf(LNK_SymbolTable *symtab, LNK_SymbolScope scope, char *f return symbol; } -internal void -lnk_symbol_table_push_alt_name(LNK_SymbolTable *symtab, LNK_Obj *obj, String8 from, String8 to) +internal +THREAD_POOL_TASK_FUNC(lnk_check_anti_dependecy_task) { - String8 to_extant; - if (hash_table_search_string_string(symtab->alt_names, from, &to_extant)) { - if (!str8_match(to_extant, to, 0)) { - lnk_error_obj(LNK_Error_AlternateNameConflict, obj, "conflicting alternative name: existing '%S=%S' vs. new '%S=%S'", from, to_extant, from, to); + LNK_FinalizeWeakSymbolsTask *task = raw_task; + LNK_SymbolTable *symtab = task->symtab; + LNK_SymbolHashTrieChunk *chunk = task->chunks[task_id]; + + for EachIndex(i, chunk->count) { + LNK_Symbol *symbol = chunk->v[i].symbol; + COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_defined(symbol); + COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol_parsed); + if (symbol_interp == COFF_SymbolValueInterp_Weak) { + COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(symbol_parsed, symbol->u.defined.obj->header.is_big_obj); + if (weak_ext->characteristics == COFF_WeakExt_AntiDependency) { + COFF_ParsedSymbol default_symbol_parsed = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, weak_ext->tag_index); + COFF_SymbolValueInterpType default_symbol_interp = coff_interp_from_parsed_symbol(default_symbol_parsed); + + COFF_SymbolValueInterpType actual_default_symbol_interp = default_symbol_interp; + if (default_symbol_interp == COFF_SymbolValueInterp_Undefined) { + LNK_Symbol *actual_default_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, default_symbol_parsed.name); + if (actual_default_symbol) { + COFF_ParsedSymbol actual_default_symbol_parsed = lnk_parsed_symbol_from_defined(actual_default_symbol); + actual_default_symbol_interp = coff_interp_from_parsed_symbol(actual_default_symbol_parsed); + } + } + + if (actual_default_symbol_interp == COFF_SymbolValueInterp_Weak) { + LNK_SymbolNode *symbol_n = push_array(arena, LNK_SymbolNode, 1); + symbol_n->data = symbol; + lnk_symbol_list_push_node(&task->anti_dependency_symbols[task_id], symbol_n); + } + } } - } else { - hash_table_push_string_string(symtab->arena->v[0], symtab->alt_names, from, to); } } @@ -538,10 +559,10 @@ THREAD_POOL_TASK_FUNC(lnk_finalize_weak_symbols_task) for EachIndex(i, chunk->count) { LNK_Symbol *symbol = chunk->v[i].symbol; - COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); - COFF_SymbolValueInterpType symbol_interp = coff_interp_symbol(symbol_parsed.section_number, symbol_parsed.value, symbol_parsed.storage_class); + COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_defined(symbol); + COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol_parsed); if (symbol_interp == COFF_SymbolValueInterp_Weak) { - struct LookupLocation { struct LookupLocation *next; LNK_SymbolDefined symbol; }; + struct LookupLocation { struct LookupLocation *next; LNK_SymbolDefined symbol; B32 is_anti_dependency; }; struct LookupLocation *lookup_first = 0, *lookup_last = 0; LNK_SymbolDefined current_symbol = symbol->u.defined; @@ -615,10 +636,10 @@ THREAD_POOL_TASK_FUNC(lnk_finalize_weak_symbols_task) } internal void -lnk_finalize_weak_symbols(TP_Context *tp, LNK_SymbolTable *symtab) +lnk_finalize_weak_symbols(TP_Arena *arena, TP_Context *tp, LNK_SymbolTable *symtab) { ProfBeginFunction(); - Temp scratch = scratch_begin(0,0); + Temp scratch = scratch_begin(arena->v, arena->count); U64 chunks_count = 0; for EachIndex(worker_id, tp->worker_count) { chunks_count += symtab->chunk_lists[LNK_SymbolScope_Defined][worker_id].count; } @@ -632,6 +653,25 @@ lnk_finalize_weak_symbols(TP_Context *tp, LNK_SymbolTable *symtab) } LNK_FinalizeWeakSymbolsTask task = { .symtab = symtab, .chunks = chunks }; + + { + TP_Temp temp = tp_temp_begin(arena); + task.anti_dependency_symbols = push_array(scratch.arena, LNK_SymbolList, tp->worker_count); + tp_for_parallel(tp, arena, chunks_count, lnk_check_anti_dependecy_task, &task); + + LNK_SymbolList anti_dependency_symbol_list = {0}; + lnk_symbol_concat_in_place_array(&anti_dependency_symbol_list, task.anti_dependency_symbols, tp->worker_count); + LNK_SymbolArray anti_dependency_symbols = lnk_symbol_array_from_list(scratch.arena, anti_dependency_symbol_list); + radsort(anti_dependency_symbols.v, anti_dependency_symbols.count, lnk_symbol_defined_is_before); + + for EachIndex(symbol_idx, anti_dependency_symbols.count) { + LNK_Symbol *s = &anti_dependency_symbols.v[symbol_idx]; + lnk_error_obj(LNK_Error_UnresolvedSymbol, s->u.defined.obj, "unresolved symbol %S", s->name); + } + + tp_temp_end(temp); + } + tp_for_parallel(tp, 0, chunks_count, lnk_finalize_weak_symbols_task, &task); scratch_end(scratch); diff --git a/src/linker/lnk_symbol_table.h b/src/linker/lnk_symbol_table.h index 2598f234..ed1003c5 100644 --- a/src/linker/lnk_symbol_table.h +++ b/src/linker/lnk_symbol_table.h @@ -104,7 +104,6 @@ typedef struct LNK_SymbolTable TP_Arena *arena; LNK_SymbolHashTrie *scopes[LNK_SymbolScope_Count]; LNK_SymbolHashTrieChunkList *chunk_lists[LNK_SymbolScope_Count]; - HashTable *alt_names; } LNK_SymbolTable; // --- Workers Contensts ------------------------------------------------------- @@ -113,6 +112,7 @@ typedef struct { LNK_SymbolTable *symtab; LNK_SymbolHashTrieChunk **chunks; + LNK_SymbolList *anti_dependency_symbols; } LNK_FinalizeWeakSymbolsTask; // --- Symbol Make ------------------------------------------------------------- @@ -127,6 +127,7 @@ internal LNK_Symbol * lnk_make_import_symbol(Arena *arena, String8 name, String8 internal void lnk_symbol_list_push_node(LNK_SymbolList *list, LNK_SymbolNode *node); internal LNK_SymbolNode * lnk_symbol_list_push(Arena *arena, LNK_SymbolList *list, LNK_Symbol *symbol); internal void lnk_symbol_list_concat_in_place(LNK_SymbolList *list, LNK_SymbolList *to_concat); +internal void lnk_symbol_concat_in_place_array(LNK_SymbolList *list, LNK_SymbolList *to_concat, U64 to_concat_count); internal LNK_SymbolList lnk_symbol_list_from_array(Arena *arena, LNK_SymbolArray arr); internal LNK_SymbolNodeArray lnk_symbol_node_array_from_list(Arena *arena, LNK_SymbolList list); internal LNK_SymbolArray lnk_symbol_array_from_list(Arena *arena, LNK_SymbolList list); @@ -146,9 +147,7 @@ internal void lnk_symbol_table_push(LNK_SymbolTable *symtab, LNK_Sy internal LNK_Symbol * lnk_symbol_table_search(LNK_SymbolTable *symtab, LNK_SymbolScope scope, String8 name); internal LNK_Symbol * lnk_symbol_table_searchf(LNK_SymbolTable *symtab, LNK_SymbolScope scope, char *fmt, ...); -internal void lnk_symbol_table_push_alt_name(LNK_SymbolTable *symtab, struct LNK_Obj *obj, String8 from, String8 to); - -internal void lnk_finalize_weak_symbols(TP_Context *tp, LNK_SymbolTable *symtab); +internal void lnk_finalize_weak_symbols(TP_Arena *arena, TP_Context *tp, LNK_SymbolTable *symtab); // --- Symbol Contrib Helpers --------------------------------------------------