improve unresolved symbols diagnostic

* show referenced by function
* show inline site that references unresolved symbol instead of the top function
This commit is contained in:
Nikita Smith authored and Ryan Fleury committed 2026-08-21 10:56:14 -07:00
1 parent 4eae2a2ecc
commit 2abbae4d90
10 files changed
+724 -122

No files matched your search

-1
View File
@@ -131,7 +131,6 @@ cv_map_encoded_base_pointer(CV_Arch arch, U32 encoded_frame_reg)
return r;
}
////////////////////////////////
//~ rjf: Enum -> String
+1 -1
View File
@@ -1161,7 +1161,7 @@ struct CV_SymPub32
//- (SymKind: LPROC32, GPROC32)
#define CV_IsProc32(x) (x == CV_SymKind_LPROC32_ID || x == CV_SymKind_GPROC32_ID || x == CV_SymKind_LPROC32_DPC)
#define CV_IsProc32(x) ((x) == CV_SymKind_GPROC32 || (x) == CV_SymKind_LPROC32 || (x) == CV_SymKind_GPROC32_ID || (x) == CV_SymKind_LPROC32_ID || (x) == CV_SymKind_LPROC32_DPC || (x) == CV_SymKind_LPROC32_DPC_ID)
typedef struct CV_SymProc32 CV_SymProc32;
struct CV_SymProc32
+1 -1
View File
@@ -232,7 +232,7 @@ coff_pick_reloc_value_x64(COFF_Reloc_X64 type,
reloc_value = safe_cast_s32(symbol_virtual_offset - reloc_virtual_offset - (4 + 5));
} break;
case COFF_Reloc_X64_Section: {
reloc_value_size = 4;
reloc_value_size = 2;
reloc_value = symbol_section_number;
} break;
case COFF_Reloc_X64_SecRel: {
+14 -40
View File
@@ -1452,6 +1452,7 @@ cv_c13_lines_from_sub_sections(Arena *arena, String8 c13_data, Rng1U64 ss_range)
String8 sub_sect_data = str8_substr(c13_data, ss_range);
for (U64 cursor = 0; cursor + sizeof(CV_C13SubSecLinesHeader) <= sub_sect_data.size; ) {
U64 header_off = cursor;
CV_C13SubSecLinesHeader *hdr = (CV_C13SubSecLinesHeader *)(sub_sect_data.str + cursor);
cursor += sizeof(*hdr);
@@ -1486,6 +1487,7 @@ cv_c13_lines_from_sub_sections(Arena *arena, String8 c13_data, Rng1U64 ss_range)
lines_parsed_node->next = 0;
CV_C13LinesHeader *lines_parsed = &lines_parsed_node->v;
lines_parsed->header_off = ss_range.min + header_off;
lines_parsed->sec_idx = hdr->sec;
lines_parsed->sec_off_lo = hdr->sec_off;
lines_parsed->sec_off_hi = hdr->sec_off + hdr->len;
@@ -1633,6 +1635,12 @@ cv_c13_voff_map_compar(const void *raw_a, const void *raw_b)
return cmp;
}
force_inline int
cv_c13_voff_map_is_before(void *raw_a, void *raw_b)
{
return cv_c13_voff_map_compar(raw_a, raw_b) < 0;
}
internal CV_LinesAccel *
cv_c13_make_lines_accel(Arena *arena, U64 lines_count, CV_LineArray *lines)
{
@@ -1676,56 +1684,22 @@ cv_c13_make_lines_accel(Arena *arena, U64 lines_count, CV_LineArray *lines)
return accel;
}
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 r = count;
for (; l < r; ) {
U64 m = l + (r - l) / 2;
if (arr[m].voff == value) {
return m;
} else if (arr[m].voff < value) {
if (arr[m].voff <= value) {
l = m + 1;
} else {
r = m - 1;
r = m;
}
}
return l;
return l - 1;
}
else if (count == 1 && arr[0].voff == value)
{
@@ -1754,7 +1728,7 @@ cv_line_from_voff(CV_LinesAccel *accel, U64 voff, U64 *out_line_count)
lines = accel->map + map_idx;
for(; map_idx < (accel->map_count-1); map_idx += 1) {
for(; map_idx < accel->map_count; map_idx += 1) {
if(accel->map[map_idx].voff != near_voff) {
break;
}
+1
View File
@@ -97,6 +97,7 @@ typedef struct CV_File
typedef struct CV_C13LinesHeader
{
U64 header_off;
U64 sec_idx;
U64 sec_off_lo;
U64 sec_off_hi;
+7
View File
@@ -841,6 +841,13 @@ hash_map_search_u64_u64(HashMap *hm, U64 key)
return n ? &n->v.value.value_u64 : 0;
}
internal String8 *
hash_map_search_u64_string(HashMap *hm, U64 key)
{
HashMapNode *n = hash_map_search(hm, hash_map_hasher(str8_struct(&key)), (HashMapKey){ .key_u64 = key }, hash_map_match_u64);
return n ? &n->v.value.value_string : 0;
}
internal void *
hash_map_search_raw_raw(HashMap *hm, void *key)
{
+1
View File
@@ -185,6 +185,7 @@ internal String8 * hash_map_search_path_string (HashMap *hm, String8 key);
internal void * hash_map_search_path_raw (HashMap *hm, String8 key);
internal void * hash_map_search_u64_raw (HashMap *hm, U64 key);
internal U64 * hash_map_search_u64_u64 (HashMap *hm, U64 key);
internal String8 * hash_map_search_u64_string (HashMap *hm, U64 key);
internal void * hash_map_search_raw_raw (HashMap *hm, void *key);
internal U64 * hash_map_search_raw_u64 (HashMap *hm, void *key);
+96 -62
View File
@@ -2310,6 +2310,7 @@ lnk_link_image(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer
{
ProfBegin("Report Unresolved Symbols");
// collect unresolved symbols
U64 unresolved_symbols_count = 0;
LNK_Symbol **unresolved_symbols = 0;
{
@@ -2345,8 +2346,11 @@ lnk_link_image(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer
}
if (unresolved_symbols_count) {
Temp debug_scratch = scratch_begin(&scratch.arena, 1);
Temp debug_scratch = temp_begin(scratch.arena);
LNK_ObjSymbolMap **func_symbol_maps = push_array(debug_scratch.arena, LNK_ObjSymbolMap *, link->objs.count);
LNK_ObjLineMap **line_maps = push_array(debug_scratch.arena, LNK_ObjLineMap *, link->objs.count);
HashMap message_hm = {0};
for EachIndex(i, unresolved_symbols_count) {
LNK_Symbol *symbol = unresolved_symbols[i];
@@ -2355,82 +2359,112 @@ lnk_link_image(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer
break;
}
String8List supp_info = {0};
{
U64 refs_count = 0;
LNK_ObjSymbolRef **refs = lnk_ref_from_symbol_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);
String8List ref_messages = {0};
U64 refs_count = 0;
LNK_ObjSymbolRef **refs = lnk_ref_from_symbol_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);
Temp debug_temp = temp_begin(debug_scratch.arena);
CV_DebugS debug_s = {0};
CV_LinesAccel *debug_lines = 0;
String8 debug_checksums = {0};
String8 debug_strings = {0};
if (func_symbol_maps[obj->input_idx] == 0) {
func_symbol_maps[obj->input_idx] = lnk_symbol_map_from_obj(debug_scratch.arena, obj);
}
if (config->map_lines_for_unresolved_symbols == LNK_SwitchState_Yes) {
if (line_maps[obj->input_idx] == 0) {
line_maps[obj->input_idx] = lnk_line_map_from_obj(debug_scratch.arena, obj);
}
}
for EachIndex(sect_idx, obj->header.section_count_no_null) {
if (obj->section_flags[sect_idx] & LNK_SECTION_FLAG_DEBUG) { continue; }
for EachIndex(sect_idx, obj->header.section_count_no_null) {
if (obj->section_flags[sect_idx] & LNK_SECTION_FLAG_DEBUG) { continue; }
if (obj->section_flags[sect_idx] & COFF_SectionFlag_LnkRemove) { continue; }
COFF_SectionHeader *section_header = &section_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");
temp_end(debug_temp);
goto next_undefined_symbol;
}
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(debug_temp.arena, obj);
String8List raw_checksums = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_FileChksms);
String8List raw_strings = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_StringTable);
debug_lines = cv_lines_accel_from_debug_s(debug_temp.arena, debug_s);
debug_checksums = str8_list_first(&raw_checksums);
debug_strings = str8_list_first(&raw_strings);
}
line_matches_count = 0;
line_matches = cv_line_from_voff(debug_lines, reloc->apply_off, &line_matches_count);
COFF_SectionHeader *section_header = &section_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 (ref_messages.node_count > config->unresolved_symbol_ref_limit) {
str8_list_pushf(scratch.arena, &ref_messages, "too many unresolved symbol references reported, stopping now");
goto next_undefined_symbol;
}
COFF_Reloc *reloc = &relocs.v[reloc_idx];
if (reloc->isymbol != ref->symbol_idx) { continue; }
String8 ref_name = {0};
String8 source_file = {0};
CV_Line *source_line = 0;
if (config->map_lines_for_unresolved_symbols == LNK_SwitchState_Yes) {
LNK_ObjLineMap *line_map = line_maps[obj->input_idx];
// is reference inline site?
LNK_InlineSite *inline_site = lnk_inline_site_from_section_offset(line_map, safe_cast_u32(section_number), reloc->apply_off);
if (inline_site && inline_site->name.size) {
CV_Line *line_inline = lnk_line_from_inline_site(inline_site, reloc->apply_off);
if (line_inline) {
ref_name = inline_site->name;
source_line = line_inline;
}
}
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(debug_temp.arena, obj), file_name, line.line_num);
}
} else {
str8_list_pushf(scratch.arena, &supp_info, "%S: %S(%llx)+%x", lnk_loc_from_obj(debug_temp.arena, obj), section_name, section_number, reloc->apply_off);
// default to regular line table
if (source_line == 0) {
U64 line_matches_count = 0;
CV_Line *line_matches = lnk_lines_from_section_offset(line_map, safe_cast_u32(section_number), reloc->apply_off, &line_matches_count);
source_line = line_matches_count ? &line_matches[0] : 0;
}
if (source_line) {
// read source file name
CV_C13Checksum checksum = {0};
B32 is_invalid = source_line->line_num == 0 ||
str8_deserial_read_struct(line_map->debug_checksums, source_line->file_off, &checksum) != sizeof(checksum) ||
str8_deserial_read_cstr(line_map->debug_strings, checksum.name_off, &source_file) == 0;
// no file name? -> invalidate source info
if (is_invalid) {
ref_name = str8_zero();
source_file = str8_zero();
source_line = 0;
}
}
}
// no inline site? -> lookup function in the COFF symbol table
if (ref_name.size == 0) {
U32 symbol_idx = lnk_symbol_from_section_offset(func_symbol_maps[obj->input_idx], safe_cast_u32(section_number), reloc->apply_off);
ref_name = symbol_idx != max_U32 ? lnk_symbol_name_from_coff_symbol_idx(obj, symbol_idx) : str8_lit("(N/A)");
}
// pick location format
String8 ref_location = source_line ?
push_str8f(scratch.arena, "%S(%u)", source_file, source_line->line_num) :
push_str8f(scratch.arena, "%S[%llx]+%x", section_name, section_number, reloc->apply_off);
// push unique reference error
String8 message = push_str8f(scratch.arena, "%S: referenced by '%S' in %S", ref_location, ref_name, lnk_loc_from_obj(scratch.arena, obj));
if (hash_map_search_string_u64(&message_hm, message) == 0) {
str8_list_push(scratch.arena, &ref_messages, message);
hash_map_push_string_u64(scratch.arena, &message_hm, message, 1);
}
}
temp_end(debug_temp);
}
next_undefined_symbol:;
}
next_undefined_symbol:;
lnk_error(LNK_Error_UnresolvedSymbol, "unresolved symbol %S", symbol->name);
lnk_supplement_error_list(supp_info);
lnk_error(LNK_Error_UnresolvedSymbol, "unresolved symbol '%S'", symbol->name);
lnk_supplement_error_list(ref_messages);
}
scratch_end(debug_scratch);
}
if (!config->force) {
lnk_exit(LNK_Error_UnresolvedSymbol);
}
if (unresolved_symbols_count && !config->force) {
lnk_exit(LNK_Error_UnresolvedSymbol);
temp_end(debug_scratch);
}
ProfEnd();
+527 -12
View File
@@ -118,17 +118,16 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer)
str8_ends_with(sect_name, str8_lit("$fo_bdd$"), 0)) {
section_flags[sect_idx] |= COFF_SectionFlag_LnkInfo;
}
if (task->find_debug_t) {
if (str8_match(sect_name, str8_lit(".debug$T"), 0)) {
debug_t_sect_idx = sect_idx;
} else if (str8_match(sect_name, str8_lit(".debug$P"), 0)) {
debug_p_sect_idx = sect_idx;
} else if (str8_match(sect_name, str8_lit(".debug$H"), 0)) {
debug_h_sect_idx = sect_idx;
}
if (str8_match(sect_name, str8_lit(".debug$T"), 0)) {
debug_t_sect_idx = sect_idx;
} else if (str8_match(sect_name, str8_lit(".debug$P"), 0)) {
debug_p_sect_idx = sect_idx;
} else if (str8_match(sect_name, str8_lit(".debug$H"), 0)) {
debug_h_sect_idx = sect_idx;
}
if (task->find_llvm_addrsig && llvm_addrsig_sect_idx == max_U32 &&
str8_match(sect_name, str8_lit(".llvm_addrsig"), 0)) {
if (llvm_addrsig_sect_idx == max_U32 && str8_match(sect_name, str8_lit(".llvm_addrsig"), 0)) {
llvm_addrsig_sect_idx = sect_idx;
}
@@ -394,8 +393,6 @@ lnk_obj_from_input_many(TP_Context *tp, TP_Arena *arena, LNK_Config *config, U64
.inputs = inputs,
.objs = objs,
.machine = config->machine,
.find_debug_t = lnk_do_debug_info(config),
.find_llvm_addrsig = config->opt_icf == LNK_SwitchState_Yes,
};
tp_for_parallel(tp, arena, inputs_count, lnk_obj_initer, &task);
}
@@ -929,6 +926,74 @@ lnk_directive_info_from_raw_directives(Arena *arena, LNK_Obj *obj, String8List r
return directive_info;
}
force_inline int
lnk_section_offset_symbol_is_before(void *raw_a, void *raw_b)
{
LNK_SectionOffsetSymbol *a = raw_a, *b = raw_b;
if (a->key == b->key) {
return a->symbol_idx < b->symbol_idx;
}
return a->key < b->key;
}
internal LNK_ObjSymbolMap *
lnk_symbol_map_from_obj(Arena *arena, LNK_Obj *obj)
{
// count functions
U64 count = 0;
COFF_ParsedSymbol symbol;
for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += 1 + symbol.aux_symbol_count) {
symbol = lnk_parsed_symbol_from_coff_symbol_idx_no_name(obj, symbol_idx);
if (coff_interp_from_parsed_symbol(symbol) == COFF_SymbolValueInterp_Regular && COFF_SymbolType_IsFunc(symbol.type)) {
count += 1;
}
}
LNK_ObjSymbolMap *map = push_array(arena, LNK_ObjSymbolMap, 1);
map->v = push_array_no_zero(arena, LNK_SectionOffsetSymbol, count);
U64 map_idx = 0;
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_no_name(obj, symbol_idx);
if (coff_interp_from_parsed_symbol(symbol) == COFF_SymbolValueInterp_Regular && COFF_SymbolType_IsFunc(symbol.type)) {
LNK_SectionOffsetSymbol *entry = &map->v[map->count++];
entry->key = Compose64Bit(symbol.section_number, symbol.value);
entry->symbol_idx = safe_cast_u32(symbol_idx);
}
}
Assert(map->count == count);
radsort(map->v, map->count, lnk_section_offset_symbol_is_before);
return map;
}
internal U32
lnk_symbol_from_section_offset(LNK_ObjSymbolMap *map, U32 section_number, U32 offset)
{
U64 key = Compose64Bit(section_number, offset);
U64 min = 0;
U64 opl = map->count;
while (min < opl) {
U64 mid = min + (opl - min) / 2;
if (map->v[mid].key <= key) {
min = mid + 1;
} else {
opl = mid;
}
}
U32 symbol_idx = max_U32;
if (min > 0) {
LNK_SectionOffsetSymbol *entry = &map->v[min - 1];
if (entry->key >> 32 == section_number) {
symbol_idx = entry->symbol_idx;
}
}
return symbol_idx;
}
internal CV_DebugS
lnk_debug_s_from_obj(Arena *arena, LNK_Obj *obj)
{
@@ -970,3 +1035,453 @@ lnk_debug_s_from_obj(Arena *arena, LNK_Obj *obj)
scratch_end(scratch);
return debug_s;
}
force_inline int
lnk_line_table_block_is_before(void *raw_a, void *raw_b)
{
LNK_LineTableBlock *a = raw_a, *b = raw_b;
U64 key_a = Compose64Bit(a->header->sec_idx, a->header->sec_off_lo);
U64 key_b = Compose64Bit(b->header->sec_idx, b->header->sec_off_lo);
if (key_a == key_b) { return a->header->sec_off_hi < b->header->sec_off_hi; }
return key_a < key_b;
}
force_inline int
lnk_inline_range_is_before(void *raw_a, void *raw_b)
{
LNK_InlineRange *a = raw_a, *b = raw_b;
if (a->key_min != b->key_min) { return a->key_min < b->key_min; }
return a->site_idx < b->site_idx;
}
internal LNK_ObjLineMap *
lnk_line_map_from_obj(Arena *arena, LNK_Obj *obj)
{
Temp scratch = scratch_begin(&arena, 1);
//
// build .debug$S section file offset -> relocation value
//
#define DebugRelocValueFromFileOffset(foff) hash_map_search_u64_u64(&debug_reloc_hm, foff)
HashMap debug_reloc_hm = {0};
for EachIndex(sect_idx, obj->header.section_count_no_null) {
LNK_ObjSection section = lnk_obj_section_from_sect_idx(obj, sect_idx);
if (!str8_match(lnk_obj_section_name_from_sect_idx(obj, sect_idx), str8_lit(".debug$S"), 0)) { continue; }
String8 section_data = str8_substr(obj->data, section.frange);
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];
COFF_ParsedSymbol symbol = lnk_parsed_symbol_from_coff_symbol_idx_no_name(obj, reloc->isymbol);
if (coff_interp_from_parsed_symbol(symbol) != COFF_SymbolValueInterp_Regular) { continue; }
U64 reloc_size = 0;
U64 reloc_value = 0;
if ((obj->header.machine == COFF_MachineType_X64 && reloc->type == COFF_Reloc_X64_Section) ||
(obj->header.machine == COFF_MachineType_X86 && reloc->type == COFF_Reloc_X86_Section)) {
reloc_size = sizeof(U16);
reloc_value = symbol.section_number;
} else if ((obj->header.machine == COFF_MachineType_X64 && reloc->type == COFF_Reloc_X64_SecRel) ||
(obj->header.machine == COFF_MachineType_X86 && reloc->type == COFF_Reloc_X86_SecRel)) {
reloc_size = sizeof(U32);
reloc_value = symbol.value;
}
if (reloc_size > 0 && reloc->apply_off + reloc_size <= section_data.size) {
U64 addend = 0;
str8_deserial_read(section_data, reloc->apply_off, &addend, reloc_size, 1);
U64 foff = section.frange.min + reloc->apply_off;
U64 value = reloc_value + extend_sign64(addend, reloc_size);
U64 *existing = hash_map_search_u64_u64(&debug_reloc_hm, foff);
if (existing) {
*existing = value;
} else {
hash_map_push_u64_u64(scratch.arena, &debug_reloc_hm, foff, value);
}
}
}
}
CV_DebugS debug_s = lnk_debug_s_from_obj(scratch.arena, obj);
String8List raw_lines_list = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_Lines);
String8List raw_checksums = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_FileChksms);
String8List raw_strings = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_StringTable);
//
// estimate requried line table block capacity
//
U64 line_table_blocks_cap = 0;
for EachNode(raw_lines_node, String8Node, raw_lines_list.first) {
Temp temp = temp_begin(scratch.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));
line_table_blocks_cap += parsed_list.count;
temp_end(temp);
}
LNK_ObjLineMap *map = push_array(arena, LNK_ObjLineMap, 1);
map->arena = arena;
map->debug_checksums = str8_list_first(&raw_checksums);
map->debug_strings = str8_list_first(&raw_strings);
map->line_table_blocks = push_array(arena, LNK_LineTableBlock, line_table_blocks_cap);
map->tables = push_array(arena, LNK_LineTable, line_table_blocks_cap);
for EachNode(raw_lines_node, String8Node, raw_lines_list.first) {
Temp temp = temp_begin(scratch.arena);
String8 raw_lines = raw_lines_node->string;
CV_C13LinesHeaderList parsed_list = cv_c13_lines_from_sub_sections(temp.arena, raw_lines, rng_1u64(0, raw_lines.size));
for EachNode(header_node, CV_C13LinesHeaderNode, parsed_list.first) {
if (header_node->v.line_count == 0) { continue; }
LNK_LineTableBlock *block = &map->line_table_blocks[map->line_table_block_count++];
block->raw_lines = raw_lines;
block->header = push_array(arena, CV_C13LinesHeader, 1);
*block->header = header_node->v;
// resovle lines header
{
CV_C13LinesHeader *header = block->header;
U64 header_foff = (U64)(raw_lines.str - obj->data.str) + header->header_off;
U64 *sec_off = DebugRelocValueFromFileOffset(header_foff + OffsetOf(CV_C13SubSecLinesHeader, sec_off));
if (sec_off) {
U64 len = header->sec_off_hi - header->sec_off_lo;
header->sec_off_lo = *sec_off;
header->sec_off_hi = *sec_off + len;
}
U64 *sec = DebugRelocValueFromFileOffset(header_foff + OffsetOf(CV_C13SubSecLinesHeader, sec));
if (sec) {
header->sec_idx = *sec;
}
}
}
temp_end(temp);
}
Assert(map->line_table_block_count <= line_table_blocks_cap);
radsort(map->line_table_blocks, map->line_table_block_count, lnk_line_table_block_is_before);
for (U64 block_idx = 0; block_idx < map->line_table_block_count;) {
LNK_LineTableBlock *block = &map->line_table_blocks[block_idx];
U64 key_min = Compose64Bit(block->header->sec_idx, block->header->sec_off_lo);
U64 key_max = Compose64Bit(block->header->sec_idx, block->header->sec_off_hi);
U64 block_opl = block_idx + 1;
for (; block_opl < map->line_table_block_count; block_opl += 1) {
LNK_LineTableBlock *next = &map->line_table_blocks[block_opl];
if (next->header->sec_idx != block->header->sec_idx ||
next->header->sec_off_lo != block->header->sec_off_lo ||
next->header->sec_off_hi != block->header->sec_off_hi) {
break;
}
}
LNK_LineTable *table = &map->tables[map->table_count++];
table->key_min = key_min;
table->key_max = key_max;
table->prefix_max = map->table_count > 1 ? Max(map->tables[map->table_count - 2].prefix_max, key_max) : key_max;
table->block_first = block_idx;
table->block_count = block_opl - block_idx;
block_idx = block_opl;
}
//
// build inlinee id -> inlinee name
//
HashMap inlinee_hm = {0};
if (obj->debug_t_sect_idx < obj->header.section_count_no_null) {
LNK_ObjSection debug_t_section = lnk_obj_section_from_sect_idx(obj, obj->debug_t_sect_idx);
String8 raw_debug_t = str8_substr(obj->data, debug_t_section.frange);
CV_Signature debug_t_sig = cv_signature_from_debug_s(raw_debug_t);
if (debug_t_sig == CV_Signature_C13) {
CV_DebugT debug_t = cv_debug_t_from_data(scratch.arena, str8_skip(raw_debug_t, sizeof(CV_Signature)), CV_LeafAlign);
for EachIndex(leaf_idx, debug_t.count) {
CV_Leaf leaf = cv_debug_t_get_leaf(&debug_t, leaf_idx);
String8 name = str8_zero();
if (leaf.kind == CV_LeafKind_FUNC_ID) {
String8 data = str8_substr(leaf.data, r1u64(sizeof(CV_LeafFuncId), leaf.data.size));
name = str8_cstring_capped(data.str, data.str + data.size);
} else if (leaf.kind == CV_LeafKind_MFUNC_ID) {
String8 data = str8_substr(leaf.data, r1u64(sizeof(CV_LeafMFuncId), leaf.data.size));
name = str8_cstring_capped(data.str, data.str + data.size);
}
if (name.size) {
U64 inlinee_itype = CV_MinComplexTypeIndex + leaf_idx;
hash_map_push_u64_string(scratch.arena, &inlinee_hm, inlinee_itype, name);
}
}
}
}
//
// build inlinee lines accelerator
//
String8List raw_inlinee_lines = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_InlineeLines);
CV_C13InlineeLinesParsedList inlinee_lines = cv_c13_inlinee_lines_from_sub_sections(scratch.arena, raw_inlinee_lines);
CV_InlineeLinesAccel *inlinee_lines_accel = cv_c13_make_inlinee_lines_accel(scratch.arena, inlinee_lines);
//
// estimate required inline site capacity
//
String8List raw_symbols = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_Symbols);
U64 inline_site_cap = 0;
for EachNode(raw_symbols_node, String8Node, raw_symbols.first) {
for (U64 cursor = 0; cursor < raw_symbols_node->string.size;) {
CV_Symbol symbol = {0};
TryReadBreak(cv_read_symbol(raw_symbols_node->string, cursor, CV_SymbolAlign, &symbol), cursor);
inline_site_cap += symbol.kind == CV_SymKind_INLINESITE || symbol.kind == CV_SymKind_INLINESITE2;
}
}
//
// extract inline site info from the .debug$S sections
//
typedef struct LNK_InlineRangeNode LNK_InlineRangeNode;
struct LNK_InlineRangeNode {
LNK_InlineRangeNode *next;
LNK_InlineRange v;
};
LNK_InlineRangeNode *range_first = 0, *range_last = 0;
map->inline_sites = push_array(arena, LNK_InlineSite, inline_site_cap);
for EachNode(raw_symbols_node, String8Node, raw_symbols.first) {
U32 proc_section = 0;
U32 proc_offset = 0;
U64 inline_site_stack_count = 0;
U32 *inline_site_stack = push_array_no_zero(scratch.arena, U32, inline_site_cap);
for (U64 cursor = 0; cursor < raw_symbols_node->string.size;) {
CV_Symbol symbol = {0};
TryReadBreak(cv_read_symbol(raw_symbols_node->string, cursor, CV_SymbolAlign, &symbol), cursor);
if (CV_IsProc32(symbol.kind)) {
CV_SymProc32 *proc = str8_deserial_get_raw_ptr(symbol.data, 0, sizeof(*proc));
if (proc) {
U64 proc_foff = IntFromPtr(symbol.data.str - obj->data.str);
U64 *off_reloc = DebugRelocValueFromFileOffset(proc_foff + OffsetOf(CV_SymProc32, off));
U64 *sec_reloc = DebugRelocValueFromFileOffset(proc_foff + OffsetOf(CV_SymProc32, sec));
proc_offset = off_reloc ? safe_cast_u32(*off_reloc) : proc->off;
proc_section = sec_reloc ? safe_cast_u32(*sec_reloc) : proc->sec;
inline_site_stack_count = 0;
}
} else if (symbol.kind == CV_SymKind_INLINESITE || symbol.kind == CV_SymKind_INLINESITE2) {
U64 header_size = symbol.kind == CV_SymKind_INLINESITE ? sizeof(CV_SymInlineSite) : sizeof(CV_SymInlineSite2);
if (proc_section && symbol.data.size >= header_size) {
CV_ItemId inlinee = symbol.kind == CV_SymKind_INLINESITE ? ((CV_SymInlineSite *)symbol.data.str)->inlinee : ((CV_SymInlineSite2 *)symbol.data.str)->inlinee;
String8 raw_annots = str8_skip(symbol.data, header_size);
String8 *inlinee_name = hash_map_search_u64_string(&inlinee_hm, inlinee);
U32 inline_site_idx = safe_cast_u32(map->inline_site_count++);
LNK_InlineSite *inline_site = &map->inline_sites[inline_site_idx];
inline_site->parent_idx_plus_one = inline_site_stack_count ? inline_site_stack[inline_site_stack_count - 1] + 1 : 0;
inline_site->depth = inline_site->parent_idx_plus_one ? map->inline_sites[inline_site->parent_idx_plus_one - 1].depth + 1 : 1;
inline_site->inlinee = inlinee;
inline_site->name = inlinee_name ? *inlinee_name : str8_zero();
inline_site_stack[inline_site_stack_count++] = inline_site_idx;
CV_C13InlineeLinesParsed *inlinee_info = cv_c13_inlinee_lines_accel_find(inlinee_lines_accel, inlinee);
if (inlinee_info) {
//
// decode inline site line table
//
typedef struct LNK_InlineLineNode LNK_InlineLineNode;
struct LNK_InlineLineNode {
LNK_InlineLineNode *next;
CV_Line v;
};
LNK_InlineLineNode *line_first = 0;
LNK_InlineLineNode *line_last = 0;
LNK_InlineRangeNode *last_site_range = 0;
U32 current_file_off = inlinee_info->file_off;
CV_C13InlineSiteDecoder decoder = cv_c13_inline_site_decoder_init(inlinee_info->file_off, inlinee_info->first_source_ln, proc_offset);
for (;;) {
U64 old_cursor = decoder.cursor;
CV_C13InlineSiteDecoderStep step = cv_c13_inline_site_decoder_step(&decoder, raw_annots);
if (step.flags == 0 || decoder.cursor <= old_cursor) { break; }
if (step.flags & CV_C13InlineSiteDecoderStepFlag_EmitFile) {
current_file_off = step.file_off;
}
if (step.range.min < step.range.max) {
if ((step.flags & CV_C13InlineSiteDecoderStepFlag_EmitRange) ||
((step.flags & CV_C13InlineSiteDecoderStepFlag_ExtendLastRange) && last_site_range == 0)) {
LNK_InlineRangeNode *node = push_array(scratch.arena, LNK_InlineRangeNode, 1);
node->v.key_min = Compose64Bit(proc_section, step.range.min);
node->v.key_max = Compose64Bit(proc_section, step.range.max);
node->v.site_idx = inline_site_idx;
SLLQueuePush(range_first, range_last, node);
last_site_range = node;
map->inline_range_count += 1;
}
}
if ((step.flags & CV_C13InlineSiteDecoderStepFlag_ExtendLastRange) && last_site_range) {
last_site_range->v.key_max = Compose64Bit(proc_section, step.range.max);
}
if (step.flags & CV_C13InlineSiteDecoderStepFlag_EmitLine) {
LNK_InlineLineNode *node = push_array(scratch.arena, LNK_InlineLineNode, 1);
node->v.voff = step.line_voff;
node->v.file_off = current_file_off;
node->v.line_num = step.ln;
SLLQueuePush(line_first, line_last, node);
inline_site->line_count += 1;
}
}
// line list -> sorted line array
inline_site->lines = push_array_no_zero(arena, CV_Line, inline_site->line_count);
U64 line_idx = 0;
for EachNode(line, LNK_InlineLineNode, line_first) { inline_site->lines[line_idx++] = line->v; }
radsort(inline_site->lines, inline_site->line_count, cv_c13_voff_map_is_before);
}
}
} else if (symbol.kind == CV_SymKind_INLINESITE_END) {
if (inline_site_stack_count > 0) {
inline_site_stack_count -= 1;
}
} else if (symbol.kind == CV_SymKind_END || symbol.kind == CV_SymKind_PROC_ID_END) {
proc_section = 0;
proc_offset = 0;
inline_site_stack_count = 0;
}
}
}
//
// fill out & sort inline site ranges
//
{
map->inline_ranges = push_array_no_zero(arena, LNK_InlineRange, map->inline_range_count);
U64 range_idx = 0;
for EachNode(range, LNK_InlineRangeNode, range_first) {
map->inline_ranges[range_idx++] = range->v;
}
radsort(map->inline_ranges, map->inline_range_count, lnk_inline_range_is_before);
for EachIndex(i, map->inline_range_count) {
map->inline_ranges[i].prefix_max = i ?
Max(map->inline_ranges[i - 1].prefix_max, map->inline_ranges[i].key_max) :
map->inline_ranges[i].key_max;
}
}
#undef DebugRelocValueFromFileOff
scratch_end(scratch);
return map;
}
internal CV_Line *
lnk_lines_from_section_offset(LNK_ObjLineMap *map, U32 section_number, U32 offset, U64 *line_count_out)
{
U64 key = Compose64Bit(section_number, offset);
// upper bound search
U64 min = 0;
U64 opl = map->table_count;
while (min < opl) {
U64 mid = min + (opl - min) / 2;
if (map->tables[mid].key_min <= key) {
min = mid + 1;
} else {
opl = mid;
}
}
// backward search until containing interval is found
LNK_LineTable *table = 0;
while (min > 0) {
LNK_LineTable *candidate = &map->tables[--min];
if (key < candidate->key_max) {
table = candidate;
break;
}
if (min == 0 || map->tables[min - 1].prefix_max <= key) {
break;
}
}
CV_Line *lines = 0;
U64 line_count = 0;
if (table) {
if (table->accel == 0) {
Temp scratch = scratch_begin(&map->arena, 1);
// unpack lines from line table blocks
CV_LineArray *line_arrays = push_array_no_zero(scratch.arena, CV_LineArray, table->block_count);
for EachIndex(i, table->block_count) {
LNK_LineTableBlock *block = &map->line_table_blocks[table->block_first + i];
CV_C13LinesHeader header = *block->header;
header.sec_off_hi -= header.sec_off_lo;
header.sec_off_lo = 0;
line_arrays[i] = cv_c13_line_array_from_data(scratch.arena, block->raw_lines, 0, header);
}
// cache line table accel
table->accel = cv_c13_make_lines_accel(map->arena, table->block_count, line_arrays);
scratch_end(scratch);
}
// match (section, offset) => lines
U32 table_offset = safe_cast_u32(key - table->key_min);
lines = cv_line_from_voff(table->accel, table_offset, &line_count);
}
if (line_count_out) {
*line_count_out = line_count;
}
return lines;
}
internal LNK_InlineSite *
lnk_inline_site_from_section_offset(LNK_ObjLineMap *map, U32 section_number, U32 offset)
{
U64 key = Compose64Bit(section_number, offset);
U64 min = 0;
U64 opl = map->inline_range_count;
while (min < opl) {
U64 mid = min + (opl - min) / 2;
if (map->inline_ranges[mid].key_min <= key) {
min = mid + 1;
} else {
opl = mid;
}
}
LNK_InlineSite *site = 0;
while (min > 0) {
LNK_InlineRange *range = &map->inline_ranges[--min];
if (key < range->key_max) {
LNK_InlineSite *candidate = &map->inline_sites[range->site_idx];
if (site == 0 || site->depth < candidate->depth) {
site = candidate;
}
}
if (min == 0 || map->inline_ranges[min - 1].prefix_max <= key) {
break;
}
}
return site;
}
internal CV_Line *
lnk_line_from_inline_site(LNK_InlineSite *site, U32 offset)
{
U64 min = 0;
U64 opl = site->line_count;
while (min < opl) {
U64 mid = min + (opl - min) / 2;
if (site->lines[mid].voff <= offset) {
min = mid + 1;
} else {
opl = mid;
}
}
return min > 0 ? &site->lines[min - 1] : 0;
}
+76 -5
View File
@@ -83,6 +83,69 @@ typedef struct LNK_ObjNodeArray
LNK_ObjNode *v;
} LNK_ObjNodeArray;
// --- Maps --------------------------------------------------------------
typedef struct LNK_SectionOffsetSymbol
{
U64 key;
U32 symbol_idx;
} LNK_SectionOffsetSymbol;
typedef struct LNK_ObjSymbolMap
{
U64 count;
LNK_SectionOffsetSymbol *v;
} LNK_ObjSymbolMap;
typedef struct LNK_LineTableBlock
{
String8 raw_lines;
CV_C13LinesHeader *header;
} LNK_LineTableBlock;
typedef struct LNK_LineTable
{
U64 key_min;
U64 key_max;
U64 prefix_max;
U64 block_first;
U64 block_count;
CV_LinesAccel *accel;
} LNK_LineTable;
typedef struct LNK_InlineSite
{
U32 parent_idx_plus_one;
U32 depth;
CV_ItemId inlinee;
String8 name;
U64 line_count;
CV_Line *lines;
} LNK_InlineSite;
typedef struct LNK_InlineRange
{
U64 key_min;
U64 key_max;
U64 prefix_max;
U32 site_idx;
} LNK_InlineRange;
typedef struct LNK_ObjLineMap
{
Arena *arena;
String8 debug_checksums;
String8 debug_strings;
U64 line_table_block_count;
LNK_LineTableBlock *line_table_blocks;
U64 table_count;
LNK_LineTable *tables;
U64 inline_site_count;
LNK_InlineSite *inline_sites;
U64 inline_range_count;
LNK_InlineRange *inline_ranges;
} LNK_ObjLineMap;
// --- Directive Parser --------------------------------------------------------
typedef struct LNK_Directive
@@ -112,8 +175,6 @@ typedef struct
LNK_ObjNode *objs;
U64 obj_id_base;
U32 machine;
B32 find_debug_t;
B32 find_llvm_addrsig;
} LNK_ObjIniter;
typedef struct
@@ -157,10 +218,11 @@ internal U32List lnk_obj_collect_associated_sections(Arena *arena, LNK_
// --- Symbol & Section Helpers ------------------------------------------------
internal COFF_SectionHeader * lnk_coff_section_header_from_section_number(LNK_Obj *obj, U64 section_number);
internal force_inline COFF_ParsedSymbol lnk_parsed_symbol_from_coff_symbol_idx(LNK_Obj *obj, U64 symbol_idx);
internal force_inline COFF_ParsedSymbol lnk_parsed_symbol_from_coff_symbol_idx_no_name(LNK_Obj *obj, U64 symbol_idx);
internal force_inline String8 lnk_symbol_name_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 U64 lnk_obj_sect_idx_from_section_number(LNK_Obj *obj, U64 section_number);
internal U64 lnk_obj_section_number_from_sect_idx(LNK_Obj *obj, U64 sect_idx);
internal String8 lnk_obj_section_name_from_section_number(LNK_Obj *obj, U64 section_number);
@@ -183,6 +245,15 @@ 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 --------------------------------------------------------------
// --- Maps ---------------------------------------------------------------------
// COFF map
internal LNK_ObjSymbolMap * lnk_symbol_map_from_obj(Arena *arena, LNK_Obj *obj);
internal U32 lnk_symbol_from_section_offset(LNK_ObjSymbolMap *map, U32 section_number, U32 offset);
// CodeView map
internal LNK_ObjLineMap * lnk_line_map_from_obj(Arena *arena, LNK_Obj *obj);
internal CV_Line * lnk_lines_from_section_offset(LNK_ObjLineMap *map, U32 section_number, U32 offset, U64 *line_count_out);
internal LNK_InlineSite * lnk_inline_site_from_section_offset(LNK_ObjLineMap *map, U32 section_number, U32 offset);
internal CV_Line * lnk_line_from_inline_site(LNK_InlineSite *site, U32 offset);
internal CV_DebugS lnk_debug_s_from_obj(Arena *arena, LNK_Obj *obj);