progress on /opt:ref

Link the debugger and strip unreferenced sections
TODO: Mark referenced undefined symbols for unresolved symbol reporting
This commit is contained in:
Nikita Smith
2025-08-07 17:36:11 -07:00
parent e1d6b9f319
commit 9a1f96bda2
6 changed files with 621 additions and 656 deletions
+323 -382
View File
@@ -964,16 +964,19 @@ lnk_make_linker_obj(Arena *arena, LNK_Config *config)
internal void internal void
lnk_queue_lib_member_input(Arena *arena, lnk_queue_lib_member_input(Arena *arena,
PathStyle path_style, LNK_Config *config,
LNK_SymbolLib *symbol, LNK_Symbol *symbol,
LNK_InputImportList *input_import_list, LNK_InputImportList *input_import_list,
LNK_InputObjList *input_obj_list) LNK_InputObjList *input_obj_list)
{ {
LNK_Lib *lib = symbol->lib; B32 was_live = lnk_mark_symbol_live(symbol);
U64 input_idx = Compose64Bit(lib->input_idx, symbol->member_offset); if (!was_live) {
LNK_SymbolLib *member = &symbol->u.lib;
LNK_Lib *lib = member->lib;
U64 input_idx = Compose64Bit(lib->input_idx, member->member_offset);
// parse member // parse member
COFF_ArchiveMember member_info = coff_archive_member_from_offset(lib->data, symbol->member_offset); COFF_ArchiveMember member_info = coff_archive_member_from_offset(lib->data, member->member_offset);
COFF_DataType member_type = coff_data_type_from_data(member_info.data); COFF_DataType member_type = coff_data_type_from_data(member_info.data);
switch (member_type) { switch (member_type) {
@@ -1002,7 +1005,7 @@ lnk_queue_lib_member_input(Arena *arena,
String8List obj_path_list = {0}; String8List obj_path_list = {0};
str8_list_push(scratch.arena, &obj_path_list, str8_chop_last_slash(lib->path)); str8_list_push(scratch.arena, &obj_path_list, str8_chop_last_slash(lib->path));
str8_list_push(scratch.arena, &obj_path_list, obj_path); str8_list_push(scratch.arena, &obj_path_list, obj_path);
obj_path = str8_path_list_join_by_style(arena, &obj_path_list, path_style); obj_path = str8_path_list_join_by_style(arena, &obj_path_list, config->path_style);
scratch_end(scratch); scratch_end(scratch);
} }
@@ -1015,69 +1018,124 @@ lnk_queue_lib_member_input(Arena *arena,
input->input_idx = input_idx; input->input_idx = input_idx;
} break; } break;
} }
}
internal
THREAD_POOL_TASK_FUNC(lnk_undef_symbol_finder)
{
LNK_SymbolFinder *task = raw_task;
LNK_SymbolFinderResult *result = &task->result_arr[task_id];
Rng1U64 range = task->range_arr[task_id];
for (U64 symbol_idx = range.min; symbol_idx < range.max; symbol_idx += 1) {
LNK_SymbolNode *symbol_n = task->lookup_node_arr.v[symbol_idx];
LNK_Symbol *symbol = symbol_n->data;
LNK_Symbol *has_defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol->name);
if (has_defn) {
continue;
}
LNK_Symbol *member_symbol = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Lib, symbol->name);
if (member_symbol) {
lnk_queue_lib_member_input(arena, task->path_style, &member_symbol->u.lib, &result->input_import_list, &result->input_obj_list);
} else {
lnk_symbol_list_push_node(&result->unresolved_symbol_list, symbol_n);
}
} }
} }
internal internal void
THREAD_POOL_TASK_FUNC(lnk_weak_symbol_finder) lnk_find_refs(Arena *arena,
TP_Context *tp,
LNK_SymbolTable *symtab,
LNK_Config *config,
LNK_ObjList objs,
LNK_InputImportList *imports_out,
LNK_InputObjList *objs_out)
{ {
LNK_SymbolFinder *task = raw_task; ProfBeginFunction();
LNK_SymbolFinderResult *result = &task->result_arr[task_id]; Temp scratch = scratch_begin(&arena,1);
Rng1U64 range = task->range_arr[task_id];
for (U64 symbol_idx = range.min; symbol_idx < range.max; symbol_idx += 1) { struct Task { struct Task *next; LNK_Obj *obj; COFF_RelocArray relocs; U32 section_number; };
LNK_SymbolNode *symbol_n = task->lookup_node_arr.v[symbol_idx]; struct Task *task_stack = 0;
LNK_Symbol *symbol = symbol_n->data; const U64 RELOCS_PER_TASK = 1024;
LNK_Symbol *defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol->name); //
if (defn) { // reset live flag on sections
//
for (LNK_ObjNode *obj_n = objs.first; obj_n != 0; obj_n = obj_n->next) {
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_IS_LIVE;
}
}
//
// define roots
//
{
String8List roots = str8_list_copy(scratch.arena, &config->include_symbol_list);
// tls
LNK_Symbol *tls_symbol = lnk_symbol_table_searchf(symtab, LNK_SymbolScope_Defined, MSCRT_TLS_SYMBOL_NAME);
if (tls_symbol) {
str8_list_pushf(scratch.arena, &roots, MSCRT_TLS_SYMBOL_NAME);
}
// push tasks for each root symbol
for (String8Node *root_n = roots.first; root_n != 0; root_n = root_n->next) {
LNK_Symbol *root = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, root_n->string);
struct Task *t = push_array(scratch.arena, struct Task, 1);
t->obj = root->u.defined.obj;
t->relocs.count = 1;
t->relocs.v = push_array(scratch.arena, COFF_Reloc, 1);
t->relocs.v[0].isymbol = root->u.defined.symbol_idx;
SLLStackPush(task_stack, t);
}
// push task for every non-COMDAT section
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);
// is section eligible for walking?
if (lnk_is_coff_section_debug(obj, sect_idx)) { continue; }
if (section_header->flags & COFF_SectionFlag_LnkRemove) { continue; }
if (config->opt_ref == LNK_SwitchState_Yes && section_header->flags & COFF_SectionFlag_LnkCOMDAT) { 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, RELOCS_PER_TASK);
struct Task *new_tasks = push_array(scratch.arena, struct Task, new_task_count);
for EachIndex(new_task_idx, new_task_count) {
struct Task *t = new_tasks + new_task_idx;
t->obj = obj;
t->relocs.count = Min(RELOCS_PER_TASK, relocs.count - (new_task_idx * RELOCS_PER_TASK));
t->relocs.v = relocs.v + (new_task_idx * RELOCS_PER_TASK);
t->section_number = section_number;
SLLStackPush(task_stack, t);
}
}
}
}
//
// walk relocations and unset the remove flag on visited sections
//
for (; task_stack; ) {
struct Task *t = task_stack; SLLStackPop(task_stack);
for EachIndex(reloc_idx, t->relocs.count) {
COFF_Reloc *reloc = &t->relocs.v[reloc_idx];
LNK_SymbolDefined reloc_defn = (LNK_SymbolDefined){ .obj = t->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);
LNK_SymbolDefined 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);
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_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_defined(defn);
COFF_SymbolValueInterpType defn_interp = coff_interp_from_parsed_symbol(defn_parsed); COFF_SymbolValueInterpType defn_interp = coff_interp_from_parsed_symbol(defn_parsed);
if (defn_interp != COFF_SymbolValueInterp_Weak) {
continue;
}
}
if (defn_interp == COFF_SymbolValueInterp_Weak) {
LNK_Symbol *member_symbol = 0; LNK_Symbol *member_symbol = 0;
{ COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(ref_parsed, ref_symbol.obj->header.is_big_obj);
COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx);
COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(parsed_symbol, symbol->u.defined.obj->header.is_big_obj);
switch (weak_ext->characteristics) { switch (weak_ext->characteristics) {
case COFF_WeakExt_NoLibrary: { case COFF_WeakExt_NoLibrary: {
// NOLIBRARY means weak symbol should be resolved in case where strong definition pulls in lib member. // NOLIBRARY means weak symbol should be resolved in case where strong definition pulls in lib member.
} break; } break;
case COFF_WeakExt_AntiDependency: case COFF_WeakExt_AntiDependency:
case COFF_WeakExt_SearchLibrary: { case COFF_WeakExt_SearchLibrary: {
member_symbol = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Lib, symbol->name); member_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Lib, ref_parsed.name);
} break; } break;
case COFF_WeakExt_SearchAlias: { case COFF_WeakExt_SearchAlias: {
member_symbol = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Lib, symbol->name); member_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Lib, ref_parsed.name);
if (member_symbol == 0) { if (member_symbol == 0) {
if (str8_match_lit(".weak.", symbol->name, StringMatchFlag_RightSideSloppy)) { if (str8_match_lit(".weak.", ref_parsed.name, StringMatchFlag_RightSideSloppy)) {
// TODO: Clang and MingGW encode extra info in alias // TODO: Clang and MingGW encode extra info in alias
// //
// __attribute__((weak,alias("foo"))) void bar(void); // __attribute__((weak,alias("foo"))) void bar(void);
@@ -1094,74 +1152,96 @@ THREAD_POOL_TASK_FUNC(lnk_weak_symbol_finder)
// In this case linker needs to parse .weak.bar.default.foo and search for bar and foo as well. // In this case linker needs to parse .weak.bar.default.foo and search for bar and foo as well.
Assert("TODO: MinGW weak symbol"); Assert("TODO: MinGW weak symbol");
} else { } else {
COFF_ParsedSymbol tag = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, weak_ext->tag_index); COFF_ParsedSymbol tag = lnk_parsed_symbol_from_coff_symbol_idx(ref_symbol.obj, weak_ext->tag_index);
member_symbol = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Lib, tag.name); member_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Lib, tag.name);
} }
} }
} break; } break;
} default: { NotImplemented; } break;
} }
member_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Lib, ref_parsed.name);
if (member_symbol) { if (member_symbol) {
lnk_queue_lib_member_input(arena, task->path_style, &member_symbol->u.lib, &result->input_import_list, &result->input_obj_list); lnk_queue_lib_member_input(arena, config, member_symbol, imports_out, objs_out);
MemoryZeroStruct(&ref_symbol);
break;
} else { } else {
lnk_symbol_list_push_node(&result->unresolved_symbol_list, symbol_n); ref_symbol = lnk_default_symbol_from_weak(symtab, ref_symbol);
}
} else {
ref_symbol = defn->u.defined;
} }
} }
} else if (ref_interp == COFF_SymbolValueInterp_Undefined) {
LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, ref_parsed.name);
internal LNK_SymbolFinderResult COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_defined(defn);
lnk_run_symbol_finder(TP_Context *tp, COFF_SymbolValueInterpType defn_interp = coff_interp_from_parsed_symbol(defn_parsed);
TP_Arena *arena, if (defn_interp == COFF_SymbolValueInterp_Undefined) {
LNK_Config *config, LNK_Symbol *member_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Lib, ref_parsed.name);
LNK_SymbolTable *symtab, if (member_symbol) {
LNK_SymbolList lookup_list, lnk_queue_lib_member_input(arena, config, member_symbol, imports_out, objs_out);
TP_TaskFunc *task_func) }
{ MemoryZeroStruct(&ref_symbol);
ProfBeginFunction(); break;
Temp scratch = scratch_begin(arena->v, arena->count); } else {
ref_symbol = defn->u.defined;
ProfBegin("Setup Task"); }
LNK_SymbolFinder task = {0}; } else if (ref_interp == COFF_SymbolValueInterp_Regular) {
task.path_style = config->path_style; LNK_Symbol *symlink = lnk_obj_get_comdat_symlink(ref_symbol.obj, ref_parsed.section_number);
task.symtab = symtab; if (symlink) {
task.lookup_node_arr = lnk_symbol_node_array_from_list(scratch.arena, lookup_list); ref_symbol = symlink->u.defined;
task.result_arr = push_array(scratch.arena, LNK_SymbolFinderResult, tp->worker_count); }
task.range_arr = tp_divide_work(scratch.arena, task.lookup_node_arr.count, tp->worker_count); break;
ProfEnd(); } else {
break;
ProfBegin("Run Task"); }
tp_for_parallel(tp, arena, tp->worker_count, task_func, &task);
ProfEnd();
ProfBegin("Concat Results");
LNK_SymbolFinderResult result = {0};
for (U64 i = 0; i < tp->worker_count; ++i) {
LNK_SymbolFinderResult *src = &task.result_arr[i];
lnk_symbol_list_concat_in_place(&result.unresolved_symbol_list, &src->unresolved_symbol_list);
lnk_input_obj_list_concat_in_place(&result.input_obj_list, &src->input_obj_list);
lnk_input_import_list_concat_in_place(&result.input_import_list, &src->input_import_list);
} }
ProfEnd();
// to get deterministic output accross multiple linker runs we have to sort inputs // skip unresolved symbol
ProfBegin("Sort Objs [Count %llu]", result.input_obj_list.count); if (ref_symbol.obj == 0) { continue; }
LNK_InputObj **input_obj_ptr_arr = lnk_array_from_input_obj_list(scratch.arena, result.input_obj_list);
qsort(input_obj_ptr_arr, result.input_obj_list.count, sizeof(input_obj_ptr_arr[0]), lnk_input_obj_compar);
//radsort(input_obj_ptr_arr, result.input_obj_list.count, lnk_input_obj_compar_is_before);
result.input_obj_list = lnk_list_from_input_obj_arr(input_obj_ptr_arr, result.input_obj_list.count);
ProfEnd();
ProfBegin("Sort Imports [Count %llu]", result.input_import_list.count); COFF_ParsedSymbol ref_parsed = lnk_parsed_symbol_from_coff_symbol_idx(ref_symbol.obj, ref_symbol.symbol_idx);
LNK_InputImportNode **input_imp_ptr_arr = lnk_input_import_arr_from_list(scratch.arena, result.input_import_list); COFF_SymbolValueInterpType ref_interp = coff_interp_from_parsed_symbol(ref_parsed);
//radsort(input_imp_ptr_arr, result.input_import_list.count, lnk_input_import_is_before); LNK_Obj *ref_obj = ref_symbol.obj;
qsort(input_imp_ptr_arr, result.input_import_list.count, sizeof(input_imp_ptr_arr[0]), lnk_input_import_node_compar);
result.input_import_list = lnk_list_from_input_import_arr(input_imp_ptr_arr, result.input_import_list.count); if (ref_interp == COFF_SymbolValueInterp_Regular) {
ProfEnd(); // 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];
// push section headers relocations to the task stack
for (U32Node *section_number_n = section_number_list; 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(ref_obj, section_number);
// is section eligible for walking?
if (section_header->flags & LNK_SECTION_FLAG_IS_LIVE) { continue; }
if (section_header->flags & COFF_SectionFlag_LnkRemove) { continue; }
if (lnk_is_coff_section_debug(ref_obj, section_number-1)) { continue; }
// mark section
section_header->flags |= LNK_SECTION_FLAG_IS_LIVE;
// divide relocs and push task for each reloc block
COFF_RelocArray relocs = lnk_coff_reloc_info_from_section_number(ref_obj, section_number);
U64 new_task_count = CeilIntegerDiv(relocs.count, RELOCS_PER_TASK);
struct Task *new_tasks = push_array(scratch.arena, struct Task, new_task_count);
for EachIndex(new_task_idx, new_task_count) {
struct Task *n = new_tasks + new_task_idx;
n->obj = ref_obj;
n->relocs.count = Min(RELOCS_PER_TASK, relocs.count - (new_task_idx * RELOCS_PER_TASK));
n->relocs.v = relocs.v + (new_task_idx * RELOCS_PER_TASK);
n->section_number = section_number;
SLLStackPush(task_stack, n);
}
}
}
}
}
scratch_end(scratch); scratch_end(scratch);
ProfEnd(); ProfEnd();
return result;
} }
internal LNK_LinkContext internal LNK_LinkContext
@@ -1178,8 +1258,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
State_PushDllHelperUndefSymbol, State_PushDllHelperUndefSymbol,
State_InputLinkerObjs, State_InputLinkerObjs,
State_PushLoadConfigUndefSymbol, State_PushLoadConfigUndefSymbol,
State_LookupUndef, State_FindRefs,
State_LookupWeak,
State_LookupEntryPoint, State_LookupEntryPoint,
State_ReportUnresolvedSymbols, State_ReportUnresolvedSymbols,
}; };
@@ -1234,13 +1313,9 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
HashTable *loaded_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 *missing_lib_ht = hash_table_init(scratch.arena, 0x100);
HashTable *loaded_obj_ht = hash_table_init(scratch.arena, 0x4000); HashTable *loaded_obj_ht = hash_table_init(scratch.arena, 0x4000);
LNK_SymbolList lookup_undef_list = {0};
LNK_SymbolList lookup_weak_list = {0};
LNK_SymbolList unresolved_undef_list = {0};
LNK_SymbolList unresolved_weak_list = {0};
U64 entry_point_lookup_attempts = 0; U64 entry_point_lookup_attempts = 0;
B32 report_unresolved_symbols = 1;
B32 input_linker_objs = 1; B32 input_linker_objs = 1;
B32 pending_refs = 0;
// //
// Init state machine // Init state machine
@@ -1293,11 +1368,6 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
continue; continue;
} }
// was import already created?
if (lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, import_header.func_name)) {
continue;
}
// 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(scratch.arena, import_header.func_name, import_stub->u.defined.obj, import_stub->u.defined.symbol_idx); LNK_Symbol *thunk_symbol = lnk_make_defined_symbol(scratch.arena, import_header.func_name, import_stub->u.defined.obj, import_stub->u.defined.symbol_idx);
@@ -1346,7 +1416,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
// input obj with includes // input obj with includes
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 = str8_lit("* INCLUDE SYMBOLS *"); input->path = str8_lit("* INCLUDE SYMBOLS *");
input->dedup_id = push_str8f(scratch.arena, "%S %llu", input->path, input_obj_list.count); input->dedup_id = push_str8f(scratch.arena, "%S %llu", input->path, obj_list.count);
input->data = coff_obj_writer_serialize(tp_arena->v[0], obj_writer); input->data = coff_obj_writer_serialize(tp_arena->v[0], obj_writer);
coff_obj_writer_release(&obj_writer); coff_obj_writer_release(&obj_writer);
@@ -1439,12 +1509,10 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
ProfEnd(); ProfEnd();
// input extern symbols from each obj to the symbol table // input extern symbols from each obj to the symbol table
LNK_SymbolInputResult input_result = 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
lnk_symbol_list_concat_in_place(&lookup_undef_list, &unresolved_undef_list); pending_refs = 1;
lnk_symbol_list_concat_in_place(&lookup_undef_list, &input_result.undef_symbols);
lnk_symbol_list_concat_in_place(&lookup_weak_list, &input_result.weak_symbols);
// reset input objs // reset input objs
MemoryZeroStruct(&input_obj_list); MemoryZeroStruct(&input_obj_list);
@@ -1539,6 +1607,9 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
ProfEnd(); ProfEnd();
} }
// schedule symbol input
pending_refs = 1;
ProfEnd(); ProfEnd();
} break; } break;
case State_InputAlternateNames: { case State_InputAlternateNames: {
@@ -1591,39 +1662,16 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
ProfEnd(); ProfEnd();
} break; } break;
case State_LookupUndef: { case State_FindRefs: {
ProfBegin("Lookup Undefined Symbols"); ProfBegin("Find Refs");
// search archives LNK_InputImportList new_imports = {0};
LNK_SymbolFinderResult result = lnk_run_symbol_finder(tp, tp_arena, config, symtab, lookup_undef_list, lnk_undef_symbol_finder); // TODO: put these on temp arena LNK_InputObjList new_objs = {0};
lnk_find_refs(scratch.arena, tp, symtab, config, obj_list, &new_imports, &new_objs);
// new inputs found
input_obj_list = result.input_obj_list;
input_import_list = result.input_import_list;
// undefined symbols that weren't resolved
lnk_symbol_list_concat_in_place(&unresolved_undef_list, &result.unresolved_symbol_list);
// reset input
MemoryZeroStruct(&lookup_undef_list);
ProfEnd();
} break;
case State_LookupWeak: {
ProfBegin("Lookup Weak Symbols");
// search archives
LNK_SymbolFinderResult result = lnk_run_symbol_finder(tp, tp_arena, config, symtab, lookup_weak_list, lnk_weak_symbol_finder); // TODO: put these on temp arena
// schedule new inputs // schedule new inputs
input_obj_list = result.input_obj_list; lnk_input_import_list_concat_in_place(&input_import_list, &new_imports);
input_import_list = result.input_import_list; lnk_input_obj_list_concat_in_place(&input_obj_list, &new_objs);
// weak symbols that weren't resolved
lnk_symbol_list_concat_in_place(&unresolved_weak_list, &result.unresolved_symbol_list);
// reset input
MemoryZeroStruct(&lookup_weak_list);
ProfEnd(); ProfEnd();
} break; } break;
@@ -1747,15 +1795,6 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
ProfEnd(); ProfEnd();
} break; } break;
case State_ReportUnresolvedSymbols: {
// report unresolved symbols
for (LNK_SymbolNode *node = unresolved_undef_list.first; node != 0; node = node->next) {
lnk_error_obj(LNK_Error_UnresolvedSymbol, node->data->u.undef.obj, "unresolved symbol %S", node->data->name);
}
if (unresolved_undef_list.count) {
goto exit;
}
} break;
case State_InputLinkerObjs: { case State_InputLinkerObjs: {
{ {
ProfBegin("Push Linker Symbols"); ProfBegin("Push Linker Symbols");
@@ -1788,7 +1827,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
for (U64 dll_idx = 0; dll_idx < delayed_imports->count; dll_idx += 1) { for (U64 dll_idx = 0; dll_idx < delayed_imports->count; dll_idx += 1) {
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 = input_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], 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;
@@ -1796,14 +1835,14 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
String8 linker_debug_symbols = lnk_make_linker_debug_symbols(tp_arena->v[0], config->machine); String8 linker_debug_symbols = lnk_make_linker_debug_symbols(tp_arena->v[0], config->machine);
{ {
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 = input_obj_list.count; input->input_idx = obj_list.count;
input->data = pe_make_null_import_descriptor_delayed(tp_arena->v[0], time_stamp, config->machine, linker_debug_symbols); input->data = pe_make_null_import_descriptor_delayed(tp_arena->v[0], time_stamp, config->machine, linker_debug_symbols);
input->path = str8_lit("* Delayed Null Import Descriptor *"); input->path = str8_lit("* Delayed Null Import Descriptor *");
input->dedup_id = input->path; input->dedup_id = input->path;
} }
{ {
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 = input_obj_list.count; input->input_idx = obj_list.count;
input->data = pe_make_null_thunk_data_obj_delayed(tp_arena->v[0], lnk_get_image_name(config), time_stamp, config->machine, linker_debug_symbols); input->data = pe_make_null_thunk_data_obj_delayed(tp_arena->v[0], lnk_get_image_name(config), time_stamp, config->machine, linker_debug_symbols);
input->path = str8_lit("* Delayed Null Thunk Data *"); input->path = str8_lit("* Delayed Null Thunk Data *");
input->dedup_id = input->path; input->dedup_id = input->path;
@@ -1822,7 +1861,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
for (U64 dll_idx = 0; dll_idx < static_imports->count; dll_idx += 1) { for (U64 dll_idx = 0; dll_idx < static_imports->count; dll_idx += 1) {
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 = input_obj_list.count; input->input_idx = obj_list.count;
input->data = pe_make_import_dll_obj_static(tp_arena->v[0], time_stamp, config->machine, dll_names[dll_idx], import_debug_symbols, *dll_import_headers[dll_idx]); input->data = pe_make_import_dll_obj_static(tp_arena->v[0], time_stamp, config->machine, dll_names[dll_idx], import_debug_symbols, *dll_import_headers[dll_idx]);
input->path = dll_names[dll_idx]; input->path = dll_names[dll_idx];
input->dedup_id = dll_names[dll_idx]; input->dedup_id = dll_names[dll_idx];
@@ -1830,14 +1869,14 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
String8 linker_debug_symbols = lnk_make_linker_debug_symbols(scratch.arena, config->machine); String8 linker_debug_symbols = lnk_make_linker_debug_symbols(scratch.arena, config->machine);
{ {
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 = input_obj_list.count; input->input_idx = obj_list.count;
input->data = pe_make_null_import_descriptor_obj(tp_arena->v[0], time_stamp, config->machine, linker_debug_symbols); input->data = pe_make_null_import_descriptor_obj(tp_arena->v[0], time_stamp, config->machine, linker_debug_symbols);
input->path = str8_lit("* Null Import Descriptor *"); input->path = str8_lit("* Null Import Descriptor *");
input->dedup_id = input->path; input->dedup_id = input->path;
} }
{ {
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 = input_obj_list.count; input->input_idx = obj_list.count;
input->data = pe_make_null_thunk_data_obj(tp_arena->v[0], lnk_get_image_name(config), time_stamp, config->machine, linker_debug_symbols); input->data = pe_make_null_thunk_data_obj(tp_arena->v[0], lnk_get_image_name(config), time_stamp, config->machine, linker_debug_symbols);
input->path = str8_lit("* Null Thunk Data *"); input->path = str8_lit("* Null Thunk Data *");
input->dedup_id = input->path; input->dedup_id = input->path;
@@ -2016,10 +2055,6 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
state_list_push(scratch.arena, state_list, State_InputAlternateNames); state_list_push(scratch.arena, state_list, State_InputAlternateNames);
continue; continue;
} }
if (input_obj_list.count) {
state_list_push(scratch.arena, state_list, State_InputObjs);
continue;
}
{ {
B32 have_pending_lib_inputs = 0; B32 have_pending_lib_inputs = 0;
for (U64 i = 0; i < ArrayCount(input_libs); ++i) { for (U64 i = 0; i < ArrayCount(input_libs); ++i) {
@@ -2033,18 +2068,13 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
continue; continue;
} }
} }
if (lookup_undef_list.count) { if (input_obj_list.count) {
state_list_push(scratch.arena, state_list, State_LookupUndef); state_list_push(scratch.arena, state_list, State_InputObjs);
continue; continue;
} }
if (lookup_weak_list.count) { if (pending_refs) {
state_list_push(scratch.arena, state_list, State_LookupWeak); pending_refs = 0;
continue; state_list_push(scratch.arena, state_list, State_FindRefs);
}
if (unresolved_weak_list.count) {
// we can't find strong definitions for unresolved weak symbols
// so now we have to use fallback symbols
MemoryZeroStruct(&unresolved_weak_list);
continue; continue;
} }
if (entry_point_lookup_attempts == 0) { if (entry_point_lookup_attempts == 0) {
@@ -2057,22 +2087,104 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
state_list_push(scratch.arena, state_list, State_InputLinkerObjs); state_list_push(scratch.arena, state_list, State_InputLinkerObjs);
continue; continue;
} }
if (unresolved_undef_list.count) {
if (report_unresolved_symbols) {
report_unresolved_symbols = 0;
state_list_push(scratch.arena, state_list, State_ReportUnresolvedSymbols);
continue;
}
}
break; break;
} }
// pass over symbol table and replace weak symbols without a strong definition with fallback definitions ProfBegin("Report Unresolved Symbols");
lnk_finalize_weak_symbols(tp_arena, tp, symtab);
// log
{ {
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);
U64 undefs_count = 0;
for EachIndex(chunk_idx, chunks_count) {
LNK_SymbolHashTrieChunk *chunk = chunks[chunk_idx];
for EachIndex(i, chunk->count) {
LNK_Symbol *symbol = chunk->v[i].symbol;
if (symbol->is_live) {
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_Undefined) {
undefs_count += 1;
}
}
}
}
U64 undefs_cursor = 0;
LNK_Symbol **undefs = push_array(scratch.arena, LNK_Symbol *, undefs_count);
for EachIndex(chunk_idx, chunks_count) {
LNK_SymbolHashTrieChunk *chunk = chunks[chunk_idx];
for EachIndex(i, chunk->count) {
LNK_Symbol *symbol = chunk->v[i].symbol;
if (symbol->is_live) {
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_Undefined) {
undefs[undefs_cursor++] = chunk->v[i].symbol;
}
}
}
}
radsort(undefs, undefs_count, lnk_symbol_defined_ptr_is_before);
for EachIndex(i, undefs_count) {
LNK_Symbol *s = undefs[i];
lnk_error_obj(LNK_Error_UnresolvedSymbol, s->u.defined.obj, "unresolved symbol %S", s->name);
}
if (undefs_count > 0) {
lnk_exit(LNK_Error_UnresolvedSymbol);
}
}
ProfEnd();
ProfBegin("Remove Unreachable Sections");
{
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) {
// 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();
lnk_replace_weak_symbols_with_default_symbols(tp, symtab);
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));
//
// log
//
if (lnk_get_log_status(LNK_Log_InputObj)) { if (lnk_get_log_status(LNK_Log_InputObj)) {
U64 total_input_size = 0; U64 total_input_size = 0;
for (LNK_ObjNode *obj_n = obj_list.first; obj_n != 0; obj_n = obj_n->next) { total_input_size += obj_n->data.data.size; } for (LNK_ObjNode *obj_n = obj_list.first; obj_n != 0; obj_n = obj_n->next) { total_input_size += obj_n->data.data.size; }
@@ -2086,15 +2198,7 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config)
} }
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);
} }
}
exit:;
// TODO: include symbol list
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);
@@ -2150,166 +2254,6 @@ lnk_resolve_symbol(LNK_SymbolTable *symtab, LNK_SymbolDefined symbol, LNK_Symbol
return is_resolved; return is_resolved;
} }
internal void
lnk_gc_comdats(TP_Context *tp, LNK_SymbolTable *symtab, U64 objs_count, LNK_Obj **objs, LNK_Config *config)
{
ProfBeginFunction();
Temp scratch = scratch_begin(0,0);
struct Task { struct Task *next; LNK_Obj *obj; COFF_RelocArray relocs; };
struct Task *task_stack = 0;
const U64 RELOCS_PER_TASK = 1024;
//
// define roots
//
{
String8List roots = str8_list_copy(scratch.arena, &config->include_symbol_list);
// tls
LNK_Symbol *tls_symbol = lnk_symbol_table_searchf(symtab, LNK_SymbolScope_Defined, MSCRT_TLS_SYMBOL_NAME);
if (tls_symbol) {
str8_list_pushf(scratch.arena, &roots, MSCRT_TLS_SYMBOL_NAME);
}
// push tasks for each root symbol
for (String8Node *root_n = roots.first; root_n != 0; root_n = root_n->next) {
LNK_Symbol *root = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, root_n->string);
struct Task *t = push_array(scratch.arena, struct Task, 1);
t->obj = root->u.defined.obj;
t->relocs.count = 1;
t->relocs.v = push_array(scratch.arena, COFF_Reloc, 1);
t->relocs.v[0].isymbol = root->u.defined.symbol_idx;
SLLStackPush(task_stack, t);
}
// push task for every non-COMDAT section
for EachIndex(obj_idx, objs_count) {
LNK_Obj *obj = objs[obj_idx];
for EachIndex(sect_idx, obj->header.section_count_no_null) {
if (lnk_is_coff_section_debug(obj, sect_idx)) { continue; }
COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, sect_idx+1);
if ((~section_header->flags & COFF_SectionFlag_LnkCOMDAT) && (~section_header->flags & COFF_SectionFlag_LnkRemove)) {
// extract reloc info
COFF_RelocArray relocs = lnk_coff_reloc_info_from_section_number(obj, sect_idx+1);
// alloc new tasks
U64 new_task_count = CeilIntegerDiv(relocs.count, RELOCS_PER_TASK);
struct Task *new_tasks = push_array(scratch.arena, struct Task, new_task_count);
// divide relocs and push tasks
for EachIndex(new_task_idx, new_task_count) {
struct Task *t = new_tasks + new_task_idx;
t->obj = obj;
t->relocs.count = Min(RELOCS_PER_TASK, relocs.count - (new_task_idx * RELOCS_PER_TASK));
t->relocs.v = relocs.v + (new_task_idx * RELOCS_PER_TASK);
SLLStackPush(task_stack, t);
}
}
}
}
}
//
// begin with COMDAT sections flagged as removed
//
for EachIndex(obj_idx, objs_count) {
LNK_Obj *obj = objs[obj_idx];
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 = &section_table[sect_idx];
if (section_header->flags & COFF_SectionFlag_LnkCOMDAT) {
section_header->flags |= COFF_SectionFlag_LnkRemove;
}
}
}
//
// init per section flag array
//
B8 **was_section_visited = push_array(scratch.arena, B8 *, objs_count);
for EachIndex(obj_idx, objs_count) { was_section_visited[obj_idx] = push_array(scratch.arena, B8, objs[obj_idx]->header.section_count_no_null + 1); }
//
// walk relocations and unset the remove flag on visited sections
//
for (; task_stack; ) {
struct Task *t = task_stack; SLLStackPop(task_stack);
for EachIndex(reloc_idx, t->relocs.count) {
COFF_Reloc *reloc = &t->relocs.v[reloc_idx];
LNK_SymbolDefined reloc_symbol = {0};
B32 is_reloc_symbol_resolved = lnk_resolve_symbol(symtab, (LNK_SymbolDefined){ .obj = t->obj, .symbol_idx = reloc->isymbol }, &reloc_symbol);
if (is_reloc_symbol_resolved) {
// parse and interp reloc symbol
LNK_Obj *reloc_obj = reloc_symbol.obj;
COFF_ParsedSymbol reloc_parsed = lnk_parsed_symbol_from_coff_symbol_idx(reloc_obj, reloc_symbol.symbol_idx);
COFF_SymbolValueInterpType reloc_interp = coff_interp_from_parsed_symbol(reloc_parsed);
if (reloc_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 = reloc_parsed.section_number;
section_number_list->next = reloc_obj->associated_sections[reloc_parsed.section_number];
// push section headers relocations to the task stack
for (U32Node *section_number_n = section_number_list; section_number_n != 0; section_number_n = section_number_n->next) {
if (was_section_visited[reloc_symbol.obj->input_idx][section_number_n->data]) { continue; }
was_section_visited[reloc_symbol.obj->input_idx][section_number_n->data] = 1;
COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(reloc_symbol.obj, section_number_n->data);
if (lnk_is_coff_section_debug(reloc_obj, section_number_n->data-1)) { continue; }
// skip regular sections that were removed
if (~section_header->flags & COFF_SectionFlag_LnkCOMDAT && section_header->flags & COFF_SectionFlag_LnkRemove) { continue; }
// on reachable COMDAT sections, unset remove flag
if (section_header->flags & COFF_SectionFlag_LnkCOMDAT) { section_header->flags &= ~COFF_SectionFlag_LnkRemove; }
// extract reloc info
COFF_RelocArray relocs = lnk_coff_reloc_info_from_section_number(reloc_symbol.obj, section_number_n->data);
// alloc new tasks
U64 new_task_count = CeilIntegerDiv(relocs.count, RELOCS_PER_TASK);
struct Task *new_tasks = push_array(scratch.arena, struct Task, new_task_count);
// divide relocs and push tasks
for EachIndex(new_task_idx, new_task_count) {
struct Task *t = new_tasks + new_task_idx;
t->obj = reloc_obj;
t->relocs.count = Min(RELOCS_PER_TASK, relocs.count - (new_task_idx * RELOCS_PER_TASK));
t->relocs.v = relocs.v + (new_task_idx * RELOCS_PER_TASK);
SLLStackPush(task_stack, t);
}
}
}
}
}
}
//
// unset flag on debug sections that associate with live sections
//
for EachIndex(obj_idx, objs_count) {
LNK_Obj *obj = objs[obj_idx];
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 & COFF_SectionFlag_LnkRemove) { continue; }
for (U32Node *section_number_n = obj->associated_sections[section_number]; section_number_n != 0; section_number_n = section_number_n->next) {
if (lnk_is_coff_section_debug(obj, section_number_n->data-1)) {
COFF_SectionHeader *associated_section_header = lnk_coff_section_header_from_section_number(obj, section_number_n->data);
associated_section_header->flags &= ~COFF_SectionFlag_LnkRemove;
}
}
}
}
scratch_end(scratch);
ProfEnd();
}
internal internal
THREAD_POOL_TASK_FUNC(lnk_gather_section_definitions_task) THREAD_POOL_TASK_FUNC(lnk_gather_section_definitions_task)
{ {
@@ -2676,19 +2620,28 @@ lnk_patch_obj_symtab(LNK_SymbolTable *symtab, LNK_Obj *obj, B8 *was_symbol_patch
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 || AssertAlways(fixup_type == COFF_SymbolValueInterp_Regular ||
fixup_type == COFF_SymbolValueInterp_Abs || fixup_type == COFF_SymbolValueInterp_Abs ||
fixup_type == COFF_SymbolValueInterp_Common); fixup_type == COFF_SymbolValueInterp_Common ||
fixup_type == COFF_SymbolValueInterp_Undefined);
U32 section_number;
U32 value;
if (fixup_type == COFF_SymbolValueInterp_Undefined) {
section_number = lnk_obj_get_removed_section_number(obj);
value = 0;
} else {
section_number = fixup_src.section_number;
value = fixup_src.value;
}
if (obj->header.is_big_obj) { if (obj->header.is_big_obj) {
COFF_Symbol32 *symbol32 = fixup_dst.raw_symbol; COFF_Symbol32 *symbol32 = fixup_dst.raw_symbol;
symbol32->section_number = fixup_src.section_number; symbol32->section_number = section_number;
symbol32->value = fixup_src.value; symbol32->value = value;
symbol32->type = fixup_src.type;
symbol32->storage_class = COFF_SymStorageClass_Static; symbol32->storage_class = COFF_SymStorageClass_Static;
} else { } else {
COFF_Symbol16 *symbol16 = fixup_dst.raw_symbol; COFF_Symbol16 *symbol16 = fixup_dst.raw_symbol;
symbol16->section_number = (U16)fixup_src.section_number; symbol16->section_number = (U16)section_number;
symbol16->value = fixup_src.value; symbol16->value = value;
symbol16->type = fixup_src.type;
symbol16->storage_class = COFF_SymStorageClass_Static; symbol16->storage_class = COFF_SymStorageClass_Static;
} }
@@ -2751,7 +2704,7 @@ THREAD_POOL_TASK_FUNC(lnk_obj_reloc_patcher)
String8 symbol_table = str8_substr(obj->data, obj_header.symbol_table_range); String8 symbol_table = str8_substr(obj->data, obj_header.symbol_table_range);
String8 string_table = str8_substr(obj->data, obj_header.string_table_range); String8 string_table = str8_substr(obj->data, obj_header.string_table_range);
for (U64 sect_idx = 0; sect_idx < obj_header.section_count_no_null; sect_idx += 1) { for EachIndex(sect_idx, obj_header.section_count_no_null) {
COFF_SectionHeader *section_header = &section_table[sect_idx]; COFF_SectionHeader *section_header = &section_table[sect_idx];
if (section_header->flags & COFF_SectionFlag_LnkRemove) { continue; } if (section_header->flags & COFF_SectionFlag_LnkRemove) { continue; }
@@ -2767,7 +2720,7 @@ THREAD_POOL_TASK_FUNC(lnk_obj_reloc_patcher)
COFF_Reloc *relocs = (COFF_Reloc *)(obj->data.str + reloc_info.array_off); COFF_Reloc *relocs = (COFF_Reloc *)(obj->data.str + reloc_info.array_off);
// apply relocs // apply relocs
for (U64 reloc_idx = 0; reloc_idx < reloc_info.count; reloc_idx += 1) { for EachIndex(reloc_idx, reloc_info.count) {
COFF_Reloc *reloc = &relocs[reloc_idx]; COFF_Reloc *reloc = &relocs[reloc_idx];
// error check relocation // error check relocation
@@ -2860,7 +2813,7 @@ THREAD_POOL_TASK_FUNC(lnk_count_common_block_contribs_task)
LNK_SymbolTable *symtab = task->symtab; LNK_SymbolTable *symtab = task->symtab;
for (LNK_SymbolHashTrieChunk *chunk = symtab->chunk_lists[LNK_SymbolScope_Defined][task_id].first; chunk != 0; chunk = chunk->next) { for (LNK_SymbolHashTrieChunk *chunk = symtab->chunk_lists[LNK_SymbolScope_Defined][task_id].first; chunk != 0; chunk = chunk->next) {
for (U64 i = 0; i < chunk->count; i += 1) { for EachIndex(i, chunk->count) {
LNK_Symbol *symbol = chunk->v[i].symbol; LNK_Symbol *symbol = chunk->v[i].symbol;
COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx);
COFF_SymbolValueInterpType parsed_interp = coff_interp_symbol(parsed_symbol.section_number, parsed_symbol.value, parsed_symbol.storage_class); COFF_SymbolValueInterpType parsed_interp = coff_interp_symbol(parsed_symbol.section_number, parsed_symbol.value, parsed_symbol.storage_class);
@@ -2879,7 +2832,7 @@ THREAD_POOL_TASK_FUNC(lnk_fill_out_common_block_contribs_task)
U64 cursor = task->u.common_block.offsets[task_id]; U64 cursor = task->u.common_block.offsets[task_id];
for (LNK_SymbolHashTrieChunk *chunk = symtab->chunk_lists[LNK_SymbolScope_Defined][task_id].first; chunk != 0; chunk = chunk->next) { for (LNK_SymbolHashTrieChunk *chunk = symtab->chunk_lists[LNK_SymbolScope_Defined][task_id].first; chunk != 0; chunk = chunk->next) {
for (U64 i = 0; i < chunk->count; i += 1) { for EachIndex(i, chunk->count) {
LNK_Symbol *symbol = chunk->v[i].symbol; LNK_Symbol *symbol = chunk->v[i].symbol;
COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx);
COFF_SymbolValueInterpType parsed_interp = coff_interp_symbol(parsed_symbol.section_number, parsed_symbol.value, parsed_symbol.storage_class); COFF_SymbolValueInterpType parsed_interp = coff_interp_symbol(parsed_symbol.section_number, parsed_symbol.value, parsed_symbol.storage_class);
@@ -3826,25 +3779,13 @@ lnk_build_win32_header(Arena *arena, LNK_SymbolTable *symtab, LNK_Config *config
} }
internal LNK_ImageContext internal LNK_ImageContext
lnk_build_image(TP_Arena *arena, lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, U64 objs_count, LNK_Obj **objs)
TP_Context *tp,
LNK_Config *config,
LNK_SymbolTable *symtab,
U64 objs_count,
LNK_Obj **objs)
{ {
ProfBegin("Image"); ProfBegin("Image");
lnk_timer_begin(LNK_Timer_Image); lnk_timer_begin(LNK_Timer_Image);
Temp scratch = scratch_begin(arena->v, arena->count); Temp scratch = scratch_begin(arena->v, arena->count);
//
// remove unreachable COMDAT sections
//
if (config->opt_ref == LNK_SwitchState_Yes) {
lnk_gc_comdats(tp, symtab, objs_count, objs, config);
}
// //
// init section table // init section table
// //
+3 -1
View File
@@ -19,6 +19,8 @@ typedef struct LNK_LinkContext
#define LNK_REMOVED_SECTION_NUMBER_32 (U32)-3 #define LNK_REMOVED_SECTION_NUMBER_32 (U32)-3
#define LNK_REMOVED_SECTION_NUMBER_16 (U16)-3 #define LNK_REMOVED_SECTION_NUMBER_16 (U16)-3
#define LNK_SECTION_FLAG_IS_LIVE (1 << 0)
typedef struct LNK_ImageContext typedef struct LNK_ImageContext
{ {
String8 image_data; String8 image_data;
@@ -212,7 +214,7 @@ internal void lnk_push_disallow_lib(Arena *arena, HashTable *disallow_lib_ht,
internal void lnk_push_loaded_lib(Arena *arena, HashTable *loaded_lib_ht, String8 path); internal void lnk_push_loaded_lib(Arena *arena, HashTable *loaded_lib_ht, String8 path);
internal LNK_InputObjList lnk_push_linker_symbols(Arena *arena, LNK_Config *config); internal LNK_InputObjList lnk_push_linker_symbols(Arena *arena, LNK_Config *config);
internal void lnk_queue_lib_member_input(Arena *arena, PathStyle path_style, LNK_SymbolLib *symbol, LNK_InputImportList *input_import_list, LNK_InputObjList *input_obj_list); internal void lnk_queue_lib_member_input(Arena *arena, LNK_Config *config, LNK_Symbol *symbol, LNK_InputImportList *input_import_list, LNK_InputObjList *input_obj_list);
internal LNK_LinkContext lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config); internal LNK_LinkContext lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config);
+4 -23
View File
@@ -365,7 +365,6 @@ THREAD_POOL_TASK_FUNC(lnk_input_coff_symbol_table)
case COFF_SymbolValueInterp_Weak: { case COFF_SymbolValueInterp_Weak: {
LNK_Symbol *defn = lnk_make_defined_symbol(arena, symbol.name, obj, symbol_idx); LNK_Symbol *defn = lnk_make_defined_symbol(arena, symbol.name, obj, symbol_idx);
lnk_symbol_table_push_(task->symtab, arena, worker_id, LNK_SymbolScope_Defined, defn); lnk_symbol_table_push_(task->symtab, arena, worker_id, LNK_SymbolScope_Defined, defn);
lnk_symbol_list_push(arena, &task->weak_lists[worker_id], defn);
} break; } break;
case COFF_SymbolValueInterp_Common: { case COFF_SymbolValueInterp_Common: {
LNK_Symbol *defn = lnk_make_defined_symbol(arena, symbol.name, obj, symbol_idx); LNK_Symbol *defn = lnk_make_defined_symbol(arena, symbol.name, obj, symbol_idx);
@@ -378,16 +377,9 @@ THREAD_POOL_TASK_FUNC(lnk_input_coff_symbol_table)
} }
} break; } break;
case COFF_SymbolValueInterp_Undefined: { case COFF_SymbolValueInterp_Undefined: {
LNK_Symbol *s = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol.name);
if (s == 0) {
if (symbol.storage_class == COFF_SymStorageClass_External) { if (symbol.storage_class == COFF_SymStorageClass_External) {
LNK_Symbol *undef = lnk_make_undefined_symbol(arena, symbol.name, obj); LNK_Symbol *defn = lnk_make_defined_symbol(arena, symbol.name, obj, symbol_idx);
lnk_symbol_list_push(arena, &task->undef_lists[worker_id], undef); lnk_symbol_table_push_(task->symtab, arena, worker_id, LNK_SymbolScope_Defined, defn);
} else if (symbol.storage_class == COFF_SymStorageClass_Section) {
// lookup is performed during image patching step
} else {
Assert(!"unexpected storage class on undefined symbol");
}
} }
} break; } break;
case COFF_SymbolValueInterp_Debug: { case COFF_SymbolValueInterp_Debug: {
@@ -426,27 +418,16 @@ THREAD_POOL_TASK_FUNC(lnk_assign_comdat_symlinks_task)
obj->symlinks = lnk_symlinks_from_obj(arena, task->symtab, obj); obj->symlinks = lnk_symlinks_from_obj(arena, task->symtab, obj);
} }
internal LNK_SymbolInputResult internal void
lnk_input_obj_symbols(TP_Context *tp, TP_Arena *arena, LNK_SymbolTable *symtab, LNK_ObjNodeArray objs) lnk_input_obj_symbols(TP_Context *tp, TP_Arena *arena, LNK_SymbolTable *symtab, LNK_ObjNodeArray objs)
{ {
ProfBeginFunction(); ProfBeginFunction();
Temp scratch = scratch_begin(arena->v, arena->count);
LNK_InputCoffSymbolTable task = {0}; LNK_InputCoffSymbolTable task = { .symtab = symtab, .objs = objs };
task.symtab = symtab;
task.objs = objs;
task.weak_lists = push_array(scratch.arena, LNK_SymbolList, tp->worker_count);
task.undef_lists = push_array(scratch.arena, LNK_SymbolList, tp->worker_count);
tp_for_parallel(tp, arena, objs.count, lnk_input_coff_symbol_table, &task); tp_for_parallel(tp, arena, objs.count, lnk_input_coff_symbol_table, &task);
tp_for_parallel(tp, arena, objs.count, lnk_assign_comdat_symlinks_task, &task); tp_for_parallel(tp, arena, objs.count, lnk_assign_comdat_symlinks_task, &task);
LNK_SymbolInputResult result = {0};
SLLConcatInPlaceArray(&result.weak_symbols, task.weak_lists, tp->worker_count);
SLLConcatInPlaceArray(&result.undef_symbols, task.undef_lists, tp->worker_count);
scratch_end(scratch);
ProfEnd(); ProfEnd();
return result;
} }
internal COFF_ParsedSymbol internal COFF_ParsedSymbol
+1 -9
View File
@@ -37,12 +37,6 @@ typedef struct LNK_ObjNodeArray
LNK_ObjNode *v; LNK_ObjNode *v;
} LNK_ObjNodeArray; } LNK_ObjNodeArray;
typedef struct LNK_SymbolInputResult
{
LNK_SymbolList weak_symbols;
LNK_SymbolList undef_symbols;
} LNK_SymbolInputResult;
// --- Directive Parser -------------------------------------------------------- // --- Directive Parser --------------------------------------------------------
typedef struct LNK_Directive typedef struct LNK_Directive
@@ -78,8 +72,6 @@ typedef struct
{ {
LNK_SymbolTable *symtab; LNK_SymbolTable *symtab;
LNK_ObjNodeArray objs; LNK_ObjNodeArray objs;
LNK_SymbolList *weak_lists;
LNK_SymbolList *undef_lists;
} LNK_InputCoffSymbolTable; } LNK_InputCoffSymbolTable;
typedef struct typedef struct
@@ -98,7 +90,7 @@ internal void lnk_error_obj(LNK_ErrorCode code, LNK_Obj *obj, char *fmt, ...);
internal LNK_Obj ** lnk_array_from_obj_list(Arena *arena, LNK_ObjList list); internal LNK_Obj ** lnk_array_from_obj_list(Arena *arena, LNK_ObjList list);
internal LNK_ObjNodeArray lnk_obj_list_push_parallel(TP_Context *tp, TP_Arena *tp_arena, LNK_ObjList *obj_list, COFF_MachineType machine, U64 input_count, LNK_InputObj **inputs); internal LNK_ObjNodeArray lnk_obj_list_push_parallel(TP_Context *tp, TP_Arena *tp_arena, LNK_ObjList *obj_list, COFF_MachineType machine, U64 input_count, LNK_InputObj **inputs);
internal LNK_SymbolInputResult lnk_input_obj_symbols(TP_Context *tp, TP_Arena *arena, LNK_SymbolTable *symtab, LNK_ObjNodeArray objs); internal void lnk_input_obj_symbols(TP_Context *tp, TP_Arena *arena, LNK_SymbolTable *symtab, LNK_ObjNodeArray objs);
// --- Metadata ---------------------------------------------------------------- // --- Metadata ----------------------------------------------------------------
+164 -119
View File
@@ -21,15 +21,6 @@ lnk_make_lib_symbol(Arena *arena, String8 name, struct LNK_Lib *lib, U64 member_
return symbol; return symbol;
} }
internal LNK_Symbol *
lnk_make_undefined_symbol(Arena *arena, String8 name, struct LNK_Obj *obj)
{
LNK_Symbol *symbol = push_array(arena, LNK_Symbol, 1);
symbol->name = name;
symbol->u.undef.obj = obj;
return symbol;
}
internal B32 internal B32
lnk_symbol_defined_is_before(void *raw_a, void *raw_b) lnk_symbol_defined_is_before(void *raw_a, void *raw_b)
{ {
@@ -37,6 +28,12 @@ lnk_symbol_defined_is_before(void *raw_a, void *raw_b)
return a->u.defined.obj->input_idx < b->u.defined.obj->input_idx; return a->u.defined.obj->input_idx < b->u.defined.obj->input_idx;
} }
internal B32
lnk_symbol_defined_ptr_is_before(void *raw_a, void *raw_b)
{
return lnk_symbol_defined_is_before(*(LNK_Symbol **)raw_a, *(LNK_Symbol **)raw_b);
}
internal B32 internal B32
lnk_symbol_lib_is_before(void *raw_a, void *raw_b) lnk_symbol_lib_is_before(void *raw_a, void *raw_b)
{ {
@@ -144,8 +141,36 @@ lnk_can_replace_symbol(LNK_SymbolScope scope, LNK_Symbol *dst, LNK_Symbol *src)
COFF_SymbolValueInterpType dst_interp = coff_interp_from_parsed_symbol(dst_parsed); COFF_SymbolValueInterpType dst_interp = coff_interp_from_parsed_symbol(dst_parsed);
COFF_SymbolValueInterpType src_interp = coff_interp_from_parsed_symbol(src_parsed); COFF_SymbolValueInterpType src_interp = coff_interp_from_parsed_symbol(src_parsed);
// undefined vs regular
if (dst_interp == COFF_SymbolValueInterp_Undefined && src_interp == COFF_SymbolValueInterp_Regular) {
can_replace = 1;
}
// undefined vs weak
else if (dst_interp == COFF_SymbolValueInterp_Undefined && src_interp == COFF_SymbolValueInterp_Weak) {
can_replace = 1;
}
// undefined vs undefined
else if (dst_interp == COFF_SymbolValueInterp_Undefined && src_interp == COFF_SymbolValueInterp_Undefined) {
can_replace = lnk_symbol_defined_is_before(src, dst);
}
// undefined vs common
else if (dst_interp == COFF_SymbolValueInterp_Undefined && src_interp == COFF_SymbolValueInterp_Common) {
can_replace = 1;
}
// undefined vs abs
else if (dst_interp == COFF_SymbolValueInterp_Undefined && src_interp == COFF_SymbolValueInterp_Abs) {
can_replace = 1;
}
// undefined vs debug
else if (dst_interp == COFF_SymbolValueInterp_Undefined && src_interp == COFF_SymbolValueInterp_Debug) {
can_replace = 1;
}
// regular/weak/common/abs/debug vs undefined
else if (dst_interp != COFF_SymbolValueInterp_Undefined && src_interp == COFF_SymbolValueInterp_Undefined) {
can_replace = 0;
}
// regular vs abs // regular vs abs
if (dst_interp == COFF_SymbolValueInterp_Regular && src_interp == COFF_SymbolValueInterp_Abs) { else if (dst_interp == COFF_SymbolValueInterp_Regular && src_interp == COFF_SymbolValueInterp_Abs) {
lnk_error_multiply_defined_symbol(dst, src); lnk_error_multiply_defined_symbol(dst, src);
} }
// abs vs regular // abs vs regular
@@ -439,6 +464,129 @@ lnk_symbol_hash_trie_remove(LNK_SymbolHashTrie *trie)
ins_atomic_ptr_eval_assign(&trie->symbol, 0); ins_atomic_ptr_eval_assign(&trie->symbol, 0);
} }
internal LNK_SymbolHashTrieChunk **
lnk_array_from_symbol_hash_trie_chunk_list(Arena *arena, LNK_SymbolHashTrieChunkList *lists, U64 lists_count, U64 *count_out)
{
U64 chunks_count = 0;
for EachIndex(i, lists_count) { chunks_count += lists[i].count; }
LNK_SymbolHashTrieChunk **chunks = push_array(arena, LNK_SymbolHashTrieChunk *, chunks_count);
U64 chunks_cursor = 0;
for EachIndex(i, lists_count) {
for (LNK_SymbolHashTrieChunk *chunk = lists[i].first; chunk != 0; chunk = chunk->next) {
chunks[chunks_cursor++] = chunk;
}
}
if (count_out) {
*count_out = chunks_count;
}
return chunks;
}
internal COFF_ParsedSymbol
lnk_parsed_symbol_from_defined(LNK_Symbol *symbol)
{
return lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx);
}
internal B32
lnk_mark_symbol_live(LNK_Symbol *symbol)
{
B32 was_live = ins_atomic_u32_eval_assign(&symbol->is_live, 1);
return was_live;
}
internal LNK_SymbolDefined
lnk_default_symbol_from_weak(LNK_SymbolTable *symtab, LNK_SymbolDefined symbol)
{
Temp scratch = scratch_begin(0,0);
struct LookupLocation { struct LookupLocation *next; LNK_SymbolDefined symbol; B32 is_anti_dep; };
struct LookupLocation *lookup_first = 0, *lookup_last = 0;
COFF_ParsedSymbol head_parsed = lnk_parsed_symbol_from_coff_symbol_idx(symbol.obj, symbol.symbol_idx);
COFF_SymbolWeakExt *head_weak_ext = coff_parse_weak_tag(head_parsed, symbol.obj->header.is_big_obj);
LNK_SymbolDefined current_symbol = (LNK_SymbolDefined){ .obj = symbol.obj, .symbol_idx = head_weak_ext->tag_index };
for (;;) {
// guard against self-referencing weak symbols
struct LookupLocation *was_visited = 0;
for (struct LookupLocation *l = lookup_first; l != 0; l = l->next) {
if (MemoryCompare(&l->symbol, &current_symbol, sizeof(LNK_SymbolDefined)) == 0) { was_visited = l; break; }
}
if (was_visited) {
Temp temp = temp_begin(scratch.arena);
String8List ref_list = {0};
for (struct LookupLocation *l = lookup_first; l != 0; l = l->next) {
COFF_ParsedSymbol loc_symbol = lnk_parsed_symbol_from_coff_symbol_idx(l->symbol.obj, l->symbol.symbol_idx);
str8_list_pushf(temp.arena, &ref_list, "\t%S Symbol %S (No. %#x) =>", l->symbol.obj->path, loc_symbol.name, l->symbol.symbol_idx);
}
COFF_ParsedSymbol loc_symbol = lnk_parsed_symbol_from_coff_symbol_idx(lookup_first->symbol.obj, lookup_first->symbol.symbol_idx);
str8_list_pushf(temp.arena, &ref_list, "\t%S Symbol %S (No. %#x)", lookup_first->symbol.obj->path, loc_symbol.name, lookup_first->symbol.symbol_idx);
COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol.obj, symbol.symbol_idx);
String8 loc_string = str8_list_join(temp.arena, &ref_list, &(StringJoin){ .sep = str8_lit("\n") });
lnk_error_obj(LNK_Error_WeakCycle, symbol.obj, "unable to resolve cyclic symbol %S; ref chain:\n%S", parsed_symbol.name, loc_string);
MemoryZeroStruct(&current_symbol);
temp_end(temp);
break;
}
COFF_ParsedSymbol current_parsed = lnk_parsed_symbol_from_coff_symbol_idx(current_symbol.obj, current_symbol.symbol_idx);
COFF_SymbolValueInterpType current_interp = coff_interp_symbol(current_parsed.section_number, current_parsed.value, current_parsed.storage_class);
if (current_interp == COFF_SymbolValueInterp_Weak) {
// check for anti dependency
for (struct LookupLocation *l = lookup_first; l != 0; l = l->next) {
if (l->is_anti_dep) {
lnk_error_obj(LNK_Error_UnresolvedSymbol, symbol.obj, "unresolved symbol %S", head_parsed.name);
break;
}
}
// does weak symbol have a definition?
LNK_Symbol *defn_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, current_parsed.name);
COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_coff_symbol_idx(defn_symbol->u.defined.obj, defn_symbol->u.defined.symbol_idx);
COFF_SymbolValueInterpType defn_interp = coff_interp_symbol(defn_parsed.section_number, defn_parsed.value, defn_parsed.storage_class);
if (defn_interp != COFF_SymbolValueInterp_Weak) {
current_symbol = defn_symbol->u.defined;
break;
}
// no definition fallback to default symbol
COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(current_parsed, current_symbol.obj->header.is_big_obj);
COFF_ParsedSymbol tag_parsed = lnk_parsed_symbol_from_coff_symbol_idx(current_symbol.obj, weak_ext->tag_index);
COFF_SymbolValueInterpType tag_interp = coff_interp_symbol(tag_parsed.section_number, tag_parsed.value, tag_parsed.storage_class);
current_symbol = (LNK_SymbolDefined){ .obj = current_symbol.obj, .symbol_idx = weak_ext->tag_index };
// record visited symbol
struct LookupLocation *loc = push_array(scratch.arena, struct LookupLocation, 1);
loc->symbol = current_symbol;
loc->is_anti_dep = weak_ext->characteristics == COFF_WeakExt_AntiDependency;
SLLQueuePush(lookup_first, lookup_last, loc);
} else if (current_interp == COFF_SymbolValueInterp_Undefined) {
LNK_Symbol *defn_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, current_parsed.name);
COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_defined(defn_symbol);
COFF_SymbolValueInterpType defn_interp = coff_interp_from_parsed_symbol(defn_parsed);
current_symbol = defn_symbol->u.defined;
if (defn_interp == COFF_SymbolValueInterp_Undefined) {
break;
}
} else {
break;
}
}
scratch_end(scratch);
return current_symbol;
}
internal U64 internal U64
lnk_symbol_hash(String8 string) lnk_symbol_hash(String8 string)
{ {
@@ -539,10 +687,8 @@ THREAD_POOL_TASK_FUNC(lnk_check_anti_dependecy_task)
} }
internal internal
THREAD_POOL_TASK_FUNC(lnk_finalize_weak_symbols_task) THREAD_POOL_TASK_FUNC(lnk_replace_weak_symbols_with_default_symbols_task)
{ {
Temp scratch = scratch_begin(&arena,1);
LNK_FinalizeWeakSymbolsTask *task = raw_task; LNK_FinalizeWeakSymbolsTask *task = raw_task;
LNK_SymbolTable *symtab = task->symtab; LNK_SymbolTable *symtab = task->symtab;
LNK_SymbolHashTrieChunk *chunk = task->chunks[task_id]; LNK_SymbolHashTrieChunk *chunk = task->chunks[task_id];
@@ -552,117 +698,22 @@ THREAD_POOL_TASK_FUNC(lnk_finalize_weak_symbols_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) {
struct LookupLocation { struct LookupLocation *next; LNK_SymbolDefined symbol; B32 is_anti_dependency; }; symbol->u.defined = lnk_default_symbol_from_weak(symtab, symbol->u.defined);
struct LookupLocation *lookup_first = 0, *lookup_last = 0;
LNK_SymbolDefined current_symbol = symbol->u.defined;
for (;;) {
// guard against self-referencing weak symbols
struct LookupLocation *was_visited = 0;
for (struct LookupLocation *l = lookup_first; l != 0; l = l->next) {
if (MemoryCompare(&l->symbol, &current_symbol, sizeof(LNK_SymbolDefined)) == 0) { was_visited = l; break; }
}
if (was_visited) {
Temp temp = temp_begin(scratch.arena);
String8List ref_list = {0};
for (struct LookupLocation *l = lookup_first; l != 0; l = l->next) {
COFF_ParsedSymbol loc_symbol = lnk_parsed_symbol_from_coff_symbol_idx(l->symbol.obj, l->symbol.symbol_idx);
str8_list_pushf(temp.arena, &ref_list, "\t%S Symbol %S (No. %#x) =>", l->symbol.obj->path, loc_symbol.name, l->symbol.symbol_idx);
}
COFF_ParsedSymbol loc_symbol = lnk_parsed_symbol_from_coff_symbol_idx(lookup_first->symbol.obj, lookup_first->symbol.symbol_idx);
str8_list_pushf(temp.arena, &ref_list, "\t%S Symbol %S (No. %#x)", lookup_first->symbol.obj->path, loc_symbol.name, lookup_first->symbol.symbol_idx);
COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx);
String8 loc_string = str8_list_join(temp.arena, &ref_list, &(StringJoin){ .sep = str8_lit("\n") });
lnk_error_obj(LNK_Error_WeakCycle, symbol->u.defined.obj, "unable to resolve cyclic symbol %S; ref chain:\n%S", parsed_symbol.name, loc_string);
MemoryZeroStruct(&current_symbol);
temp_end(temp);
break;
}
COFF_ParsedSymbol current_parsed = lnk_parsed_symbol_from_coff_symbol_idx(current_symbol.obj, current_symbol.symbol_idx);
COFF_SymbolValueInterpType current_interp = coff_interp_symbol(current_parsed.section_number, current_parsed.value, current_parsed.storage_class);
if (current_interp == COFF_SymbolValueInterp_Weak) {
// record visited symbol
struct LookupLocation *loc = push_array(scratch.arena, struct LookupLocation, 1);
loc->symbol = current_symbol;
SLLQueuePush(lookup_first, lookup_last, loc);
// does weak symbol have a definition?
LNK_Symbol *defn_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, current_parsed.name);
COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_coff_symbol_idx(defn_symbol->u.defined.obj, defn_symbol->u.defined.symbol_idx);
COFF_SymbolValueInterpType defn_interp = coff_interp_symbol(defn_parsed.section_number, defn_parsed.value, defn_parsed.storage_class);
if (defn_interp != COFF_SymbolValueInterp_Weak) {
current_symbol = defn_symbol->u.defined;
break;
}
// no definition fallback to the tag
COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(current_parsed, current_symbol.obj->header.is_big_obj);
COFF_ParsedSymbol tag_parsed = lnk_parsed_symbol_from_coff_symbol_idx(current_symbol.obj, weak_ext->tag_index);
COFF_SymbolValueInterpType tag_interp = coff_interp_symbol(tag_parsed.section_number, tag_parsed.value, tag_parsed.storage_class);
current_symbol = (LNK_SymbolDefined){ .obj = current_symbol.obj, .symbol_idx = weak_ext->tag_index };
} else if (current_interp == COFF_SymbolValueInterp_Undefined) {
LNK_Symbol *defn_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, current_parsed.name);
if (defn_symbol == 0) {
MemoryZeroStruct(&current_symbol);
break;
}
current_symbol = defn_symbol->u.defined;
} else {
break;
} }
} }
// replace weak symbol with it's tag
symbol->u.defined = current_symbol;
}
}
scratch_end(scratch);
} }
internal void internal void
lnk_finalize_weak_symbols(TP_Arena *arena, TP_Context *tp, LNK_SymbolTable *symtab) lnk_replace_weak_symbols_with_default_symbols(TP_Context *tp, LNK_SymbolTable *symtab)
{ {
ProfBeginFunction(); ProfBeginFunction();
Temp scratch = scratch_begin(arena->v, arena->count); Temp scratch = scratch_begin(0,0);
U64 chunks_count = 0; U64 chunks_count = 0;
for EachIndex(worker_id, tp->worker_count) { chunks_count += symtab->chunk_lists[LNK_SymbolScope_Defined][worker_id].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);
LNK_SymbolHashTrieChunk **chunks = push_array(scratch.arena, LNK_SymbolHashTrieChunk *, chunks_count);
U64 chunks_cursor = 0;
for EachIndex(worker_id, tp->worker_count) {
for (LNK_SymbolHashTrieChunk *chunk = symtab->chunk_lists[LNK_SymbolScope_Defined][worker_id].first; chunk != 0; chunk = chunk->next) {
chunks[chunks_cursor++] = chunk;
}
}
LNK_FinalizeWeakSymbolsTask task = { .symtab = symtab, .chunks = chunks }; LNK_FinalizeWeakSymbolsTask task = { .symtab = symtab, .chunks = chunks };
tp_for_parallel(tp, 0, chunks_count, lnk_replace_weak_symbols_with_default_symbols_task, &task);
{
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); scratch_end(scratch);
ProfEnd(); ProfEnd();
@@ -708,9 +759,3 @@ lnk_file_off_from_symbol(COFF_SectionHeader **section_table, LNK_Symbol *symbol)
return foff; return foff;
} }
internal COFF_ParsedSymbol
lnk_parsed_symbol_from_defined(LNK_Symbol *symbol)
{
return lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx);
}
+9 -5
View File
@@ -32,10 +32,10 @@ typedef struct LNK_SymbolUndefined
typedef struct LNK_Symbol typedef struct LNK_Symbol
{ {
String8 name; String8 name;
B32 is_live;
union { union {
LNK_SymbolDefined defined; LNK_SymbolDefined defined;
LNK_SymbolLib lib; LNK_SymbolLib lib;
LNK_SymbolUndefined undef;
} u; } u;
} LNK_Symbol; } LNK_Symbol;
@@ -112,7 +112,6 @@ typedef struct
internal LNK_Symbol * lnk_make_defined_symbol(Arena *arena, String8 name, struct LNK_Obj *obj, U32 symbol_idx); internal LNK_Symbol * lnk_make_defined_symbol(Arena *arena, String8 name, struct LNK_Obj *obj, U32 symbol_idx);
internal LNK_Symbol * lnk_make_lib_symbol(Arena *arena, String8 name, struct LNK_Lib *lib, U64 member_offset); internal LNK_Symbol * lnk_make_lib_symbol(Arena *arena, String8 name, struct LNK_Lib *lib, U64 member_offset);
internal LNK_Symbol * lnk_make_undefined_symbol(Arena *arena, String8 name, struct LNK_Obj *obj);
// --- Symbol Containers ------------------------------------------------------ // --- Symbol Containers ------------------------------------------------------
@@ -129,6 +128,13 @@ internal LNK_SymbolArray lnk_symbol_array_from_list(Arena *arena, LNK_Symbol
internal void lnk_symbol_hash_trie_insert_or_replace(Arena *arena, LNK_SymbolHashTrieChunkList *chunks, LNK_SymbolHashTrie **trie, U64 hash, LNK_SymbolScope scope, LNK_Symbol *symbol); internal void lnk_symbol_hash_trie_insert_or_replace(Arena *arena, LNK_SymbolHashTrieChunkList *chunks, LNK_SymbolHashTrie **trie, U64 hash, LNK_SymbolScope scope, LNK_Symbol *symbol);
internal LNK_SymbolHashTrie * lnk_symbol_hash_trie_search(LNK_SymbolHashTrie *trie, U64 hash, String8 name); internal LNK_SymbolHashTrie * lnk_symbol_hash_trie_search(LNK_SymbolHashTrie *trie, U64 hash, String8 name);
internal void lnk_symbol_hash_trie_remove(LNK_SymbolHashTrie *trie); internal void lnk_symbol_hash_trie_remove(LNK_SymbolHashTrie *trie);
internal LNK_SymbolHashTrieChunk ** lnk_array_from_symbol_hash_trie_chunk_list(Arena *arena, LNK_SymbolHashTrieChunkList *lists, U64 lists_count, U64 *count_out);
// --- Symbol Helpers ----------------------------------------------------------
internal COFF_ParsedSymbol lnk_parsed_symbol_from_defined(LNK_Symbol *symbol);
internal B32 lnk_mark_symbol_live(LNK_Symbol *symbol);
internal LNK_SymbolDefined lnk_default_symbol_from_weak(LNK_SymbolTable *symtab, LNK_SymbolDefined symbol);
// --- Symbol Table ------------------------------------------------------------ // --- Symbol Table ------------------------------------------------------------
@@ -139,7 +145,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_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 LNK_Symbol * lnk_symbol_table_searchf(LNK_SymbolTable *symtab, LNK_SymbolScope scope, char *fmt, ...);
internal void lnk_finalize_weak_symbols(TP_Arena *arena, TP_Context *tp, LNK_SymbolTable *symtab); internal void lnk_replace_weak_symbols_with_default_symbols(TP_Context *tp, LNK_SymbolTable *symtab);
// --- Symbol Contrib Helpers -------------------------------------------------- // --- Symbol Contrib Helpers --------------------------------------------------
@@ -149,5 +155,3 @@ internal U64 lnk_sect_off_from_symbol(LNK_Symbol *symbol);
internal U64 lnk_virt_off_from_symbol(COFF_SectionHeader **section_table, LNK_Symbol *symbol); internal U64 lnk_virt_off_from_symbol(COFF_SectionHeader **section_table, LNK_Symbol *symbol);
internal U64 lnk_file_off_from_symbol(COFF_SectionHeader **section_table, LNK_Symbol *symbol); internal U64 lnk_file_off_from_symbol(COFF_SectionHeader **section_table, LNK_Symbol *symbol);
internal COFF_ParsedSymbol lnk_parsed_symbol_from_defined(LNK_Symbol *symbol);