diff --git a/src/linker/codeview_ext/codeview.c b/src/linker/codeview_ext/codeview.c index b427c911..4dceeaa2 100644 --- a/src/linker/codeview_ext/codeview.c +++ b/src/linker/codeview_ext/codeview.c @@ -1606,6 +1606,12 @@ cv_c13_collect_source_file_names(Arena *arena, CV_ChecksumList checksum_list, St // $$Lines +internal void +cv_c13_lines_header_list_concat_in_place(CV_C13LinesHeaderList *list, CV_C13LinesHeaderList *to_concat) +{ + SLLConcatInPlace(list, to_concat); +} + internal CV_C13LinesHeaderList cv_c13_lines_from_sub_sections(Arena *arena, String8 c13_data, Rng1U64 ss_range) { @@ -1840,7 +1846,64 @@ cv_c13_make_lines_accel(Arena *arena, U64 lines_count, CV_LineArray *lines) return accel; } -#if 0 +internal CV_LinesAccel * +cv_lines_accel_from_debug_s(Arena *arena, CV_DebugS debug_s) +{ + // parse $$LINES + U64 c13_lines_count = 0; + CV_LineArray *c13_lines = 0; + { + String8List raw_lines_list = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_Lines); + + for (String8Node *raw_lines_node = raw_lines_list.first; raw_lines_node != 0; raw_lines_node = raw_lines_node->next) { + Temp temp = temp_begin(arena); + CV_C13LinesHeaderList parsed_list = cv_c13_lines_from_sub_sections(temp.arena, raw_lines_node->string, rng_1u64(0, raw_lines_node->string.size)); + c13_lines_count += parsed_list.count; + temp_end(temp); + } + + c13_lines = push_array_no_zero(arena, CV_LineArray, c13_lines_count); + + U64 c13_lines_idx = 0; + for (String8Node *raw_lines_node = raw_lines_list.first; raw_lines_node != 0; raw_lines_node = raw_lines_node->next) { + String8 raw_lines = raw_lines_node->string; + CV_C13LinesHeaderList parsed_list = cv_c13_lines_from_sub_sections(arena, raw_lines, rng_1u64(0, raw_lines.size)); + + for(CV_C13LinesHeaderNode *header_node = parsed_list.first; header_node != 0; header_node = header_node->next) { + c13_lines[c13_lines_idx++] = cv_c13_line_array_from_data(arena, raw_lines, 0, header_node->v); + } + } + } + + return cv_c13_make_lines_accel(arena, c13_lines_count, c13_lines); +} + +internal U64 +cv_nearest_line(CV_Line *arr, U64 count, U64 value) +{ + if(count > 1 && arr[0].voff <= value && value < arr[count-1].voff) + { + U64 l = 0; + U64 r = count - 1; + for (; l <= r; ) { + U64 m = l + (r - l) / 2; + if (arr[m].voff == value) { + return m; + } else if (arr[m].voff < value) { + l = m + 1; + } else { + r = m - 1; + } + } + return l; + } + else if (count == 1 && arr[0].voff == value) + { + return 0; + } + return max_U64; +} + internal CV_Line * cv_line_from_voff(CV_LinesAccel *accel, U64 voff, U64 *out_line_count) { @@ -1849,7 +1912,7 @@ cv_line_from_voff(CV_LinesAccel *accel, U64 voff, U64 *out_line_count) U64 voff_line_count = 0; CV_Line *lines = 0; - U64 map_idx = bsearch_nearest_u64(accel->map, accel->map_count, voff, sizeof(accel->map[0]), OffsetOf(CV_Line, voff)); + U64 map_idx = cv_nearest_line(accel->map, accel->map_count, voff); if(map_idx < accel->map_count) { U64 near_voff = accel->map[map_idx].voff; @@ -1874,7 +1937,6 @@ cv_line_from_voff(CV_LinesAccel *accel, U64 voff, U64 *out_line_count) ProfEnd(); return lines; } -#endif //////////////////////////////// // $$InlineeLines Accel diff --git a/src/linker/codeview_ext/codeview.h b/src/linker/codeview_ext/codeview.h index c8d09c0a..421af3c2 100644 --- a/src/linker/codeview_ext/codeview.h +++ b/src/linker/codeview_ext/codeview.h @@ -455,7 +455,7 @@ internal String8List cv_c13_collect_source_file_names(Arena *arena, CV_ChecksumL // $$Lines internal CV_C13LinesHeaderList cv_c13_lines_from_sub_sections(Arena *arena, String8 c13_data, Rng1U64 ss_range); -internal CV_LineArray cv_c13_line_array_from_data(Arena *arena, String8 c13_data, U64 sec_base, CV_C13LinesHeader parsed_lines); +internal CV_LineArray cv_c13_line_array_from_data(Arena *arena, String8 c13_data, U64 sec_base, CV_C13LinesHeader parsed_lines); // $$InlineeLines internal CV_C13InlineeLinesParsedList cv_c13_inlinee_lines_from_sub_sections(Arena *arena, String8List raw_inlinee_lines); diff --git a/src/linker/lnk.c b/src/linker/lnk.c index 4c4a0acd..11816d90 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -194,6 +194,11 @@ lnk_config_from_argcv(Arena *arena, int argc, char **argv) lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_RemoveSection, ".gfids"); lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_RemoveSection, ".gxfg"); + // set limits on unresolved symbol errors + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_MapLinesForUnresolvedSymbols, ""); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_UnresolvedSymbolLimit, "1000"); + lnk_cmd_line_push_option_if_not_presentf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_UnresolvedSymbolRefLimit, "10"); + // set default max worker count if (lnk_cmd_line_has_switch(cmd_line, LNK_CmdSwitch_Rad_SharedThreadPool)) { lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_SharedThreadPoolMaxWorkers, ""); @@ -1507,22 +1512,24 @@ lnk_link_inputs_(TP_Context *tp, LNK_Symbol *symbol = c->v[i].symbol; if (symbol->is_lib_member_linked) { continue; } + LNK_ObjSymbolRef symbol_ref = lnk_get_obj_symbol_ref(symbol); COFF_ParsedSymbol symbol_parsed = lnk_parse_symbol(symbol); COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol_parsed); + if (symbol_interp == COFF_SymbolValueInterp_Undefined) { U32 member_idx; if (lnk_search_lib(&lib_n->data, symbol->name, &member_idx)) { lnk_queue_lib_member(arena->v[0], &queued_members, symbol, &lib_n->data, member_idx); } } else if (symbol_interp == COFF_SymbolValueInterp_Weak) { - COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(symbol_parsed, symbol->ref.obj->header.is_big_obj); + COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(symbol_parsed, symbol_ref.obj->header.is_big_obj); if (weak_ext->characteristics == COFF_WeakExt_SearchLibrary) { U32 member_idx; if (lnk_search_lib(&lib_n->data, symbol->name, &member_idx)) { lnk_queue_lib_member(arena->v[0], &queued_members, symbol, &lib_n->data, member_idx); } } else if (search_anti_deps && weak_ext->characteristics == COFF_WeakExt_AntiDependency) { - LNK_ObjSymbolRef dep_symbol = lnk_resolve_weak_symbol(symtab, symbol->ref); + LNK_ObjSymbolRef dep_symbol = lnk_resolve_weak_symbol(symtab, symbol_ref); 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) { @@ -1569,8 +1576,9 @@ lnk_link_inputs_(TP_Context *tp, hash_table_push_string_raw(imps->arena, imps->import_stub_ht, import_header.func_name, 0); // create import stub (later replaced with acutal import generated by linker) - LNK_Symbol *import_stub = lnk_symbol_table_search(symtab, str8_lit(LNK_IMPORT_STUB)); - LNK_Symbol *import_symbol = lnk_make_obj_ref_symbol(symtab->arena->v[0], link_symbol->name, import_stub->ref.obj, import_stub->ref.symbol_idx); + LNK_Symbol *import_stub = lnk_symbol_table_search(symtab, str8_lit(LNK_IMPORT_STUB)); + LNK_ObjSymbolRef import_symbol_ref = lnk_get_obj_symbol_ref(import_stub); + LNK_Symbol *import_symbol = lnk_make_obj_ref_symbol(symtab->arena->v[0], link_symbol->name, import_symbol_ref.obj, import_symbol_ref.symbol_idx); lnk_symbol_table_push(symtab, import_symbol); // search DLL symbol list @@ -1936,45 +1944,116 @@ lnk_link_image(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer // { ProfBegin("Report Unresolved Symbols"); - U64 chunks_count = 0; - LNK_SymbolHashTrieChunk **chunks = lnk_array_from_symbol_hash_trie_chunk_list(scratch.arena, symtab->chunks, symtab->arena->count, &chunks_count); - U64 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; - COFF_SymbolValueInterpType symbol_interp = lnk_interp_symbol(symbol); - if (symbol_interp == COFF_SymbolValueInterp_Undefined) { - count += 1; + U64 unresolved_symbols_count = 0; + LNK_Symbol **unresolved_symbols = 0; + { + U64 chunks_count = 0; + LNK_SymbolHashTrieChunk **chunks = lnk_array_from_symbol_hash_trie_chunk_list(scratch.arena, symtab->chunks, symtab->arena->count, &chunks_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; + COFF_SymbolValueInterpType symbol_interp = lnk_interp_symbol(symbol); + if (symbol_interp == COFF_SymbolValueInterp_Undefined) { + unresolved_symbols_count += 1; + } } } - } - U64 cursor = 0; - LNK_Symbol **unresolved = push_array(scratch.arena, LNK_Symbol *, 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; - COFF_SymbolValueInterpType symbol_interp = lnk_interp_symbol(symbol); - if (symbol_interp == COFF_SymbolValueInterp_Undefined) { - unresolved[cursor++] = chunk->v[i].symbol; + unresolved_symbols = push_array(scratch.arena, LNK_Symbol *, unresolved_symbols_count); + if (unresolved_symbols_count) { + U64 cursor = 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 (lnk_interp_symbol(symbol) == COFF_SymbolValueInterp_Undefined) { + unresolved_symbols[cursor++] = chunk->v[i].symbol; + } + } } } + + radsort(unresolved_symbols, unresolved_symbols_count, lnk_symbol_ptr_is_before); } - radsort(unresolved, count, lnk_symbol_ptr_is_before); + for EachIndex(i, unresolved_symbols_count) { + LNK_Symbol *symbol = unresolved_symbols[i]; - for EachIndex(i, count) { - LNK_Symbol *symbol = unresolved[i]; - lnk_error_obj(LNK_Error_UnresolvedSymbol, symbol->ref.obj, "unresolved symbol %S", symbol->name); + if (i > config->unresolved_symbol_limit) { + lnk_error(LNK_Error_UnresolvedSymbol, "too many unresolved symbol errors, stopping now"); + break; + } + + String8List supp_info = {0}; + { + U64 refs_count = 0; + LNK_ObjSymbolRef **refs = lnk_get_obj_symbol_ref_many(scratch.arena, symbol, &refs_count); + for EachIndex(ref_idx, refs_count) { + LNK_ObjSymbolRef *ref = refs[ref_idx]; + LNK_Obj *obj = ref->obj; + COFF_SectionHeader *section_table = lnk_coff_section_table_from_obj(obj); + String8 string_table = lnk_coff_string_table_from_obj(obj); + + CV_DebugS debug_s = {0}; + CV_LinesAccel *debug_lines = 0; + String8 debug_checksums = {0}; + String8 debug_strings = {0}; + + for EachIndex(sect_idx, obj->header.section_count_no_null) { + COFF_SectionHeader *section_header = §ion_table[sect_idx]; + String8 section_name = coff_name_from_section_header(string_table, section_header); + U64 section_number = sect_idx+1; + COFF_RelocArray relocs = lnk_coff_relocs_from_section_header(obj, section_header); + for EachIndex(reloc_idx, relocs.count) { + if (supp_info.node_count > config->unresolved_symbol_ref_limit) { + str8_list_pushf(scratch.arena, &supp_info, "too many unresolved symbol references reported, stopping now"); + break; + } + COFF_Reloc *reloc = &relocs.v[reloc_idx]; + if (reloc->isymbol == ref->symbol_idx) { + U64 line_matches_count = 0; + CV_Line *line_matches = 0; + if (config->map_lines_for_unresolved_symbols == LNK_SwitchState_Yes) { + if (debug_lines == 0) { + debug_s = lnk_debug_s_from_obj(scratch.arena, obj); + debug_lines = cv_lines_accel_from_debug_s(scratch.arena, debug_s); + debug_checksums = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_FileChksms).first->string; + debug_strings = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_StringTable).first->string; + } + line_matches_count = 0; + line_matches = cv_line_from_voff(debug_lines, reloc->apply_off, &line_matches_count); + } + + if (line_matches) { + for EachIndex(i, line_matches_count) { + CV_Line line = line_matches[i]; + CV_C13Checksum checksum = {0}; + String8 file_name = {0}; + str8_deserial_read_struct(debug_checksums, line.file_off, &checksum); + str8_deserial_read_cstr(debug_strings, checksum.name_off, &file_name); + str8_list_pushf(scratch.arena, &supp_info, "%S: %S:%u", lnk_loc_from_obj(scratch.arena, obj), file_name, line.line_num); + } + } else { + str8_list_pushf(scratch.arena, &supp_info, "%S: %S(%llx)+%x", lnk_loc_from_obj(scratch.arena, obj), section_name, section_number, reloc->apply_off); + } + } + } + } + } + } + + lnk_error(LNK_Error_UnresolvedSymbol, "unresolved symbol %S", symbol->name); + lnk_supplement_error_list(supp_info); } // TODO: /FORCE - if (count) { + if (unresolved_symbols_count) { lnk_exit(LNK_Error_UnresolvedSymbol); } + ProfEnd(); } @@ -2054,13 +2133,14 @@ lnk_opt_ref(TP_Context *tp, LNK_SymbolTable *symtab, LNK_Config *config, LNK_Obj // push tasks for each root symbol for (LNK_IncludeSymbolNode *root_n = config->include_symbol_list.first; root_n != 0; root_n = root_n->next) { - LNK_Symbol *root = lnk_symbol_table_search(symtab, root_n->v.name); + LNK_Symbol *root = lnk_symbol_table_search(symtab, root_n->v.name); + LNK_ObjSymbolRef root_ref = lnk_get_obj_symbol_ref(root); struct Task *t = push_array(scratch.arena, struct Task, 1); - t->obj = root->ref.obj; + t->obj = root_ref.obj; t->relocs.count = 1; t->relocs.v = push_array(scratch.arena, COFF_Reloc, 1); - t->relocs.v[0].isymbol = root->ref.symbol_idx; + t->relocs.v[0].isymbol = root_ref.symbol_idx; SLLStackPush(task_stack, t); } @@ -2113,20 +2193,20 @@ lnk_opt_ref(TP_Context *tp, LNK_SymbolTable *symtab, LNK_Config *config, LNK_Obj if (ref_interp == COFF_SymbolValueInterp_Regular) { LNK_Symbol *symlink = lnk_obj_get_comdat_symlink(ref_symbol.obj, ref_parsed.section_number); if (symlink) { - ref_symbol = symlink->ref; + ref_symbol = lnk_get_obj_symbol_ref(symlink); } break; } else if (ref_interp == COFF_SymbolValueInterp_Undefined) { if (reloc_parsed.storage_class == COFF_SymStorageClass_External) { LNK_Symbol *defn = lnk_symbol_table_search(symtab, ref_parsed.name); - next_ref = defn->ref; + next_ref = lnk_get_obj_symbol_ref(defn); } else { MemoryZeroStruct(&ref_symbol); break; } } else if (ref_interp == COFF_SymbolValueInterp_Weak) { LNK_Symbol *defn = lnk_symbol_table_search(symtab, ref_parsed.name); - next_ref = defn->ref; + next_ref = lnk_get_obj_symbol_ref(defn); } else { break; } @@ -2227,14 +2307,14 @@ lnk_resolve_symbol(LNK_SymbolTable *symtab, LNK_ObjSymbolRef symbol, LNK_ObjSymb switch (symbol_interp) { case COFF_SymbolValueInterp_Regular: { LNK_Symbol *symlink = lnk_obj_get_comdat_symlink(symbol.obj, symbol_parsed.section_number); - *symbol_out = symlink ? symlink->ref : symbol; + *symbol_out = symlink ? lnk_get_obj_symbol_ref(symlink) : symbol; } break; case COFF_SymbolValueInterp_Weak: { LNK_Symbol *defn = lnk_symbol_table_search(symtab, symbol_parsed.name); - COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_coff_symbol_idx(defn->ref.obj, defn->ref.symbol_idx); - COFF_SymbolValueInterpType defn_interp = coff_interp_symbol(defn_parsed.section_number, defn_parsed.value, defn_parsed.storage_class); + COFF_ParsedSymbol defn_parsed = lnk_parse_symbol(defn); + COFF_SymbolValueInterpType defn_interp = lnk_interp_symbol(defn); if (defn_interp != COFF_SymbolValueInterp_Undefined) { - *symbol_out = defn->ref; + *symbol_out = lnk_get_obj_symbol_ref(defn); } else { is_resolved = 0; } @@ -2242,19 +2322,19 @@ lnk_resolve_symbol(LNK_SymbolTable *symtab, LNK_ObjSymbolRef symbol, LNK_ObjSymb case COFF_SymbolValueInterp_Undefined: { LNK_Symbol *defn = lnk_symbol_table_search(symtab, symbol_parsed.name); if (defn) { - *symbol_out = defn->ref; + *symbol_out = lnk_get_obj_symbol_ref(defn); } else { is_resolved = 0; } } break; case COFF_SymbolValueInterp_Common: { LNK_Symbol *defn = lnk_symbol_table_search(symtab, symbol_parsed.name); - *symbol_out = defn->ref; + *symbol_out = lnk_get_obj_symbol_ref(defn); } break; case COFF_SymbolValueInterp_Abs: { if (symbol_parsed.storage_class == COFF_SymStorageClass_External) { LNK_Symbol *defn = lnk_symbol_table_search(symtab, symbol_parsed.name); - *symbol_out = defn->ref; + *symbol_out = lnk_get_obj_symbol_ref(defn); } else { *symbol_out = symbol; } @@ -2379,7 +2459,8 @@ THREAD_POOL_TASK_FUNC(lnk_set_comdat_leaders_contribs_task) if (symlink == 0) { continue; } COFF_ParsedSymbol symlink_parsed = lnk_parse_symbol(symlink); - task->sect_map[obj_idx][sect_idx] = task->sect_map[symlink->ref.obj->input_idx][symlink_parsed.section_number - 1]; + LNK_ObjSymbolRef symlink_ref = lnk_get_obj_symbol_ref(symlink); + task->sect_map[obj_idx][sect_idx] = task->sect_map[symlink_ref.obj->input_idx][symlink_parsed.section_number - 1]; } ProfEnd(); } @@ -2422,29 +2503,32 @@ THREAD_POOL_TASK_FUNC(lnk_patch_comdat_leaders_task) COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); if (interp == COFF_SymbolValueInterp_Regular) { LNK_Symbol *symlink = lnk_obj_get_comdat_symlink(obj, symbol.section_number); - if (symlink && symlink->ref.obj != obj) { - U32 section_number; - U32 value; - if (symbol.storage_class == COFF_SymStorageClass_External) { - // COMDAT leader may be at a different offset, so update this symbol with leader's offset - COFF_ParsedSymbol parsed_symlink = lnk_parse_symbol(symlink); - section_number = symbol.section_number; - value = parsed_symlink.value; - } else { - // COMDAT section may have static symbols which are now invalid to relocate against - section_number = LNK_REMOVED_SECTION_NUMBER_32; - value = max_U32; - task->u.patch_symtabs.was_symbol_patched[obj_idx][symbol_idx] = 1; - } + if (symlink) { + LNK_ObjSymbolRef symlink_ref = lnk_get_obj_symbol_ref(symlink); + if (symlink_ref.obj != obj) { + U32 section_number; + U32 value; + if (symbol.storage_class == COFF_SymStorageClass_External) { + // COMDAT leader may be at a different offset, so update this symbol with leader's offset + COFF_ParsedSymbol parsed_symlink = lnk_parse_symbol(symlink); + section_number = symbol.section_number; + value = parsed_symlink.value; + } else { + // COMDAT section may have static symbols which are now invalid to relocate against + section_number = LNK_REMOVED_SECTION_NUMBER_32; + value = max_U32; + task->u.patch_symtabs.was_symbol_patched[obj_idx][symbol_idx] = 1; + } - if (obj->header.is_big_obj) { - COFF_Symbol32 *symbol32 = symbol.raw_symbol; - symbol32->section_number = section_number; - symbol32->value = value; - } else { - COFF_Symbol16 *symbol16 = symbol.raw_symbol; - symbol16->section_number = (U16)section_number; - symbol16->value = value; + if (obj->header.is_big_obj) { + COFF_Symbol32 *symbol32 = symbol.raw_symbol; + symbol32->section_number = section_number; + symbol32->value = value; + } else { + COFF_Symbol16 *symbol16 = symbol.raw_symbol; + symbol16->section_number = (U16)section_number; + symbol16->value = value; + } } } } @@ -2504,11 +2588,11 @@ THREAD_POOL_TASK_FUNC(lnk_patch_common_block_leaders_task) for (U64 contrib_idx = contrib_range.min; contrib_idx < contrib_range.max; contrib_idx += 1) { LNK_CommonBlockContrib *contrib = &task->u.patch_symtabs.common_block_contribs[contrib_idx]; LNK_Symbol *symbol = contrib->symbol; - LNK_Obj *obj = symbol->ref.obj; - COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol->ref.symbol_idx); + LNK_ObjSymbolRef symbol_ref = lnk_get_obj_symbol_ref(symbol); + COFF_ParsedSymbol parsed_symbol = lnk_parse_symbol(symbol); U64 section_number = task->u.patch_symtabs.common_block_sect->sect_idx + 1; - if (obj->header.is_big_obj) { + if (symbol_ref.obj->header.is_big_obj) { COFF_Symbol32 *symbol32 = parsed_symbol.raw_symbol; symbol32->value = contrib->u.offset; symbol32->section_number = safe_cast_u32(section_number); @@ -2518,7 +2602,7 @@ THREAD_POOL_TASK_FUNC(lnk_patch_common_block_leaders_task) symbol16->section_number = safe_cast_u16(section_number); } - task->u.patch_symtabs.was_symbol_patched[obj->input_idx][symbol->ref.symbol_idx] = 1; + task->u.patch_symtabs.was_symbol_patched[symbol_ref.obj->input_idx][symbol_ref.symbol_idx] = 1; } ProfEnd(); @@ -2536,11 +2620,10 @@ THREAD_POOL_TASK_FUNC(lnk_patch_common_block_symbols_task) for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) { symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - if (interp == COFF_SymbolValueInterp_Common) { LNK_Symbol *defn = lnk_symbol_table_search(task->symtab, symbol.name); - COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_coff_symbol_idx(defn->ref.obj, defn->ref.symbol_idx); - Assert(coff_interp_symbol(defn_parsed.section_number, defn_parsed.value, defn_parsed.storage_class) == COFF_SymbolValueInterp_Regular); + COFF_ParsedSymbol defn_parsed = lnk_parse_symbol(defn); + Assert(lnk_interp_symbol(defn) == COFF_SymbolValueInterp_Regular); if (defn) { if (obj->header.is_big_obj) { COFF_Symbol32 *symbol32 = symbol.raw_symbol; @@ -2720,13 +2803,10 @@ THREAD_POOL_TASK_FUNC(lnk_obj_reloc_patcher) Rng1U64 section_frange = rng_1u64(section_header->foff, section_header->foff + section_header->fsize); String8 section_data = str8_substr(data, section_frange); - // find section relocs - COFF_RelocInfo reloc_info = coff_reloc_info_from_section_header(obj->data, section_header); - COFF_Reloc *relocs = (COFF_Reloc *)(obj->data.str + reloc_info.array_off); - // apply relocs - for EachIndex(reloc_idx, reloc_info.count) { - COFF_Reloc *reloc = &relocs[reloc_idx]; + COFF_RelocArray relocs = lnk_coff_relocs_from_section_header(obj, section_header); + for EachIndex(reloc_idx, relocs.count) { + COFF_Reloc *reloc = &relocs.v[reloc_idx]; // error check relocation if (obj->header.machine == COFF_MachineType_X64) { diff --git a/src/linker/lnk_config.c b/src/linker/lnk_config.c index 79a413a4..8a1bb9a6 100644 --- a/src/linker/lnk_config.c +++ b/src/linker/lnk_config.c @@ -121,38 +121,41 @@ global read_only LNK_CmdSwitch g_cmd_switch_map[] = { LNK_CmdSwitch_NotImplemented, 0, "WX", "", "" }, //- internal switches - { LNK_CmdSwitch_Rad_Age, 0, "RAD_AGE", ":#", "Age embeded in EXE and PDB, used to validate incremental build. Default is 1." }, - { LNK_CmdSwitch_Rad_BuildInfo, 0, "RAD_BUILD_INFO", "", "Print build info and exit." }, - { LNK_CmdSwitch_Rad_CheckUnusedDelayLoadDll, 0, "RAD_CHECK_UNUSED_DELAY_LOAD_DLL", "[:NO]", "" }, - { LNK_CmdSwitch_Rad_Map, 0, "RAD_MAP", ":FILENAME", "Emit file with the output image's layout description." }, - { LNK_CmdSwitch_Rad_MemoryMapFiles, 0, "RAD_MEMORY_MAP_FILES", "[:NO]", "When enabled, files are memory-mapped instead of being read entirely on request." }, - { LNK_CmdSwitch_Rad_Debug, 0, "RAD_DEBUG", "[:NO]", "Emit RAD debug info file." }, - { LNK_CmdSwitch_Rad_DebugAltPath, 0, "RAD_DEBUGALTPATH", "", "" }, - { LNK_CmdSwitch_Rad_DebugName, 0, "RAD_DEBUG_NAME", ":FILENAME", "Sets file name for RAD debug info file." }, - { LNK_CmdSwitch_Rad_DelayBind, 0, "RAD_DELAY_BIND", "[:NO]", "" }, - { LNK_CmdSwitch_Rad_DoMerge, 0, "RAD_DO_MERGE", "[:NO]", "" }, - { LNK_CmdSwitch_Rad_EnvLib, 0, "RAD_ENV_LIB", "[:NO]", "" }, - { LNK_CmdSwitch_Rad_Exe, 0, "RAD_EXE", "[:NO]", "" }, - { LNK_CmdSwitch_Rad_Guid, 0, "RAD_GUID", ":{IMAGEBLAKE3|XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXXXXXX}", "" }, - { LNK_CmdSwitch_Rad_LargePages, 0, "RAD_LARGE_PAGES", "[:NO]", "Disabled by default on Windows." }, - { LNK_CmdSwitch_Rad_LinkVer, 0, "RAD_LINK_VER", ":##,##", "" }, - { LNK_CmdSwitch_Rad_Log, 0, "RAD_LOG", ":{ALL,INPUT_OBJ,INPUT_LIB,IO,LINK_STATS,TIMERS}", "" }, - { LNK_CmdSwitch_Rad_MtPath, 0, "RAD_MT_PATH", ":EXEPATH", "Exe path to manifest tool, default: " LNK_MANIFEST_MERGE_TOOL_NAME }, - { LNK_CmdSwitch_Rad_OsVer, 0, "RAD_OS_VER", ":##,##", "" }, - { LNK_CmdSwitch_Rad_PageSize, 0, "RAD_PAGE_SIZE", ":#", "Must be power of two." }, - { LNK_CmdSwitch_Rad_PathStyle, 0, "RAD_PATH_STYLE", ":{WindowsAbsolute|UnixAbsolute}", "" }, - { LNK_CmdSwitch_Rad_PdbHashTypeNameLength, 0, "RAD_PDB_HASH_TYPE_NAME_LENGTH", ":#", "Number of hash bytes to use to replace type name. Default 8 bytes (Max 16)." }, - { LNK_CmdSwitch_Rad_PdbHashTypeNameMap, 0, "RAD_PDB_HASH_TYPE_NAME_MAP", ":FILENAME", "Produce map file with hash -> type name mappings." }, - { LNK_CmdSwitch_Rad_PdbHashTypeNames, 0, "RAD_PDB_HASH_TYPE_NAMES", ":{NONE|LENIENT|FULL}", "Replace type names in LF_STRUCTURE and LF_CLASS with hashes." }, - { LNK_CmdSwitch_Rad_RemoveSection, 0, "RAD_REMOVE_SECTION", ":NAME", "Removes a section from output image." }, - { LNK_CmdSwitch_Rad_SharedThreadPool, 0, "RAD_SHARED_THREAD_POOL", "[:STRING]", "Default value \"" LNK_DEFAULT_THREAD_POOL_NAME "\"" }, - { LNK_CmdSwitch_Rad_SharedThreadPoolMaxWorkers, 0, "RAD_SHARED_THREAD_POOL_MAX_WORKERS", ":#", "Sets maximum number of workers in a thread pool." }, - { LNK_CmdSwitch_Rad_SuppressError, 0, "RAD_SUPPRESS_ERROR", ":#", "" }, - { LNK_CmdSwitch_Rad_TargetOs, 0, "RAD_TARGET_OS", ":{WINDOWS,LINUX,MAC}" }, - { LNK_CmdSwitch_Rad_WriteTempFiles, 0, "RAD_WRITE_TEMP_FILES", "[:NO]", "When speicifed linker writes image and debug info to temporary files and renames after link is done." }, - { LNK_CmdSwitch_Rad_TimeStamp, 0, "RAD_TIME_STAMP", ":#", "Time stamp embeded in EXE and PDB." }, - { LNK_CmdSwitch_Rad_Version, 0, "RAD_VERSION", "", "Print version and exit." }, - { LNK_CmdSwitch_Rad_Workers, 0, "RAD_WORKERS", ":#", "Sets number of workers created in the pool. Number is capped at 1024. When /RAD_SHARED_THREAD_POOL is specified this number cant exceed /RAD_SHARED_THREAD_POOL_MAX_WORKERS." }, + { LNK_CmdSwitch_Rad_Age, 0, "RAD_AGE", ":#", "Age embeded in EXE and PDB, used to validate incremental build. Default is 1." }, + { LNK_CmdSwitch_Rad_BuildInfo, 0, "RAD_BUILD_INFO", "", "Print build info and exit." }, + { LNK_CmdSwitch_Rad_CheckUnusedDelayLoadDll, 0, "RAD_CHECK_UNUSED_DELAY_LOAD_DLL", "[:NO]", "" }, + { LNK_CmdSwitch_Rad_Map, 0, "RAD_MAP", ":FILENAME", "Emit file with the output image's layout description." }, + { LNK_CmdSwitch_Rad_MapLinesForUnresolvedSymbols, 0, "RAD_MAP_LINES_FOR_UNRESOLVED_SYMBOLS", "[:NO]", "Use debug info to print source file location for unresolved symbol" }, + { LNK_CmdSwitch_Rad_MemoryMapFiles, 0, "RAD_MEMORY_MAP_FILES", "[:NO]", "When enabled, files are memory-mapped instead of being read entirely on request." }, + { LNK_CmdSwitch_Rad_Debug, 0, "RAD_DEBUG", "[:NO]", "Emit RAD debug info file." }, + { LNK_CmdSwitch_Rad_DebugAltPath, 0, "RAD_DEBUGALTPATH", "", "" }, + { LNK_CmdSwitch_Rad_DebugName, 0, "RAD_DEBUG_NAME", ":FILENAME", "Sets file name for RAD debug info file." }, + { LNK_CmdSwitch_Rad_DelayBind, 0, "RAD_DELAY_BIND", "[:NO]", "" }, + { LNK_CmdSwitch_Rad_DoMerge, 0, "RAD_DO_MERGE", "[:NO]", "" }, + { LNK_CmdSwitch_Rad_EnvLib, 0, "RAD_ENV_LIB", "[:NO]", "" }, + { LNK_CmdSwitch_Rad_Exe, 0, "RAD_EXE", "[:NO]", "" }, + { LNK_CmdSwitch_Rad_Guid, 0, "RAD_GUID", ":{IMAGEBLAKE3|XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXXXXXX}", "" }, + { LNK_CmdSwitch_Rad_LargePages, 0, "RAD_LARGE_PAGES", "[:NO]", "Disabled by default on Windows." }, + { LNK_CmdSwitch_Rad_LinkVer, 0, "RAD_LINK_VER", ":##,##", "" }, + { LNK_CmdSwitch_Rad_Log, 0, "RAD_LOG", ":{ALL,INPUT_OBJ,INPUT_LIB,IO,LINK_STATS,TIMERS}", "" }, + { LNK_CmdSwitch_Rad_MtPath, 0, "RAD_MT_PATH", ":EXEPATH", "Exe path to manifest tool, default: " LNK_MANIFEST_MERGE_TOOL_NAME }, + { LNK_CmdSwitch_Rad_OsVer, 0, "RAD_OS_VER", ":##,##", "" }, + { LNK_CmdSwitch_Rad_PageSize, 0, "RAD_PAGE_SIZE", ":#", "Must be power of two." }, + { LNK_CmdSwitch_Rad_PathStyle, 0, "RAD_PATH_STYLE", ":{WindowsAbsolute|UnixAbsolute}", "" }, + { LNK_CmdSwitch_Rad_PdbHashTypeNameLength, 0, "RAD_PDB_HASH_TYPE_NAME_LENGTH", ":#", "Number of hash bytes to use to replace type name. Default 8 bytes (Max 16)." }, + { LNK_CmdSwitch_Rad_PdbHashTypeNameMap, 0, "RAD_PDB_HASH_TYPE_NAME_MAP", ":FILENAME", "Produce map file with hash -> type name mappings." }, + { LNK_CmdSwitch_Rad_PdbHashTypeNames, 0, "RAD_PDB_HASH_TYPE_NAMES", ":{NONE|LENIENT|FULL}", "Replace type names in LF_STRUCTURE and LF_CLASS with hashes." }, + { LNK_CmdSwitch_Rad_RemoveSection, 0, "RAD_REMOVE_SECTION", ":NAME", "Removes a section from output image." }, + { LNK_CmdSwitch_Rad_SharedThreadPool, 0, "RAD_SHARED_THREAD_POOL", "[:STRING]", "Default value \"" LNK_DEFAULT_THREAD_POOL_NAME "\"" }, + { LNK_CmdSwitch_Rad_SharedThreadPoolMaxWorkers, 0, "RAD_SHARED_THREAD_POOL_MAX_WORKERS", ":#", "Sets maximum number of workers in a thread pool." }, + { LNK_CmdSwitch_Rad_SuppressError, 0, "RAD_SUPPRESS_ERROR", ":#", "" }, + { LNK_CmdSwitch_Rad_TargetOs, 0, "RAD_TARGET_OS", ":{WINDOWS,LINUX,MAC}" }, + { LNK_CmdSwitch_Rad_WriteTempFiles, 0, "RAD_WRITE_TEMP_FILES", "[:NO]", "When speicifed linker writes image and debug info to temporary files and renames after link is done." }, + { LNK_CmdSwitch_Rad_TimeStamp, 0, "RAD_TIME_STAMP", ":#", "Time stamp embeded in EXE and PDB." }, + { LNK_CmdSwitch_Rad_UnresolvedSymbolLimit, 0, "RAD_UNRESOLVED_SYMBOL_LIMIT", ":#", "Limits number of unresolved symbol errors linker reports." }, + { LNK_CmdSwitch_Rad_UnresolvedSymbolRefLimit, 0, "RAD_UNRESOLVED_SYMBOL_REF_LIMIT", ":#", "Limit number of unresolved symbol references linker reports." }, + { LNK_CmdSwitch_Rad_Version, 0, "RAD_VERSION", "", "Print version and exit." }, + { LNK_CmdSwitch_Rad_Workers, 0, "RAD_WORKERS", ":#", "Sets number of workers created in the pool. Number is capped at 1024. When /RAD_SHARED_THREAD_POOL is specified this number cant exceed /RAD_SHARED_THREAD_POOL_MAX_WORKERS." }, { LNK_CmdSwitch_Help, 0, "HELP", "", "" }, { LNK_CmdSwitch_Help, 0, "?", "", "" }, @@ -1776,6 +1779,10 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List config->rad_chunk_map = LNK_SwitchState_Yes; } break; + case LNK_CmdSwitch_Rad_MapLinesForUnresolvedSymbols: { + lnk_cmd_switch_parse_flag(obj, cmd_switch, value_strings, &config->map_lines_for_unresolved_symbols); + } break; + case LNK_CmdSwitch_Rad_MemoryMapFiles: { lnk_cmd_switch_set_flag_32(obj, cmd_switch, value_strings, &config->io_flags, LNK_IO_Flags_MemoryMapFiles); } break; @@ -2005,6 +2012,14 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List lnk_cmd_switch_parse_u32(obj, cmd_switch, value_strings, &config->time_stamp, 0); } break; + case LNK_CmdSwitch_Rad_UnresolvedSymbolLimit: { + lnk_cmd_switch_parse_u64(obj, cmd_switch, value_strings, &config->unresolved_symbol_limit, 0); + } break; + + case LNK_CmdSwitch_Rad_UnresolvedSymbolRefLimit: { + lnk_cmd_switch_parse_u64(obj, cmd_switch, value_strings, &config->unresolved_symbol_ref_limit, 0); + } break; + case LNK_CmdSwitch_Rad_Version: { fprintf(stdout, "%s\n", BUILD_TITLE_STRING_LITERAL); os_abort(0); diff --git a/src/linker/lnk_config.h b/src/linker/lnk_config.h index 780a2e12..5c81ab44 100644 --- a/src/linker/lnk_config.h +++ b/src/linker/lnk_config.h @@ -164,6 +164,7 @@ typedef enum LNK_CmdSwitch_Rad_Log, LNK_CmdSwitch_Rad_Logo, LNK_CmdSwitch_Rad_Map, + LNK_CmdSwitch_Rad_MapLinesForUnresolvedSymbols, LNK_CmdSwitch_Rad_MemoryMapFiles, LNK_CmdSwitch_Rad_MtPath, LNK_CmdSwitch_Rad_OsVer, @@ -178,6 +179,8 @@ typedef enum LNK_CmdSwitch_Rad_SuppressError, LNK_CmdSwitch_Rad_TargetOs, LNK_CmdSwitch_Rad_TimeStamp, + LNK_CmdSwitch_Rad_UnresolvedSymbolLimit, + LNK_CmdSwitch_Rad_UnresolvedSymbolRefLimit, LNK_CmdSwitch_Rad_Version, LNK_CmdSwitch_Rad_Workers, LNK_CmdSwitch_Rad_WriteTempFiles, @@ -409,6 +412,9 @@ typedef struct LNK_Config HashTable *include_symbol_ht; HashTable *delay_load_ht; HashTable *disallow_lib_ht; + U64 unresolved_symbol_limit; + U64 unresolved_symbol_ref_limit; + LNK_SwitchState map_lines_for_unresolved_symbols; } LNK_Config; // --- MSVC Error Codes -------------------------------------------------------- diff --git a/src/linker/lnk_debug_helper.c b/src/linker/lnk_debug_helper.c index 6c0da272..7d1b7017 100644 --- a/src/linker/lnk_debug_helper.c +++ b/src/linker/lnk_debug_helper.c @@ -72,4 +72,3 @@ lnk_make_dll_import_debug_symbols(Arena *arena, COFF_MachineType machine, String return debug_symbols; } - diff --git a/src/linker/lnk_debug_info.c b/src/linker/lnk_debug_info.c index 44d38728..5b07d9a5 100644 --- a/src/linker/lnk_debug_info.c +++ b/src/linker/lnk_debug_info.c @@ -3021,9 +3021,10 @@ THREAD_POOL_TASK_FUNC(lnk_build_pdb_public_symbols_defined_task) U64 node_idx = 0; for EachIndex(i, chunk->count) { LNK_Symbol *symbol = chunk->v[i].symbol; + LNK_ObjSymbolRef symbol_ref = lnk_get_obj_symbol_ref(symbol); COFF_ParsedSymbol symbol_parsed = lnk_parse_symbol(symbol); - if (symbol_parsed.section_number == lnk_obj_get_removed_section_number(symbol->ref.obj)) { continue; } + if (symbol_parsed.section_number == lnk_obj_get_removed_section_number(symbol_ref.obj)) { continue; } COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol_parsed); if (symbol_interp != COFF_SymbolValueInterp_Regular) { continue; } diff --git a/src/linker/lnk_error.h b/src/linker/lnk_error.h index 2c34eee4..7a7e4a28 100644 --- a/src/linker/lnk_error.h +++ b/src/linker/lnk_error.h @@ -37,7 +37,6 @@ typedef enum LNK_Error_IllegalSectionMerge, LNK_Error_IllegalRelocation, LNK_Error_CircularMerge, - LNK_Error_UnresolvedSymbol, LNK_Error_AssociativeLoop, LNK_Error_AlternateNameConflict, LNK_Error_RelocationAgainstRemovedSection, @@ -55,6 +54,7 @@ typedef enum LNK_Error_UndefinedIsWeak, LNK_Error_WeakCycle, LNK_Error_InvalidLib, + LNK_Error_UnresolvedSymbol, LNK_Error_Last, LNK_Warning_First, diff --git a/src/linker/lnk_obj.c b/src/linker/lnk_obj.c index fc5abf37..50e2ecf6 100644 --- a/src/linker/lnk_obj.c +++ b/src/linker/lnk_obj.c @@ -1,11 +1,25 @@ // Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) +internal String8 +lnk_loc_from_obj(Arena *arena, LNK_Obj *obj) +{ + String8 obj_path = str8_skip_last_slash(obj ? obj->path : str8_lit("RADLINK")); + String8 lib_path = str8_skip_last_slash(lnk_obj_get_lib_path(obj)); + String8 result; + if (lib_path.size) { + result = push_str8f(arena, "%S(%S)", lib_path, obj_path); + } else { + result = push_str8_copy(arena, obj_path); + } + return result; +} + internal void lnk_error_obj(LNK_ErrorCode code, LNK_Obj *obj, char *fmt, ...) { va_list args; va_start(args, fmt); - String8 obj_path = obj ? obj->path : str8_lit("RADLINK"); + String8 obj_path = obj ? obj->path : str8_zero(); String8 lib_path = lnk_obj_get_lib_path(obj); lnk_error_with_loc_fv(code, obj_path, lib_path, fmt, args); va_end(args); @@ -494,12 +508,27 @@ lnk_coff_section_header_from_section_number(LNK_Obj *obj, U64 section_number) return section_header; } +internal COFF_RelocArray +lnk_coff_relocs_from_section_header(LNK_Obj *obj, COFF_SectionHeader *section_header) +{ + COFF_RelocInfo reloc_info = coff_reloc_info_from_section_header(obj->data, section_header); + COFF_Reloc *relocs = (COFF_Reloc *)(obj->data.str + reloc_info.array_off); + COFF_RelocArray result = { .count = reloc_info.count, .v = relocs }; + return result; +} + internal COFF_SectionHeader * lnk_coff_section_table_from_obj(LNK_Obj *obj) { return (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; } +internal String8 +lnk_coff_string_table_from_obj(LNK_Obj *obj) +{ + return str8_substr(obj->data, obj->header.string_table_range); +} + internal COFF_RelocArray lnk_coff_reloc_info_from_section_number(LNK_Obj *obj, U64 section_number) { @@ -684,3 +713,47 @@ lnk_directive_info_from_raw_directives(Arena *arena, LNK_Obj *obj, String8List r return directive_info; } +internal CV_DebugS +lnk_debug_s_from_obj(Arena *arena, LNK_Obj *obj) +{ + Temp scratch = scratch_begin(&arena, 1); + + String8List raw_debug_s = {0}; + { + COFF_SectionHeader *section_table = lnk_coff_section_table_from_obj(obj); + String8 string_table = lnk_coff_string_table_from_obj(obj); + for EachIndex(sect_idx, obj->header.section_count_no_null) { + COFF_SectionHeader *section_header = §ion_table[sect_idx]; + String8 section_name = coff_name_from_section_header(string_table, section_header); + if (str8_match(section_name, str8_lit(".debug$S"), 0)) { + String8 debug_s = str8_substr(obj->data, rng_1u64(section_header->foff, section_header->foff + section_header->fsize)); + str8_list_push(scratch.arena, &raw_debug_s, debug_s); + } + } + } + + CV_DebugS debug_s = {0}; + { + for (String8Node *node = raw_debug_s.first; node != 0; node = node->next) { + // parse & merge sub sections + CV_DebugS ds = cv_parse_debug_s(scratch.arena, node->string); + cv_debug_s_concat_in_place(&debug_s, &ds); + + // make sure there is one string table + String8List string_data_list = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_StringTable); + if (string_data_list.node_count > 1) { + break; + } + + // make sure there is one file checksum table + String8List checksum_data_list = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_FileChksms); + if (checksum_data_list.node_count > 1) { + continue; + } + } + } + + scratch_end(scratch); + return debug_s; +} + diff --git a/src/linker/lnk_obj.h b/src/linker/lnk_obj.h index 48a4c880..3f449725 100644 --- a/src/linker/lnk_obj.h +++ b/src/linker/lnk_obj.h @@ -89,6 +89,7 @@ typedef struct // --- Error ------------------------------------------------------------------- +internal String8 lnk_loc_from_obj(Arena *arena, LNK_Obj *obj); internal void lnk_error_obj(LNK_ErrorCode code, LNK_Obj *obj, char *fmt, ...); internal void lnk_error_input_obj(LNK_ErrorCode code, struct LNK_Input *input, char *fmt, ...); @@ -115,7 +116,9 @@ internal LNK_Symbol * lnk_obj_get_comdat_symlink(LNK_Obj *obj, U64 section_n internal COFF_ParsedSymbol lnk_parsed_symbol_from_coff(LNK_Obj *obj, void *coff_symbol); internal COFF_ParsedSymbol lnk_parsed_symbol_from_coff_symbol_idx(LNK_Obj *obj, U64 symbol_idx); internal COFF_SectionHeader * lnk_coff_section_header_from_section_number(LNK_Obj *obj, U64 section_number); +internal COFF_RelocArray lnk_coff_relocs_from_section_header(LNK_Obj *obj, COFF_SectionHeader *section_header); internal COFF_SectionHeader * lnk_coff_section_table_from_obj(LNK_Obj *obj); +internal String8 lnk_coff_string_table_from_obj(LNK_Obj *obj); internal B32 lnk_try_comdat_props_from_section_number(LNK_Obj *obj, U32 section_number, COFF_ComdatSelectType *select_out, U32 *section_number_out, U32 *section_length_out, U32 *check_sum_out); internal B32 lnk_is_coff_section_debug(LNK_Obj *obj, U64 sect_idx); @@ -130,3 +133,7 @@ internal void lnk_parse_msvc_linker_directive(Arena *arena, LNK_Obj internal String8List lnk_raw_directives_from_obj(Arena *arena, LNK_Obj *obj); internal LNK_DirectiveInfo lnk_directive_info_from_raw_directives(Arena *arena, LNK_Obj *obj, String8List raw_directives); +// --- Debug Info -------------------------------------------------------------- + +internal CV_DebugS lnk_debug_s_from_obj(Arena *arena, LNK_Obj *obj); + diff --git a/src/linker/lnk_symbol_table.c b/src/linker/lnk_symbol_table.c index 024d5569..1759be9c 100644 --- a/src/linker/lnk_symbol_table.c +++ b/src/linker/lnk_symbol_table.c @@ -4,30 +4,51 @@ internal LNK_Symbol * lnk_make_obj_ref_symbol(Arena *arena, String8 name, struct LNK_Obj *obj, U32 symbol_idx) { + LNK_ObjSymbolRefNode *ref = push_array(arena, LNK_ObjSymbolRefNode, 1); + ref->v.obj = obj; + ref->v.symbol_idx = symbol_idx; + LNK_Symbol *symbol = push_array(arena, LNK_Symbol, 1); symbol->name = name; - symbol->ref.obj = obj; - symbol->ref.symbol_idx = symbol_idx; + symbol->refs = ref; + return symbol; } +internal int +lnk_obj_symbol_ref_is_before(void *raw_a, void *raw_b) +{ + LNK_ObjSymbolRef *a_ref = raw_a; + LNK_ObjSymbolRef *b_ref = raw_b; + LNK_Lib *a_lib = lnk_obj_get_lib(a_ref->obj); + LNK_Lib *b_lib = lnk_obj_get_lib(b_ref->obj); + U32 a_lib_input_idx = a_lib ? a_lib->input_idx : 0; + U32 b_lib_input_idx = b_lib ? b_lib->input_idx : 0; + + if (a_lib_input_idx == b_lib_input_idx) { + if (a_ref->obj->input_idx == b_ref->obj->input_idx) { + return a_ref->symbol_idx < b_ref->symbol_idx; + } + return a_ref->obj->input_idx < b_ref->obj->input_idx; + } + + return a_lib_input_idx < b_lib_input_idx; +} + +internal int +lnk_obj_symbol_ref_ptr_is_before(void *raw_a, void *raw_b) +{ + LNK_ObjSymbolRef **a = raw_a, **b = raw_b; + return lnk_obj_symbol_ref_is_before(*a, *b); +} + internal B32 lnk_symbol_is_before(void *raw_a, void *raw_b) { LNK_Symbol *a = raw_a, *b = raw_b; - - LNK_Lib *a_lib = lnk_obj_get_lib(a->ref.obj); - LNK_Lib *b_lib = lnk_obj_get_lib(b->ref.obj); - U32 a_lib_input_idx = a_lib ? a_lib->input_idx : 0; - U32 b_lib_input_idx = b_lib ? b_lib->input_idx : 0; - - if (a_lib_input_idx == b_lib_input_idx) { - if (a->ref.obj->input_idx == b->ref.obj->input_idx) { - return a->ref.symbol_idx < b->ref.symbol_idx; - } - return a->ref.obj->input_idx < b->ref.obj->input_idx; - } - return a_lib_input_idx < b_lib_input_idx; + LNK_ObjSymbolRef a_ref = lnk_get_obj_symbol_ref(a); + LNK_ObjSymbolRef b_ref = lnk_get_obj_symbol_ref(b); + return lnk_obj_symbol_ref_is_before(&a_ref, &b_ref); } internal B32 @@ -120,7 +141,9 @@ lnk_symbol_hash_trie_chunk_list_push(Arena *arena, LNK_SymbolHashTrieChunkList * internal void lnk_error_multiply_defined_symbol(LNK_Symbol *dst, LNK_Symbol *src) { - lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, dst->ref.obj, "symbol \"%S\" (No. %#x) is multiply defined in %S (No. %#x)", dst->name, dst->ref.symbol_idx, src->ref.obj->path, src->ref.symbol_idx); + LNK_ObjSymbolRef dst_ref = lnk_get_obj_symbol_ref(dst); + LNK_ObjSymbolRef src_ref = lnk_get_obj_symbol_ref(src); + lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, dst_ref.obj, "symbol \"%S\" (No. %#x) is multiply defined in %S (No. %#x)", dst->name, dst_ref.symbol_idx, src_ref.obj->path, src_ref.symbol_idx); } internal B32 @@ -128,12 +151,14 @@ lnk_can_replace_symbol(LNK_Symbol *dst, LNK_Symbol *src) { B32 can_replace = 0; - LNK_Obj *dst_obj = dst->ref.obj; - LNK_Obj *src_obj = src->ref.obj; - COFF_ParsedSymbol dst_parsed = lnk_parsed_symbol_from_coff_symbol_idx(dst->ref.obj, dst->ref.symbol_idx); - COFF_ParsedSymbol src_parsed = lnk_parsed_symbol_from_coff_symbol_idx(src->ref.obj, src->ref.symbol_idx); - COFF_SymbolValueInterpType dst_interp = coff_interp_from_parsed_symbol(dst_parsed); - COFF_SymbolValueInterpType src_interp = coff_interp_from_parsed_symbol(src_parsed); + COFF_ParsedSymbol dst_parsed = lnk_parse_symbol(dst); + COFF_ParsedSymbol src_parsed = lnk_parse_symbol(src); + COFF_SymbolValueInterpType dst_interp = lnk_interp_symbol(dst); + COFF_SymbolValueInterpType src_interp = lnk_interp_symbol(src); + LNK_ObjSymbolRef dst_ref = lnk_get_obj_symbol_ref(dst); + LNK_ObjSymbolRef src_ref = lnk_get_obj_symbol_ref(src); + LNK_Obj *dst_obj = dst_ref.obj; + LNK_Obj *src_obj = src_ref.obj; // undefined vs regular if (dst_interp == COFF_SymbolValueInterp_Undefined && src_interp == COFF_SymbolValueInterp_Regular) { @@ -151,7 +176,8 @@ lnk_can_replace_symbol(LNK_Symbol *dst, LNK_Symbol *src) weak_parsed = src_parsed; } - COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(weak_parsed, weak->ref.obj->header.is_big_obj); + LNK_ObjSymbolRef weak_symbol_ref = lnk_get_obj_symbol_ref(weak); + COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(weak_parsed, weak_symbol_ref.obj->header.is_big_obj); if (weak_ext->characteristics == COFF_WeakExt_SearchLibrary) { // NOTE: MSVC does not let a weak symbol to replace an undefined one, // but LLD links without errors or warnings, meaning undefined symbols @@ -214,8 +240,8 @@ lnk_can_replace_symbol(LNK_Symbol *dst, LNK_Symbol *src) } // weak vs weak else if (dst_interp == COFF_SymbolValueInterp_Weak && src_interp == COFF_SymbolValueInterp_Weak) { - COFF_SymbolWeakExt *dst_ext = coff_parse_weak_tag(dst_parsed, dst->ref.obj->header.is_big_obj); - COFF_SymbolWeakExt *src_ext = coff_parse_weak_tag(src_parsed, src->ref.obj->header.is_big_obj); + COFF_SymbolWeakExt *dst_ext = coff_parse_weak_tag(dst_parsed, dst_ref.obj->header.is_big_obj); + COFF_SymbolWeakExt *src_ext = coff_parse_weak_tag(src_parsed, src_ref.obj->header.is_big_obj); if ((dst_ext->characteristics == COFF_WeakExt_SearchAlias && src_ext->characteristics != COFF_WeakExt_SearchAlias)) { if (lnk_symbol_is_before(dst, src) || src_ext->characteristics == COFF_WeakExt_AntiDependency) { can_replace = 0; @@ -250,7 +276,7 @@ lnk_can_replace_symbol(LNK_Symbol *dst, LNK_Symbol *src) U32 dst_section_length; U32 dst_check_sum; if (dst_interp == COFF_SymbolValueInterp_Regular) { - dst_is_comdat = lnk_try_comdat_props_from_section_number(dst->ref.obj, dst_parsed.section_number, &dst_select, 0, &dst_section_length, &dst_check_sum); + dst_is_comdat = lnk_try_comdat_props_from_section_number(dst_ref.obj, dst_parsed.section_number, &dst_select, 0, &dst_section_length, &dst_check_sum); } else if (dst_interp == COFF_SymbolValueInterp_Common) { dst_select = COFF_ComdatSelect_Largest; dst_section_length = dst_parsed.value; @@ -264,7 +290,7 @@ lnk_can_replace_symbol(LNK_Symbol *dst, LNK_Symbol *src) U32 src_section_length, src_checks; U32 src_check_sum; if (src_interp == COFF_SymbolValueInterp_Regular) { - src_is_comdat = lnk_try_comdat_props_from_section_number(src->ref.obj, src_parsed.section_number, &src_select, 0, &src_section_length, &src_check_sum); + src_is_comdat = lnk_try_comdat_props_from_section_number(src_ref.obj, src_parsed.section_number, &src_select, 0, &src_section_length, &src_check_sum); } else if (src_interp == COFF_SymbolValueInterp_Common) { src_select = COFF_ComdatSelect_Largest; src_section_length = src_parsed.value; @@ -356,29 +382,38 @@ lnk_can_replace_symbol(LNK_Symbol *dst, LNK_Symbol *src) internal void lnk_on_symbol_replace(LNK_Symbol *dst, LNK_Symbol *src) { - COFF_ParsedSymbol dst_parsed = lnk_parsed_symbol_from_coff_symbol_idx(dst->ref.obj, dst->ref.symbol_idx); - COFF_SymbolValueInterpType dst_interp = coff_interp_from_parsed_symbol(dst_parsed); + COFF_ParsedSymbol dst_parsed = lnk_parse_symbol(dst); + COFF_SymbolValueInterpType dst_interp = lnk_interp_symbol(dst); + LNK_ObjSymbolRef dst_ref = lnk_get_obj_symbol_ref(dst); + if (dst_interp == COFF_SymbolValueInterp_Regular) { // remove replaced section from the output - COFF_SectionHeader *dst_sect = lnk_coff_section_header_from_section_number(dst->ref.obj, dst_parsed.section_number); + COFF_SectionHeader *dst_sect = lnk_coff_section_header_from_section_number(dst_ref.obj, dst_parsed.section_number); dst_sect->flags |= COFF_SectionFlag_LnkRemove; // remove associated sections from the output - for (U32Node *associated_section = dst->ref.obj->associated_sections[dst_parsed.section_number]; + for (U32Node *associated_section = dst_ref.obj->associated_sections[dst_parsed.section_number]; associated_section != 0; associated_section = associated_section->next) { - COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(dst->ref.obj, associated_section->data); + COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(dst_ref.obj, associated_section->data); section_header->flags |= COFF_SectionFlag_LnkRemove; } } + // merge symbol refs + LNK_ObjSymbolRefNode *src_last_ref; + for (src_last_ref = src->refs; src_last_ref->next != 0; src_last_ref = src_last_ref->next); + src_last_ref->next = dst->refs; + // assert leader section is live #if BUILD_DEBUG { - COFF_ParsedSymbol src_parsed = lnk_parsed_symbol_from_coff_symbol_idx(src->ref.obj, src->ref.symbol_idx); - COFF_SymbolValueInterpType src_interp = coff_interp_from_parsed_symbol(src_parsed); + COFF_ParsedSymbol src_parsed = lnk_parse_symbol(src); + COFF_SymbolValueInterpType src_interp = lnk_interp_symbol(src); + LNK_ObjSymbolRef src_ref = lnk_get_obj_symbol_ref(src); + if (src_interp == COFF_SymbolValueInterp_Regular) { - COFF_SectionHeader *src_sect = lnk_coff_section_header_from_section_number(src->ref.obj, src_parsed.section_number); + COFF_SectionHeader *src_sect = lnk_coff_section_header_from_section_number(src_ref.obj, src_parsed.section_number); AssertAlways(~src_sect->flags & COFF_SectionFlag_LnkRemove); } } @@ -505,10 +540,42 @@ lnk_array_from_symbol_hash_trie_chunk_list(Arena *arena, LNK_SymbolHashTrieChunk return chunks; } +internal LNK_ObjSymbolRef +lnk_get_obj_symbol_ref(LNK_Symbol *symbol) +{ + return symbol->refs->v; +} + +internal U64 +lnk_get_obj_symbol_ref_count(LNK_Symbol *symbol) +{ + U64 count = 0; + for (LNK_ObjSymbolRefNode *node = symbol->refs; node != 0; node = node->next, count += 1); + return count; +} + +internal LNK_ObjSymbolRef ** +lnk_get_obj_symbol_ref_many(Arena *arena, LNK_Symbol *symbol, U64 *count_out) +{ + // TODO: would be simpler if we sorted refs on insert/update + U64 refs_count = lnk_get_obj_symbol_ref_count(symbol); + LNK_ObjSymbolRef **refs = push_array(arena, LNK_ObjSymbolRef *, refs_count); + U64 i = 0; + for (LNK_ObjSymbolRefNode *node = symbol->refs; node != 0; node = node->next, i += 1) { + refs[i] = &node->v; + } + radsort(refs, refs_count, lnk_obj_symbol_ref_ptr_is_before); + if (count_out) { + *count_out = refs_count; + } + return refs; +} + internal COFF_ParsedSymbol lnk_parse_symbol(LNK_Symbol *symbol) { - return lnk_parsed_symbol_from_coff_symbol_idx(symbol->ref.obj, symbol->ref.symbol_idx); + LNK_ObjSymbolRef ref = lnk_get_obj_symbol_ref(symbol); + return lnk_parsed_symbol_from_coff_symbol_idx(ref.obj, ref.symbol_idx); } internal COFF_SymbolValueInterpType @@ -559,10 +626,10 @@ lnk_resolve_weak_symbol(LNK_SymbolTable *symtab, LNK_ObjSymbolRef symbol) // does weak symbol have a definition? LNK_Symbol *defn_symbol = lnk_symbol_table_search(symtab, current_parsed.name); - COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_coff_symbol_idx(defn_symbol->ref.obj, defn_symbol->ref.symbol_idx); + COFF_ParsedSymbol defn_parsed = lnk_parse_symbol(defn_symbol); 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->ref; + current_symbol = lnk_get_obj_symbol_ref(defn_symbol); break; } @@ -588,7 +655,7 @@ lnk_resolve_weak_symbol(LNK_SymbolTable *symtab, LNK_ObjSymbolRef symbol) if (defn_interp == COFF_SymbolValueInterp_Undefined) { break; } // follow symbol definition - current_symbol = defn_symbol->ref; + current_symbol = lnk_get_obj_symbol_ref(defn_symbol); } else { break; } } @@ -666,15 +733,16 @@ THREAD_POOL_TASK_FUNC(lnk_replace_weak_with_default_symbol_task) LNK_SymbolHashTrieChunk *chunk = task->chunks[task_id]; for EachIndex(i, chunk->count) { LNK_Symbol *symbol = chunk->v[i].symbol; + LNK_ObjSymbolRef symbol_ref = lnk_get_obj_symbol_ref(symbol); COFF_ParsedSymbol symbol_parsed = lnk_parse_symbol(symbol); COFF_SymbolValueInterpType symbol_interp = coff_interp_from_parsed_symbol(symbol_parsed); if (symbol_interp == COFF_SymbolValueInterp_Weak) { - LNK_ObjSymbolRef resolve = lnk_resolve_weak_symbol(symtab, symbol->ref); + LNK_ObjSymbolRef resolve = lnk_resolve_weak_symbol(symtab, symbol_ref); 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->ref.obj->header.is_big_obj); - if (symbol->ref.obj->header.is_big_obj) { + COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(resolve_parsed, symbol_ref.obj->header.is_big_obj); + if (symbol_ref.obj->header.is_big_obj) { COFF_Symbol32 *symbol32 = symbol_parsed.raw_symbol; symbol32->section_number = COFF_Symbol_UndefinedSection; symbol32->value = 0; @@ -686,7 +754,7 @@ THREAD_POOL_TASK_FUNC(lnk_replace_weak_with_default_symbol_task) symbol16->storage_class = COFF_SymStorageClass_External; } } else { - symbol->ref = resolve; + symbol->refs->v = resolve; } } } @@ -721,7 +789,7 @@ lnk_replace_weak_with_default_symbols(TP_Context *tp, LNK_SymbolTable *symtab) internal ISectOff lnk_sc_from_symbol(LNK_Symbol *symbol) { - COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->ref.obj, symbol->ref.symbol_idx); + COFF_ParsedSymbol parsed_symbol = lnk_parse_symbol(symbol); ISectOff sc = {0}; sc.isect = parsed_symbol.section_number; diff --git a/src/linker/lnk_symbol_table.h b/src/linker/lnk_symbol_table.h index cc633568..78bd1291 100644 --- a/src/linker/lnk_symbol_table.h +++ b/src/linker/lnk_symbol_table.h @@ -11,11 +11,17 @@ typedef struct LNK_ObjSymbolRef U32 symbol_idx; } LNK_ObjSymbolRef; +typedef struct LNK_ObjSymbolRefNode +{ + struct LNK_ObjSymbolRefNode *next; + LNK_ObjSymbolRef v; +} LNK_ObjSymbolRefNode; + typedef struct LNK_Symbol { - String8 name; - B8 is_lib_member_linked; - LNK_ObjSymbolRef ref; + String8 name; + B8 is_lib_member_linked; + LNK_ObjSymbolRefNode *refs; } LNK_Symbol; // --- Symbol Containers ------------------------------------------------------- @@ -109,6 +115,8 @@ internal LNK_SymbolHashTrieChunk ** lnk_array_from_symbol_hash_trie_chunk_list(A // --- Symbol Helpers ---------------------------------------------------------- +internal LNK_ObjSymbolRef lnk_get_obj_symbol_ref(LNK_Symbol *symbol); +internal U64 lnk_get_obj_symbol_ref_count(LNK_Symbol *symbol); internal COFF_ParsedSymbol lnk_parse_symbol(LNK_Symbol *symbol); internal COFF_SymbolValueInterpType lnk_interp_symbol(LNK_Symbol *symbol); internal LNK_ObjSymbolRef lnk_resolve_weak_symbol(LNK_SymbolTable *symtab, LNK_ObjSymbolRef symbol);