diff --git a/src/codeview/codeview.c b/src/codeview/codeview.c index 2923f941..39ccd1a4 100644 --- a/src/codeview/codeview.c +++ b/src/codeview/codeview.c @@ -131,7 +131,6 @@ cv_map_encoded_base_pointer(CV_Arch arch, U32 encoded_frame_reg) return r; } - //////////////////////////////// //~ rjf: Enum -> String diff --git a/src/codeview/codeview.h b/src/codeview/codeview.h index 7e4672e4..196e1996 100644 --- a/src/codeview/codeview.h +++ b/src/codeview/codeview.h @@ -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 diff --git a/src/coff/coff.c b/src/coff/coff.c index 7541f1a2..437b39de 100644 --- a/src/coff/coff.c +++ b/src/coff/coff.c @@ -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: { diff --git a/src/linker/codeview_ext/codeview.c b/src/linker/codeview_ext/codeview.c index 4c55c73d..11592f7a 100644 --- a/src/linker/codeview_ext/codeview.c +++ b/src/linker/codeview_ext/codeview.c @@ -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; } diff --git a/src/linker/codeview_ext/codeview.h b/src/linker/codeview_ext/codeview.h index 985f2dce..f3e5ae9f 100644 --- a/src/linker/codeview_ext/codeview.h +++ b/src/linker/codeview_ext/codeview.h @@ -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; diff --git a/src/linker/hash_table.c b/src/linker/hash_table.c index 45591cbb..0fecf387 100644 --- a/src/linker/hash_table.c +++ b/src/linker/hash_table.c @@ -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) { diff --git a/src/linker/hash_table.h b/src/linker/hash_table.h index f9e89d67..a8645412 100644 --- a/src/linker/hash_table.h +++ b/src/linker/hash_table.h @@ -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); diff --git a/src/linker/lnk.c b/src/linker/lnk.c index e29f7947..342edbb4 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -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 = §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"); - 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 = §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 (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(); diff --git a/src/linker/lnk_obj.c b/src/linker/lnk_obj.c index d86f5e05..b695e747 100644 --- a/src/linker/lnk_obj.c +++ b/src/linker/lnk_obj.c @@ -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; +} + diff --git a/src/linker/lnk_obj.h b/src/linker/lnk_obj.h index 1a7d90b6..9b74e4a2 100644 --- a/src/linker/lnk_obj.h +++ b/src/linker/lnk_obj.h @@ -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);