changed symbol search logic to run in three steps: undefined, weak,

and anti-dependency weak, allowing linking to proceed only after
symbols from previous step are added to the symbol table

this solves the problem of anti-dependency symbols because they are
searched in libraries if the dependency is unresolved weak symbol
This commit is contained in:
Nikita Smith
2025-08-18 19:09:32 -07:00
committed by Nikita Smith
parent e1786d0a11
commit 1bca83610f
+333 -312
View File
@@ -966,7 +966,7 @@ lnk_make_linker_obj(Arena *arena, LNK_Config *config)
} }
internal void internal void
lnk_queue_lib_member_input(Arena *arena, lnk_queue_lib_member_for_input(Arena *arena,
LNK_Config *config, LNK_Config *config,
LNK_Symbol *pull_in_ref, LNK_Symbol *pull_in_ref,
LNK_Symbol *member_symbol, LNK_Symbol *member_symbol,
@@ -1039,16 +1039,10 @@ lnk_queue_lib_member_input(Arena *arena,
} }
internal void internal void
lnk_find_refs(Arena *arena, lnk_opt_ref(TP_Context *tp, LNK_SymbolTable *symtab, LNK_Config *config, LNK_ObjList objs)
TP_Context *tp,
LNK_SymbolTable *symtab,
LNK_Config *config,
LNK_ObjList objs,
LNK_InputImportList *imports_out,
LNK_InputObjList *objs_out)
{ {
ProfBeginFunction(); ProfBeginFunction();
Temp scratch = scratch_begin(&arena,1); Temp scratch = scratch_begin(0,0);
struct Task { struct Task *next; LNK_Obj *obj; COFF_RelocArray relocs; U32 section_number; }; struct Task { struct Task *next; LNK_Obj *obj; COFF_RelocArray relocs; U32 section_number; };
struct Task *task_stack = 0; struct Task *task_stack = 0;
@@ -1099,7 +1093,7 @@ lnk_find_refs(Arena *arena,
// is section eligible for walking? // is section eligible for walking?
if (lnk_is_coff_section_debug(obj, sect_idx)) { continue; } if (lnk_is_coff_section_debug(obj, sect_idx)) { continue; }
if (section_header->flags & COFF_SectionFlag_LnkRemove) { continue; } if (section_header->flags & COFF_SectionFlag_LnkRemove) { continue; }
if (config->opt_ref == LNK_SwitchState_Yes && section_header->flags & COFF_SectionFlag_LnkCOMDAT) { continue; } if (section_header->flags & COFF_SectionFlag_LnkCOMDAT) { continue; }
// divide relocs and push task for each reloc block // divide relocs and push task for each reloc block
COFF_RelocArray relocs = lnk_coff_reloc_info_from_section_number(obj, section_number); COFF_RelocArray relocs = lnk_coff_reloc_info_from_section_number(obj, section_number);
@@ -1132,87 +1126,31 @@ lnk_find_refs(Arena *arena,
for (;;) { for (;;) {
COFF_ParsedSymbol ref_parsed = lnk_parsed_symbol_from_coff_symbol_idx(ref_symbol.obj, ref_symbol.symbol_idx); 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); COFF_SymbolValueInterpType ref_interp = coff_interp_from_parsed_symbol(ref_parsed);
if (ref_interp == COFF_SymbolValueInterp_Weak) {
LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, ref_parsed.name);
COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_defined(defn);
COFF_SymbolValueInterpType defn_interp = coff_interp_from_parsed_symbol(defn_parsed);
if (defn_interp == COFF_SymbolValueInterp_Weak) {
LNK_Symbol *member_symbol = 0;
COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(ref_parsed, ref_symbol.obj->header.is_big_obj);
switch (weak_ext->characteristics) {
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(symtab, LNK_SymbolScope_Lib, ref_parsed.name);
} break;
case COFF_WeakExt_SearchAlias: {
member_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Lib, ref_parsed.name);
if (member_symbol == 0) {
if (str8_match_lit(".weak.", ref_parsed.name, StringMatchFlag_RightSideSloppy)) {
// TODO: Clang and MingGW encode extra info in alias
//
// __attribute__((weak,alias("foo"))) void bar(void);
// static void foo() {}
//
// Clang write these COFF symbols in obj for code above:
//
// 30 00000000 0000000001 0 FUNC NULL EXTERNAL foo
// ...
// 33 00000000 UNDEF 1 NULL NULL WEAK_EXTERNAL bar
// Tag Index 35, Characteristics SEARCH_ALIAS
// 35 00000000 0000000001 0 NULL NULL EXTERNAL .weak.bar.default.foo
//
// In this case linker needs to parse .weak.bar.default.foo and search for bar and foo as well.
Assert("TODO: MinGW weak symbol");
} else {
COFF_ParsedSymbol tag = lnk_parsed_symbol_from_coff_symbol_idx(ref_symbol.obj, weak_ext->tag_index);
member_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Lib, tag.name);
}
}
} break;
default: { NotImplemented; } break;
}
if (member_symbol) { LNK_SymbolDefined next_ref = {0};
lnk_queue_lib_member_input(arena, config, defn, member_symbol, imports_out, objs_out); if (ref_interp == COFF_SymbolValueInterp_Regular) {
MemoryZeroStruct(&ref_symbol);
break;
} else {
ref_symbol = lnk_resolve_weak_symbol(symtab, ref_symbol);
}
} else {
ref_symbol = defn->u.defined;
}
}
else if (ref_interp == COFF_SymbolValueInterp_Undefined) {
if (ref_parsed.storage_class == COFF_SymStorageClass_External) {
LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, ref_parsed.name);
COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_defined(defn);
COFF_SymbolValueInterpType defn_interp = coff_interp_from_parsed_symbol(defn_parsed);
if (defn_interp == COFF_SymbolValueInterp_Undefined) {
LNK_Symbol *member_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Lib, ref_parsed.name);
if (member_symbol) {
lnk_queue_lib_member_input(arena, config, defn, member_symbol, imports_out, objs_out);
}
break;
} else {
ref_symbol = defn->u.defined;
}
} else {
break;
}
} else if (ref_interp == COFF_SymbolValueInterp_Regular) {
LNK_Symbol *symlink = lnk_obj_get_comdat_symlink(ref_symbol.obj, ref_parsed.section_number); LNK_Symbol *symlink = lnk_obj_get_comdat_symlink(ref_symbol.obj, ref_parsed.section_number);
if (symlink) { if (symlink) {
ref_symbol = symlink->u.defined; ref_symbol = symlink->u.defined;
} }
break; break;
} else if (ref_interp == COFF_SymbolValueInterp_Undefined) {
if (reloc_parsed.storage_class == COFF_SymStorageClass_External) {
LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, ref_parsed.name);
next_ref = defn->u.defined;
}
} else if (ref_interp == COFF_SymbolValueInterp_Weak) {
LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, ref_parsed.name);
next_ref = defn->u.defined;
} else { } else {
break; break;
} }
if (MemoryMatchStruct(&next_ref, &ref_symbol)) {
MemoryZeroStruct(&ref_symbol);
break;
}
ref_symbol = next_ref;
} }
// skip unresolved symbol // skip unresolved symbol
@@ -1241,8 +1179,8 @@ lnk_find_refs(Arena *arena,
COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(ref_obj, section_number); COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(ref_obj, section_number);
// is section eligible for walking? // is section eligible for walking?
if (section_header->flags & LNK_SECTION_FLAG_IS_LIVE) { continue; }
if (section_header->flags & COFF_SectionFlag_LnkRemove) { continue; } if (section_header->flags & COFF_SectionFlag_LnkRemove) { continue; }
if (section_header->flags & LNK_SECTION_FLAG_IS_LIVE) { continue; }
if (lnk_is_coff_section_debug(ref_obj, section_number-1)) { continue; } if (lnk_is_coff_section_debug(ref_obj, section_number-1)) { continue; }
// mark section // mark section
@@ -1265,12 +1203,42 @@ lnk_find_refs(Arena *arena,
} }
} }
ProfBegin("Remove Unreachable Sections");
for (LNK_ObjNode *obj_n = objs.first; obj_n != 0; obj_n = obj_n->next) {
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 (lnk_is_coff_section_debug(obj, sect_idx)) { continue; }
// remove unreferenced sections
if (~section_header->flags & LNK_SECTION_FLAG_IS_LIVE && section_header->flags & COFF_SectionFlag_LnkCOMDAT) {
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_IS_LIVE;
if (section_header->flags & COFF_SectionFlag_LnkRemove) {
for (U32Node *section_number_n = obj->associated_sections[section_number]; section_number_n != 0; section_number_n = section_number_n->next) {
U32 section_number = section_number_n->data;
COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, section_number);
section_header->flags |= COFF_SectionFlag_LnkRemove;
}
}
}
}
ProfEnd();
scratch_end(scratch); scratch_end(scratch);
ProfEnd(); ProfEnd();
} }
internal internal
THREAD_POOL_TASK_FUNC(lnk_replace_weak_symbols_with_default_symbol_task) THREAD_POOL_TASK_FUNC(lnk_replace_weak_with_default_symbol_task)
{ {
LNK_ReplaceWeakSymbolsWithDefaultSymbolTask *task = raw_task; LNK_ReplaceWeakSymbolsWithDefaultSymbolTask *task = raw_task;
LNK_SymbolTable *symtab = task->symtab; LNK_SymbolTable *symtab = task->symtab;
@@ -1280,7 +1248,26 @@ THREAD_POOL_TASK_FUNC(lnk_replace_weak_symbols_with_default_symbol_task)
COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_defined(symbol); COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_defined(symbol);
COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol_parsed); COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol_parsed);
if (symbol_interp == COFF_SymbolValueInterp_Weak) { if (symbol_interp == COFF_SymbolValueInterp_Weak) {
symbol->u.defined = lnk_resolve_weak_symbol(symtab, symbol->u.defined); LNK_SymbolDefined resolve = lnk_resolve_weak_symbol(symtab, symbol->u.defined);
COFF_ParsedSymbol resolve_parsed = lnk_parsed_symbol_from_coff_symbol_idx(resolve.obj, resolve.symbol_idx);
COFF_SymbolValueInterpType resolve_interp = coff_interp_from_parsed_symbol(resolve_parsed);
if (resolve_interp == COFF_SymbolValueInterp_Weak) {
COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(resolve_parsed, symbol->u.defined.obj->header.is_big_obj);
COFF_SymStorageClass storage_class = weak_ext->characteristics == COFF_WeakExt_AntiDependency ? COFF_SymStorageClass_Null : COFF_SymStorageClass_External;
if (symbol->u.defined.obj->header.is_big_obj) {
COFF_Symbol32 *symbol32 = symbol_parsed.raw_symbol;
symbol32->section_number = COFF_Symbol_UndefinedSection;
symbol32->value = 0;
symbol32->storage_class = storage_class;
} else {
COFF_Symbol16 *symbol16 = symbol_parsed.raw_symbol;
symbol16->section_number = COFF_Symbol_UndefinedSection;
symbol16->value = 0;
symbol16->storage_class = storage_class;
}
} else {
symbol->u.defined = resolve;
}
} }
} }
} }
@@ -1288,20 +1275,20 @@ THREAD_POOL_TASK_FUNC(lnk_replace_weak_symbols_with_default_symbol_task)
internal LNK_LinkContext internal LNK_LinkContext
lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
{ {
enum State { enum {
State_Null, State_Null,
State_InputDisallowLibs, State_InputDisallowLibs,
State_InputImports, State_InputImports,
State_InputSymbols, State_InputInclude,
State_InputObjs, State_InputObjs,
State_InputLibs, State_InputLibs,
State_InputAlternateNames, State_InputAlternateNames,
State_PushDllHelperUndefSymbol, State_InputDelayLoadDlls,
State_InputLinkerObjs, State_InputLinkerObjs,
State_PushLoadConfigUndefSymbol, State_SearchUndefined,
State_FindRefs, State_SearchWeak,
State_LookupEntryPoint, State_SearchWeakAntiDep,
State_ReportUnresolvedSymbols, State_SearchEntryPoint,
}; };
struct StateNode { struct StateNode *next; enum State state; }; struct StateNode { struct StateNode *next; enum State state; };
struct StateList { U64 count; struct StateNode *first; struct StateNode *last; }; struct StateList { U64 count; struct StateNode *first; struct StateNode *last; };
@@ -1312,75 +1299,68 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
l.count += 1; \ l.count += 1; \
} while (0) } while (0)
#define state_list_pop(l) (l).first->state; SLLQueuePop((l).first, (l).last); (l).count -= 1 #define state_list_pop(l) (l).first->state; SLLQueuePop((l).first, (l).last); (l).count -= 1
typedef enum {
SearchFlag_Undefined = (1 << 0),
SearchFlag_Weak = (1 << 1),
SearchFlag_WeakAntiDep = (1 << 2),
SearchFlag_All = (SearchFlag_Undefined|SearchFlag_Weak|SearchFlag_WeakAntiDep)
} SearchFlags;
ProfBeginFunction(); ProfBeginFunction();
Temp scratch = scratch_begin(tp_arena->v, tp_arena->count); Temp scratch = scratch_begin(tp_arena->v, tp_arena->count);
// inputs //
// state
//
LNK_SymbolTable *symtab = lnk_symbol_table_init(tp_arena);
String8Node **last_include_symbol = &config->include_symbol_list.first; String8Node **last_include_symbol = &config->include_symbol_list.first;
String8Node **last_disallow_lib = &config->disallow_lib_list.first; String8Node **last_disallow_lib = &config->disallow_lib_list.first;
String8Node **last_delay_load_dll = &config->delay_load_dll_list.first;
LNK_AltNameNode **last_alt_name = &config->alt_name_list.first; LNK_AltNameNode **last_alt_name = &config->alt_name_list.first;
LNK_InputObjList input_obj_list = {0}; LNK_InputObjList input_obj_list = {0};
LNK_InputImportList input_import_list = {0}; LNK_InputImportList input_import_list = {0};
LNK_InputLib **input_libs[LNK_InputSource_Count] = { LNK_InputLib **input_libs[LNK_InputSource_Count] = { &config->input_list[LNK_Input_Lib].first, &config->input_default_lib_list.first, &config->input_obj_lib_list.first };
&config->input_list[LNK_Input_Lib].first, LNK_ObjList obj_list = {0};
&config->input_default_lib_list.first, LNK_LibList lib_index[LNK_InputSource_Count] = {0};
&config->input_obj_lib_list.first U64 entry_point_search_attempts = 0;
}; SearchFlags search_flags = SearchFlag_All;
B32 input_linker_objs = 1;
HashTable *static_imports = hash_table_init(scratch.arena, 512);
HashTable *delayed_imports = hash_table_init(scratch.arena, 512);
HashTable *import_stub_ht = hash_table_init(scratch.arena, 0x1000);
// TODO: move disallow_lib, loaded_lib, missing_lib, and loaded_obj to config
Arena *ht_arena = arena_alloc();
HashTable *disallow_lib_ht = hash_table_init(scratch.arena, 0x100);
HashTable *loaded_lib_ht = hash_table_init(scratch.arena, 0x100);
HashTable *missing_lib_ht = hash_table_init(scratch.arena, 0x100);
HashTable *loaded_obj_ht = hash_table_init(scratch.arena, 0x4000);
// input :null_obj // TODO: this should happen in the config before state machine setup
{ {
// input :null_obj
LNK_InputObj *null_obj_input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); LNK_InputObj *null_obj_input = lnk_input_obj_list_push(scratch.arena, &input_obj_list);
null_obj_input->exclude_from_debug_info = 1; null_obj_input->exclude_from_debug_info = 1;
null_obj_input->path = str8_lit("* Null Obj *"); null_obj_input->path = str8_lit("* Null Obj *");
null_obj_input->dedup_id = null_obj_input->path; null_obj_input->dedup_id = null_obj_input->path;
null_obj_input->data = lnk_make_null_obj(tp_arena->v[0]); null_obj_input->data = lnk_make_null_obj(tp_arena->v[0]);
}
// input command line objs // input command line objs
LNK_InputObjList cmd_line_obj_inputs = lnk_input_obj_list_from_string_list(scratch.arena, config->input_list[LNK_Input_Obj]); LNK_InputObjList cmd_line_obj_inputs = lnk_input_obj_list_from_string_list(scratch.arena, config->input_list[LNK_Input_Obj]);
lnk_input_obj_list_concat_in_place(&input_obj_list, &cmd_line_obj_inputs); lnk_input_obj_list_concat_in_place(&input_obj_list, &cmd_line_obj_inputs);
}
// state // TODO: MSVC does not need this flag to include load config
LNK_SymbolTable *symtab = lnk_symbol_table_init(tp_arena); if (config->guard_flags != LNK_Guard_None) {
LNK_SectionTable *sectab = 0; // TODO: config_refactor
HashTable *static_imports = hash_table_init(scratch.arena, 512); String8List value_strings = {0};
HashTable *delayed_imports = hash_table_init(scratch.arena, 512); str8_list_push(scratch.arena, &value_strings, str8_lit(MSCRT_LOAD_CONFIG_SYMBOL_NAME));
LNK_ObjList obj_list = {0}; lnk_apply_cmd_option_to_config(tp_arena->v[0], config, str8_lit("include"), value_strings, 0);
LNK_LibList lib_index[LNK_InputSource_Count] = {0}; }
Arena *ht_arena = arena_alloc();
String8 delay_load_helper_name = {0};
HashTable *disallow_lib_ht = hash_table_init(scratch.arena, 0x100);
HashTable *delay_load_dll_ht = hash_table_init(scratch.arena, 0x100);
HashTable *loaded_lib_ht = hash_table_init(scratch.arena, 0x100);
HashTable *missing_lib_ht = hash_table_init(scratch.arena, 0x100);
HashTable *loaded_obj_ht = hash_table_init(scratch.arena, 0x4000);
U64 entry_point_lookup_attempts = 0;
B32 input_linker_objs = 1;
B32 pending_refs = 0;
// //
// Init state machine // run states
// //
struct StateList state_list = {0}; struct StateList state_list = {0};
state_list_push(scratch.arena, state_list, State_InputDisallowLibs);
state_list_push(scratch.arena, state_list, State_InputObjs);
state_list_push(scratch.arena, state_list, State_InputLibs);
if (config->delay_load_dll_list.node_count) {
for (String8Node *delay_load_dll_node = config->delay_load_dll_list.first;
delay_load_dll_node != 0;
delay_load_dll_node = delay_load_dll_node->next) {
hash_table_push_path_u64(scratch.arena, delay_load_dll_ht, delay_load_dll_node->string, 0);
}
state_list_push(scratch.arena, state_list, State_PushDllHelperUndefSymbol);
}
if (config->guard_flags != LNK_Guard_None) {
state_list_push(scratch.arena, state_list, State_PushLoadConfigUndefSymbol);
}
//
// Run states
//
for (;;) { for (;;) {
for (; state_list.count > 0; ) { for (; state_list.count > 0; ) {
enum State state = state_list_pop(state_list); enum State state = state_list_pop(state_list);
@@ -1410,6 +1390,10 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
continue; continue;
} }
// skip duplicate import inputs
if (hash_table_search_string_raw(import_stub_ht, import_header.func_name, 0)) { continue; }
hash_table_push_string_raw(ht_arena, import_stub_ht, import_header.func_name, 0);
// create import stubs (later replaced with acutal imports generated by linker) // create import stubs (later replaced with acutal imports generated by linker)
LNK_Symbol *import_stub = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit(LNK_IMPORT_STUB)); LNK_Symbol *import_stub = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, str8_lit(LNK_IMPORT_STUB));
LNK_Symbol *thunk_symbol = lnk_make_defined_symbol(symtab->arena->v[0], import_header.func_name, import_stub->u.defined.obj, import_stub->u.defined.symbol_idx); LNK_Symbol *thunk_symbol = lnk_make_defined_symbol(symtab->arena->v[0], import_header.func_name, import_stub->u.defined.obj, import_stub->u.defined.symbol_idx);
@@ -1418,15 +1402,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
lnk_symbol_table_push(symtab, LNK_SymbolScope_Defined, imp_symbol); lnk_symbol_table_push(symtab, LNK_SymbolScope_Defined, imp_symbol);
// pick imports hash table // pick imports hash table
HashTable *imports_ht; HashTable *imports_ht = lnk_is_dll_delay_load(config, import_header.dll_name) ? delayed_imports : static_imports;
{
B32 is_delay_load_dll = hash_table_search_path_u64(delay_load_dll_ht, import_header.dll_name, 0);
if (is_delay_load_dll) {
imports_ht = delayed_imports;
} else {
imports_ht = static_imports;
}
}
// search DLL symbol list // search DLL symbol list
String8List *import_symbols = hash_table_search_path_raw(imports_ht, import_header.dll_name); String8List *import_symbols = hash_table_search_path_raw(imports_ht, import_header.dll_name);
@@ -1444,8 +1420,8 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
ProfEnd(); ProfEnd();
} break; } break;
case State_InputSymbols: { case State_InputInclude: {
ProfBegin("Input Symbols"); ProfBegin("Input Include");
// push a relocation which references an undefined include symbol // push a relocation which references an undefined include symbol
COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_Unknown); COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_Unknown);
@@ -1524,7 +1500,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
// if the machine was omitted on the command line, derive machine from obj // if the machine was omitted on the command line, derive machine from obj
if (config->machine == COFF_MachineType_Unknown) { if (config->machine == COFF_MachineType_Unknown) {
for (U64 obj_idx = 0; obj_idx < obj_node_arr.count; obj_idx += 1) { for EachIndex(obj_idx, obj_node_arr.count) {
if (obj_node_arr.v[obj_idx].data.header.machine != COFF_MachineType_Unknown) { if (obj_node_arr.v[obj_idx].data.header.machine != COFF_MachineType_Unknown) {
config->machine = obj_node_arr.v[obj_idx].data.header.machine; config->machine = obj_node_arr.v[obj_idx].data.header.machine;
break; break;
@@ -1532,12 +1508,6 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
} }
} }
// infer minimal padding size for functions from the target machine
if (config->machine != COFF_MachineType_Unknown && config->infer_function_pad_min) {
config->function_pad_min = lnk_get_default_function_pad_min(config->machine);
config->infer_function_pad_min = 0;
}
ProfBegin("Apply Directives"); ProfBegin("Apply Directives");
for EachIndex(obj_idx, obj_node_arr.count) { for EachIndex(obj_idx, obj_node_arr.count) {
LNK_Obj *obj = &obj_node_arr.v[obj_idx].data; LNK_Obj *obj = &obj_node_arr.v[obj_idx].data;
@@ -1555,7 +1525,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
lnk_input_obj_symbols(tp, tp_arena, symtab, obj_node_arr); lnk_input_obj_symbols(tp, tp_arena, symtab, obj_node_arr);
// schedule symbol input // schedule symbol input
pending_refs = 1; search_flags = SearchFlag_All;
// reset input objs // reset input objs
MemoryZeroStruct(&input_obj_list); MemoryZeroStruct(&input_obj_list);
@@ -1641,7 +1611,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
if (lnk_get_log_status(LNK_Log_InputLib)) { if (lnk_get_log_status(LNK_Log_InputLib)) {
if (libs.count > 0) { if (libs.count > 0) {
U64 input_size = 0; U64 input_size = 0;
for (U64 i = 0; i < libs.count; ++i) { input_size += libs.v[i].data.data.size; } for EachIndex(i, libs.count) { input_size += libs.v[i].data.data.size; }
lnk_log(LNK_Log_InputObj, "[ Lib Input Size %M ]", input_size); lnk_log(LNK_Log_InputObj, "[ Lib Input Size %M ]", input_size);
} }
} }
@@ -1651,76 +1621,129 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
} }
// schedule symbol input // schedule symbol input
pending_refs = 1; search_flags = SearchFlag_All;
ProfEnd(); ProfEnd();
} break; } break;
case State_InputAlternateNames: { case State_InputAlternateNames: {
ProfBegin("Input Alternate Names"); ProfBegin("Input Alternate Names");
COFF_ObjWriter *obj_writer = 0;
for (; *last_alt_name; last_alt_name = &(*last_alt_name)->next) { // linker is not allowed to create a new alternate name if the "from" symbol already exists
// make object writer if it was reset // (MSVC silently ignores the directive)
if (obj_writer == 0) { {
obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_Unknown); LNK_Symbol *extant_from = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, (*last_alt_name)->data.from);
if (extant_from) {
COFF_SymbolValueInterpType extant_interp = lnk_interp_from_symbol(extant_from);
if (extant_interp != COFF_SymbolValueInterp_Undefined) { break; }
}
} }
// append weak symbol // make & input alternate name obj
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); COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_Unknown);
COFF_ObjSymbol *null_symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit(LNK_NULL_SYMBOL));
COFF_ObjSymbol *from_symbol = coff_obj_writer_push_symbol_weak(obj_writer, (*last_alt_name)->data.from, COFF_WeakExt_Rad_WeakSearchLibrary, null_symbol);
COFF_ObjSymbol *to_symbol = coff_obj_writer_push_symbol_weak(obj_writer, (*last_alt_name)->data.to, COFF_WeakExt_Rad_WeakSearchLibrary, from_symbol);
String8 alt_name_obj = coff_obj_writer_serialize(tp_arena->v[0], obj_writer);
coff_obj_writer_release(&obj_writer);
// 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); 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->path = (*last_alt_name)->data.obj ? (*last_alt_name)->data.obj->path : str8_lit("RADLINK");
input->exclude_from_debug_info = 1; input->exclude_from_debug_info = 1;
input->dedup_id = push_str8f(scratch.arena, "* ALTERNATE NAMES FOR %S *", input->path); input->dedup_id = push_str8f(scratch.arena, "* ALTERNATE NAMES FOR %S * %u", input->path, obj_list.count);
input->data = coff_obj_writer_serialize(tp_arena->v[0], obj_writer); input->data = alt_name_obj;
input->lib = (*last_alt_name)->data.obj ? (*last_alt_name)->data.obj->lib : 0; input->lib = (*last_alt_name)->data.obj ? (*last_alt_name)->data.obj->lib : 0;
}
// advance to next alt name
last_alt_name = &(*last_alt_name)->next;
// reset obj writer
coff_obj_writer_release(&obj_writer);
obj_writer = 0;
}
}
ProfEnd(); ProfEnd();
} break; } break;
case State_PushDllHelperUndefSymbol: { case State_InputDelayLoadDlls: {
ProfBegin("Push Dll Helper Undef Symbol"); ProfBegin("Input Delay Load Dlls");
delay_load_helper_name = mscrt_delay_load_helper_name_from_machine(config->machine);
for (; *last_delay_load_dll; last_delay_load_dll = &(*last_delay_load_dll)->next);
// TODO: config_refactor // TODO: config_refactor
String8List value_strings = {0}; String8List value_strings = {0};
str8_list_push(scratch.arena, &value_strings, delay_load_helper_name); str8_list_push(scratch.arena, &value_strings, config->delay_load_helper_name);
lnk_apply_cmd_option_to_config(tp_arena->v[0], config, str8_lit("include"), value_strings, 0); lnk_apply_cmd_option_to_config(tp_arena->v[0], config, str8_lit("include"), value_strings, 0);
ProfEnd(); ProfEnd();
} break; } break;
case State_PushLoadConfigUndefSymbol: { case State_SearchUndefined: {
ProfBegin("Push Load Config Undef Symbol"); ProfBegin("Search Undefined");
String8 load_config_name = str8_lit(MSCRT_LOAD_CONFIG_SYMBOL_NAME); for EachIndex(worker_id, symtab->arena->count) {
for (LNK_SymbolHashTrieChunk *c = symtab->chunk_lists[LNK_SymbolScope_Defined][worker_id].first; c != 0; c = c->next) {
// TODO: config_refactor for EachIndex(i, c->count) {
String8List value_strings = {0}; LNK_Symbol *symbol = c->v[i].symbol;
str8_list_push(scratch.arena, &value_strings, load_config_name); COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_defined(symbol);
lnk_apply_cmd_option_to_config(tp_arena->v[0], config, str8_lit("include"), value_strings, 0); COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol_parsed);
if (symbol_interp == COFF_SymbolValueInterp_Undefined) {
LNK_Symbol *member_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Lib, symbol->name);
if (member_symbol) {
lnk_queue_lib_member_for_input(scratch.arena, config, symbol, member_symbol, &input_import_list, &input_obj_list);
}
}
}
}
}
ProfEnd(); ProfEnd();
} break; } break;
case State_FindRefs: { case State_SearchWeak: {
ProfBegin("Find Refs"); ProfBegin("Search Weak");
for EachIndex(worker_id, symtab->arena->count) {
LNK_InputImportList new_imports = {0}; for (LNK_SymbolHashTrieChunk *c = symtab->chunk_lists[LNK_SymbolScope_Defined][worker_id].first; c != 0; c = c->next) {
LNK_InputObjList new_objs = {0}; for EachIndex(i, c->count) {
lnk_find_refs(scratch.arena, tp, symtab, config, obj_list, &new_imports, &new_objs); LNK_Symbol *symbol = c->v[i].symbol;
COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_defined(symbol);
// schedule new inputs COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol_parsed);
lnk_input_import_list_concat_in_place(&input_import_list, &new_imports); if (symbol_interp == COFF_SymbolValueInterp_Weak) {
lnk_input_obj_list_concat_in_place(&input_obj_list, &new_objs); COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(symbol_parsed, symbol->u.defined.obj->header.is_big_obj);
if (weak_ext->characteristics == COFF_WeakExt_SearchLibrary) {
LNK_Symbol *member_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Lib, symbol->name);
if (member_symbol) {
lnk_queue_lib_member_for_input(scratch.arena, config, symbol, member_symbol, &input_import_list, &input_obj_list);
}
}
}
}
}
}
ProfEnd(); ProfEnd();
} break; } break;
case State_LookupEntryPoint: { case State_SearchWeakAntiDep: {
ProfBegin("Lookup Entry Point"); ProfBegin("Search Weak AntiDep");
for EachIndex(worker_id, symtab->arena->count) {
for (LNK_SymbolHashTrieChunk *c = symtab->chunk_lists[LNK_SymbolScope_Defined][worker_id].first; c != 0; c = c->next) {
for EachIndex(i, c->count) {
LNK_Symbol *symbol = c->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) {
LNK_SymbolDefined dep_symbol = lnk_resolve_weak_symbol(symtab, symbol->u.defined);
COFF_ParsedSymbol dep_parsed = lnk_parsed_symbol_from_coff_symbol_idx(dep_symbol.obj, dep_symbol.symbol_idx);
COFF_SymbolValueInterpType dep_interp = coff_interp_from_parsed_symbol(dep_parsed);
if (dep_interp == COFF_SymbolValueInterp_Weak) {
LNK_Symbol *member_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Lib, symbol_parsed.name);
if (member_symbol) {
lnk_queue_lib_member_for_input(scratch.arena, config, symbol, member_symbol, &input_import_list, &input_obj_list);
}
}
}
}
}
}
}
ProfEnd();
} break;
case State_SearchEntryPoint: {
ProfBegin("Search Entry Point");
LNK_Symbol *entry_point_symbol = 0; LNK_Symbol *entry_point_symbol = 0;
B32 is_entry_point_unspecified = config->entry_point_name.size == 0; B32 is_entry_point_unspecified = config->entry_point_name.size == 0;
@@ -1729,9 +1752,9 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
// we don't have a subsystem and entry point name, // we don't have a subsystem and entry point name,
// so we loop over every subsystem and search potential entry // so we loop over every subsystem and search potential entry
// points in the symbol table // points in the symbol table
for (U64 subsys_idx = 0; subsys_idx < PE_WindowsSubsystem_COUNT; subsys_idx += 1) { for EachIndex(subsys_idx, PE_WindowsSubsystem_COUNT) {
String8Array name_arr = pe_get_entry_point_names(config->machine, (PE_WindowsSubsystem)subsys_idx, config->file_characteristics); String8Array name_arr = pe_get_entry_point_names(config->machine, (PE_WindowsSubsystem)subsys_idx, config->file_characteristics);
for (U64 entry_idx = 0; entry_idx < name_arr.count; entry_idx += 1) { for EachIndex(entry_idx, name_arr.count) {
entry_point_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, name_arr.v[entry_idx]); entry_point_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, name_arr.v[entry_idx]);
if (entry_point_symbol) { if (entry_point_symbol) {
config->subsystem = (PE_WindowsSubsystem)subsys_idx; config->subsystem = (PE_WindowsSubsystem)subsys_idx;
@@ -1742,9 +1765,9 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
// search for potential entry points in libs // search for potential entry points in libs
if (!entry_point_symbol) { if (!entry_point_symbol) {
for (U64 subsys_idx = 0; subsys_idx < PE_WindowsSubsystem_COUNT; subsys_idx += 1) { for EachIndex(subsys_idx, PE_WindowsSubsystem_COUNT) {
String8Array name_arr = pe_get_entry_point_names(config->machine, (PE_WindowsSubsystem)subsys_idx, config->file_characteristics); String8Array name_arr = pe_get_entry_point_names(config->machine, (PE_WindowsSubsystem)subsys_idx, config->file_characteristics);
for (U64 entry_idx = 0; entry_idx < name_arr.count; entry_idx += 1) { for EachIndex(entry_idx, name_arr.count) {
entry_point_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Lib, name_arr.v[entry_idx]); entry_point_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Lib, name_arr.v[entry_idx]);
if (entry_point_symbol) { if (entry_point_symbol) {
config->subsystem = (PE_WindowsSubsystem)subsys_idx; config->subsystem = (PE_WindowsSubsystem)subsys_idx;
@@ -1759,7 +1782,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
// we have subsystem but no entry point name, get potential entry point names // we have subsystem but no entry point name, get potential entry point names
// and see which is in the symbol table // and see which is in the symbol table
String8Array name_arr = pe_get_entry_point_names(config->machine, config->subsystem, config->file_characteristics); String8Array name_arr = pe_get_entry_point_names(config->machine, config->subsystem, config->file_characteristics);
for (U64 entry_idx = 0; entry_idx < name_arr.count; entry_idx += 1) { for EachIndex(entry_idx, name_arr.count) {
LNK_Symbol *symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, name_arr.v[entry_idx]); LNK_Symbol *symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, name_arr.v[entry_idx]);
if (symbol) { if (symbol) {
if (entry_point_symbol) { if (entry_point_symbol) {
@@ -1775,7 +1798,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
// search for entry point in libs // search for entry point in libs
if (!entry_point_symbol) { if (!entry_point_symbol) {
for (U64 entry_idx = 0; entry_idx < name_arr.count; entry_idx += 1) { for EachIndex(entry_idx, name_arr.count) {
entry_point_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Lib, name_arr.v[entry_idx]); entry_point_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Lib, name_arr.v[entry_idx]);
if (entry_point_symbol) { if (entry_point_symbol) {
break; break;
@@ -1869,11 +1892,11 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
String8 *dll_names = keys_from_hash_table_string(scratch.arena, delayed_imports); String8 *dll_names = keys_from_hash_table_string(scratch.arena, delayed_imports);
String8List **dll_import_headers = values_from_hash_table_raw(scratch.arena, delayed_imports); String8List **dll_import_headers = values_from_hash_table_raw(scratch.arena, delayed_imports);
for (U64 dll_idx = 0; dll_idx < delayed_imports->count; dll_idx += 1) { for EachIndex(dll_idx, delayed_imports->count) {
String8 import_debug_symbols = lnk_make_dll_import_debug_symbols(scratch.arena, config->machine, dll_names[dll_idx]); String8 import_debug_symbols = lnk_make_dll_import_debug_symbols(scratch.arena, config->machine, dll_names[dll_idx]);
LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list);
input->input_idx = obj_list.count; input->input_idx = obj_list.count;
input->data = pe_make_import_dll_obj_delayed(tp_arena->v[0], time_stamp, config->machine, dll_names[dll_idx], delay_load_helper_name, import_debug_symbols, *dll_import_headers[dll_idx], emit_biat, emit_uiat); input->data = pe_make_import_dll_obj_delayed(tp_arena->v[0], time_stamp, config->machine, dll_names[dll_idx], config->delay_load_helper_name, import_debug_symbols, *dll_import_headers[dll_idx], emit_biat, emit_uiat);
input->path = dll_names[dll_idx]; input->path = dll_names[dll_idx];
input->dedup_id = input->path; input->dedup_id = input->path;
} }
@@ -1905,7 +1928,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
COFF_TimeStamp time_stamp = COFF_TimeStamp_Max; COFF_TimeStamp time_stamp = COFF_TimeStamp_Max;
String8 *dll_names = keys_from_hash_table_string(scratch.arena, static_imports); String8 *dll_names = keys_from_hash_table_string(scratch.arena, static_imports);
String8List **dll_import_headers = values_from_hash_table_raw(scratch.arena, static_imports); String8List **dll_import_headers = values_from_hash_table_raw(scratch.arena, static_imports);
for (U64 dll_idx = 0; dll_idx < static_imports->count; dll_idx += 1) { for EachIndex(dll_idx, static_imports->count) {
String8 import_debug_symbols = lnk_make_dll_import_debug_symbols(scratch.arena, config->machine, dll_names[dll_idx]); String8 import_debug_symbols = lnk_make_dll_import_debug_symbols(scratch.arena, config->machine, dll_names[dll_idx]);
LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list); LNK_InputObj *input = lnk_input_obj_list_push(scratch.arena, &input_obj_list);
input->input_idx = obj_list.count; input->input_idx = obj_list.count;
@@ -2090,7 +2113,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
} }
} }
if (*last_disallow_lib != 0) { if (*last_disallow_lib) {
state_list_push(scratch.arena, state_list, State_InputDisallowLibs); state_list_push(scratch.arena, state_list, State_InputDisallowLibs);
continue; continue;
} }
@@ -2098,17 +2121,17 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
state_list_push(scratch.arena, state_list, State_InputImports); state_list_push(scratch.arena, state_list, State_InputImports);
continue; continue;
} }
if (*last_include_symbol != 0) { if (*last_include_symbol) {
state_list_push(scratch.arena, state_list, State_InputSymbols); state_list_push(scratch.arena, state_list, State_InputInclude);
continue; continue;
} }
if (*last_alt_name != 0) { if (*last_alt_name) {
state_list_push(scratch.arena, state_list, State_InputAlternateNames); state_list_push(scratch.arena, state_list, State_InputAlternateNames);
continue; continue;
} }
{ {
B32 have_pending_lib_inputs = 0; B32 have_pending_lib_inputs = 0;
for (U64 i = 0; i < ArrayCount(input_libs); ++i) { for EachIndex(i, ArrayCount(input_libs)) {
if (*input_libs[i] != 0) { if (*input_libs[i] != 0) {
have_pending_lib_inputs = 1; have_pending_lib_inputs = 1;
break; break;
@@ -2123,14 +2146,28 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
state_list_push(scratch.arena, state_list, State_InputObjs); state_list_push(scratch.arena, state_list, State_InputObjs);
continue; continue;
} }
if (pending_refs) { if (*last_delay_load_dll) {
pending_refs = 0; state_list_push(scratch.arena, state_list, State_InputDelayLoadDlls);
state_list_push(scratch.arena, state_list, State_FindRefs);
continue; continue;
} }
if (entry_point_lookup_attempts == 0) { if (search_flags & SearchFlag_Undefined) {
state_list_push(scratch.arena, state_list, State_LookupEntryPoint); search_flags &= ~SearchFlag_Undefined;
entry_point_lookup_attempts += 1; state_list_push(scratch.arena, state_list, State_SearchUndefined);
continue;
}
if (search_flags & SearchFlag_Weak) {
search_flags &= ~SearchFlag_Weak;
state_list_push(scratch.arena, state_list, State_SearchWeak);
continue;
}
if (search_flags & SearchFlag_WeakAntiDep) {
search_flags &= ~SearchFlag_WeakAntiDep;
state_list_push(scratch.arena, state_list, State_SearchWeakAntiDep);
continue;
}
if (entry_point_search_attempts == 0) {
state_list_push(scratch.arena, state_list, State_SearchEntryPoint);
entry_point_search_attempts += 1;
continue; continue;
} }
if (input_linker_objs) { if (input_linker_objs) {
@@ -2142,104 +2179,83 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
break; break;
} }
ProfBegin("Report Unresolved Symbols");
{ {
U64 chunks_count = 0; U64 chunks_count = 0;
LNK_SymbolHashTrieChunk **chunks = lnk_array_from_symbol_hash_trie_chunk_list(scratch.arena, symtab->chunk_lists[LNK_SymbolScope_Defined], tp->worker_count, &chunks_count); LNK_SymbolHashTrieChunk **chunks = lnk_array_from_symbol_hash_trie_chunk_list(scratch.arena, symtab->chunk_lists[LNK_SymbolScope_Defined], symtab->arena->count, &chunks_count);
U64 undefs_count = 0; ProfBegin("Replace Unresolved Weak Symbols With Defualt Symbol");
{
LNK_ReplaceWeakSymbolsWithDefaultSymbolTask task = { .symtab = symtab, .chunks = chunks };
tp_for_parallel(tp, 0, chunks_count, lnk_replace_weak_with_default_symbol_task, &task);
#if BUILD_DEBUG
for EachIndex(chunk_idx, chunks_count) { for EachIndex(chunk_idx, chunks_count) {
LNK_SymbolHashTrieChunk *chunk = chunks[chunk_idx]; LNK_SymbolHashTrieChunk *chunk = chunks[chunk_idx];
for EachIndex(i, chunk->count) { for EachIndex(i, chunk->count) {
LNK_Symbol *symbol = chunk->v[i].symbol; LNK_Symbol *symbol = chunk->v[i].symbol;
if (symbol->is_live) { COFF_SymbolValueInterpType symbol_interp = lnk_interp_from_symbol(symbol);
COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_defined(symbol); AssertAlways(symbol_interp != COFF_SymbolValueInterp_Weak);
if (coff_is_undefined_data_symbol(symbol_parsed)) {
undefs_count += 1;
}
} }
} }
#endif
} }
ProfEnd();
U64 undefs_cursor = 0; ProfBegin("Report Unresolved Symbols");
LNK_Symbol **undefs = push_array(scratch.arena, LNK_Symbol *, undefs_count); {
U64 count = 0;
for EachIndex(chunk_idx, chunks_count) { for EachIndex(chunk_idx, chunks_count) {
LNK_SymbolHashTrieChunk *chunk = chunks[chunk_idx]; LNK_SymbolHashTrieChunk *chunk = chunks[chunk_idx];
for EachIndex(i, chunk->count) { for EachIndex(i, chunk->count) {
LNK_Symbol *symbol = chunk->v[i].symbol; LNK_Symbol *symbol = chunk->v[i].symbol;
if (symbol->is_live) { COFF_SymbolValueInterpType symbol_interp = lnk_interp_from_symbol(symbol);
COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_defined(symbol); if (symbol_interp == COFF_SymbolValueInterp_Undefined) {
if (coff_is_undefined_data_symbol(symbol_parsed)) { count += 1;
undefs[undefs_cursor++] = chunk->v[i].symbol;
}
} }
} }
} }
radsort(undefs, undefs_count, lnk_symbol_defined_ptr_is_before); U64 cursor = 0;
LNK_Symbol **unresolved = push_array(scratch.arena, LNK_Symbol *, count);
for EachIndex(i, undefs_count) { for EachIndex(chunk_idx, chunks_count) {
LNK_Symbol *s = undefs[i]; LNK_SymbolHashTrieChunk *chunk = chunks[chunk_idx];
lnk_error_obj(LNK_Error_UnresolvedSymbol, s->u.defined.obj, "unresolved symbol %S", s->name); for EachIndex(i, chunk->count) {
LNK_Symbol *symbol = chunk->v[i].symbol;
COFF_SymbolValueInterpType symbol_interp = lnk_interp_from_symbol(symbol);
if (symbol_interp == COFF_SymbolValueInterp_Undefined) {
unresolved[cursor++] = chunk->v[i].symbol;
}
}
} }
if (undefs_count > 0) { radsort(unresolved, count, lnk_symbol_defined_ptr_is_before);
for EachIndex(i, count) {
LNK_Symbol *symbol = unresolved[i];
lnk_error_obj(LNK_Error_UnresolvedSymbol, symbol->u.defined.obj, "unresolved symbol %S", symbol->name);
}
// TODO: /FORCE
if (count) {
lnk_exit(LNK_Error_UnresolvedSymbol); lnk_exit(LNK_Error_UnresolvedSymbol);
} }
} }
ProfEnd(); ProfEnd();
}
ProfBegin("Remove Unreachable Sections"); //
{ // discard COMDAT sections that are not referenced
for (LNK_ObjNode *obj_n = obj_list.first; obj_n != 0; obj_n = obj_n->next) { //
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 (lnk_is_coff_section_debug(obj, sect_idx)) { continue; }
if (config->opt_ref == LNK_SwitchState_Yes) { if (config->opt_ref == LNK_SwitchState_Yes) {
// remove unreferenced sections lnk_opt_ref(tp, symtab, config, obj_list);
if (~section_header->flags & LNK_SECTION_FLAG_IS_LIVE && section_header->flags & COFF_SectionFlag_LnkCOMDAT) {
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_IS_LIVE;
if (section_header->flags & COFF_SectionFlag_LnkRemove) {
for (U32Node *section_number_n = obj->associated_sections[section_number]; section_number_n != 0; section_number_n = section_number_n->next) {
U32 section_number = section_number_n->data;
COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, section_number);
section_header->flags |= COFF_SectionFlag_LnkRemove;
}
}
}
}
}
ProfEnd();
ProfBegin("Replace Unresolved Weak Symbols With Defualt Symbol");
{
U64 chunks_count = 0;
LNK_SymbolHashTrieChunk **chunks = lnk_array_from_symbol_hash_trie_chunk_list(scratch.arena, symtab->chunk_lists[LNK_SymbolScope_Defined], symtab->arena->count, &chunks_count);
LNK_ReplaceWeakSymbolsWithDefaultSymbolTask task = { .symtab = symtab, .chunks = chunks };
tp_for_parallel(tp, 0, chunks_count, lnk_replace_weak_symbols_with_default_symbol_task, &task);
}
ProfEnd();
// //
// fill out link context // infer minimal padding size for functions from the target machine
// //
LNK_LinkContext link_ctx = {0}; if (config->machine != COFF_MachineType_Unknown && config->infer_function_pad_min) {
link_ctx.symtab = symtab; config->function_pad_min = lnk_get_default_function_pad_min(config->machine);
link_ctx.objs_count = obj_list.count; config->infer_function_pad_min = 0;
link_ctx.objs = lnk_array_from_obj_list(tp_arena->v[0], obj_list); }
MemoryCopyTyped(&link_ctx.lib_index[0], &lib_index[0], ArrayCount(lib_index));
// //
// log // log
@@ -2251,13 +2267,22 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
} }
if (lnk_get_log_status(LNK_Log_InputLib)) { if (lnk_get_log_status(LNK_Log_InputLib)) {
U64 total_input_size = 0; U64 total_input_size = 0;
for (U64 i = 0; i < ArrayCount(lib_index); ++i) { for EachIndex(i, ArrayCount(lib_index)) {
LNK_LibList list = lib_index[i]; LNK_LibList list = lib_index[i];
for (LNK_LibNode *lib_n = list.first; lib_n != 0; lib_n = lib_n->next) { total_input_size += lib_n->data.data.size; } for (LNK_LibNode *lib_n = list.first; lib_n != 0; lib_n = lib_n->next) { total_input_size += lib_n->data.data.size; }
} }
lnk_log(LNK_Log_InputLib, "[Total Lib Input Size %M]", total_input_size); lnk_log(LNK_Log_InputLib, "[Total Lib Input Size %M]", total_input_size);
} }
//
// fill out link context
//
LNK_LinkContext link_ctx = {0};
link_ctx.symtab = symtab;
link_ctx.objs_count = obj_list.count;
link_ctx.objs = lnk_array_from_obj_list(tp_arena->v[0], obj_list);
MemoryCopyTyped(&link_ctx.lib_index[0], &lib_index[0], ArrayCount(lib_index));
ProfEnd(); ProfEnd();
scratch_end(scratch); scratch_end(scratch);
return link_ctx; return link_ctx;
@@ -2410,25 +2435,6 @@ THREAD_POOL_TASK_FUNC(lnk_gather_section_contribs_task)
scratch_end(scratch); 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 internal
THREAD_POOL_TASK_FUNC(lnk_set_comdat_leaders_contribs_task) THREAD_POOL_TASK_FUNC(lnk_set_comdat_leaders_contribs_task)
{ {
@@ -2452,6 +2458,25 @@ THREAD_POOL_TASK_FUNC(lnk_set_comdat_leaders_contribs_task)
ProfEnd(); ProfEnd();
} }
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 internal
THREAD_POOL_TASK_FUNC(lnk_patch_comdat_leaders_task) THREAD_POOL_TASK_FUNC(lnk_patch_comdat_leaders_task)
{ {
@@ -2676,14 +2701,10 @@ lnk_patch_obj_symtab(LNK_SymbolTable *symtab, LNK_Obj *obj, B8 *was_symbol_patch
if (is_resolved) { if (is_resolved) {
COFF_ParsedSymbol fixup_src = lnk_parsed_symbol_from_coff_symbol_idx(fixup_symbol.obj, fixup_symbol.symbol_idx); COFF_ParsedSymbol fixup_src = lnk_parsed_symbol_from_coff_symbol_idx(fixup_symbol.obj, fixup_symbol.symbol_idx);
COFF_SymbolValueInterpType fixup_type = coff_interp_symbol(fixup_src.section_number, fixup_src.value, fixup_src.storage_class); COFF_SymbolValueInterpType fixup_type = coff_interp_symbol(fixup_src.section_number, fixup_src.value, fixup_src.storage_class);
AssertAlways(fixup_type == COFF_SymbolValueInterp_Regular ||
fixup_type == COFF_SymbolValueInterp_Abs ||
fixup_type == COFF_SymbolValueInterp_Common ||
fixup_type == COFF_SymbolValueInterp_Undefined);
U32 section_number; U32 section_number;
U32 value; U32 value;
if (fixup_type == COFF_SymbolValueInterp_Undefined) { if (fixup_type == COFF_SymbolValueInterp_Undefined || fixup_type == COFF_SymbolValueInterp_Weak) {
section_number = lnk_obj_get_removed_section_number(obj); section_number = lnk_obj_get_removed_section_number(obj);
value = 0; value = 0;
} else { } else {