diff --git a/src/base/base_core.h b/src/base/base_core.h index ebef4649..858ca47e 100644 --- a/src/base/base_core.h +++ b/src/base/base_core.h @@ -445,8 +445,8 @@ C_LINKAGE void __asan_unpoison_memory_region(void const volatile *addr, size_t s # define this_function_name __func__ #endif -#define TryRead(func__, cursor__, label__) do { U64 size__ = (func__); if (size__ == 0) { Assert(0 && "failed read"); goto label__; } cursor__ += size__; } while (0) -#define TryReadBreak(func__, cursor__) { U64 size__ = (func__); if (size__ == 0) { Assert(0 && "failed read"); break; } cursor__ += size__; } +#define TryRead(func__, cursor__, label__) do { U64 size__ = (func__); if (size__ == 0) { goto label__; } cursor__ += size__; } while (0) +#define TryReadBreak(func__, cursor__) { U64 size__ = (func__); if (size__ == 0) { break; } cursor__ += size__; } //////////////////////////////// //~ rjf: Base Types diff --git a/src/linker/codeview_ext/codeview.c b/src/linker/codeview_ext/codeview.c index 6dc83333..ef6b6234 100644 --- a/src/linker/codeview_ext/codeview.c +++ b/src/linker/codeview_ext/codeview.c @@ -84,7 +84,7 @@ internal U64 cv_read_leaf(String8 raw_data, U64 off, U64 align, CV_Leaf *leaf_out) { // do we have enough bytes to read header? - Assert(raw_data.size >= sizeof(CV_LeafHeader)); + if (raw_data.size < sizeof(CV_LeafHeader)) { return 0; } U8 *leaf_ptr = raw_data.str + off; @@ -92,17 +92,18 @@ cv_read_leaf(String8 raw_data, U64 off, U64 align, CV_Leaf *leaf_out) CV_LeafHeader header = { .v = memory_read32(leaf_ptr) }; // leaf size must have enough bytes for the kind enum - Assert(header.size >= sizeof(CV_LeafKind)); + if (header.size < sizeof(CV_LeafKind)) { return 0; } // do we have enough bytes to read leaf data? - Assert(sizeof(CV_LeafSize) + header.size <= raw_data.size); + if (sizeof(CV_LeafSize) + header.size > raw_data.size) { return 0; } // fill out leaf leaf_out->kind = header.kind; leaf_out->data = str8(leaf_ptr + sizeof(CV_LeafHeader), header.size - sizeof(CV_LeafKind)); U64 leaf_size = AlignPow2(sizeof(CV_LeafHeader) + leaf_out->data.size, align); - Assert(leaf_size <= raw_data.size); + if (leaf_size > raw_data.size) { return 0; } + return leaf_size; } @@ -1161,10 +1162,9 @@ count_stop: ProfBegin("store leaf offsets"); debug_t.offsets = push_array_no_zero(arena, U32, debug_t.count); - for (U64 cursor = 0, idx = 0; cursor < data.size;) { - debug_t.offsets[idx++] = cursor; - CV_Leaf leaf; - TryRead(cv_read_leaf(data, cursor, align, &leaf), cursor, store_stop); + for (U64 cursor = 0, idx = 0; cursor < data.size && idx < debug_t.count; idx += 1) { + debug_t.offsets[idx] = cursor; + TryRead(cv_read_leaf(data, cursor, align, &(CV_Leaf){0}), cursor, store_stop); } store_stop: diff --git a/src/linker/lnk_debug_info.c b/src/linker/lnk_debug_info.c index 4dc7d2ea..cd751deb 100644 --- a/src/linker/lnk_debug_info.c +++ b/src/linker/lnk_debug_info.c @@ -632,21 +632,6 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config, } } - // pre-build PCH obj hash map (obj_path, obj_idx) - String8 work_dir = get_current_path(scratch.arena); - HashMap debug_p_hm = {0}; - for EachIndex(obj_idx, obj_count) { - LNK_Obj *obj = obj_arr[obj_idx]; - if (obj->debug_p_sect_idx < obj->header.section_count_no_null) { - String8 obj_path = path_absolute_dst_from_relative_dst_src(scratch.arena, obj_arr[obj_idx]->path, work_dir); - if (hash_map_search_path_u64(&debug_p_hm, obj_path)) { - lnk_error_obj(LNK_Warning_DuplicateObjPath, obj_arr[obj_idx], "duplicate obj path %S", obj_path); - } else { - hash_map_push_path_u64(scratch.arena, &debug_p_hm, obj_path, obj_idx); - } - } - } - ProfBegin("Apply RRT to Objs"); // hash map (obj path, obj idx) @@ -698,6 +683,7 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config, input.debug_s_list_arr = lnk_collect_obj_sections(tp, tp_arena, obj_count, obj_arr, str8_lit(".debug$S"), 0); ProfEnd(); + // profiler info if (lnk_get_log_status(LNK_Log_Debug) || PROFILE_TELEMETRY) { U64 total_debug_s_size = 0, total_debug_t_size = 0, total_debug_p_size = 0, total_debug_h_size = 0; for EachIndex(obj_idx, obj_count) { @@ -745,41 +731,48 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config, ProfBegin("Parse CodeView"); CV_DebugT *debug_p_arr; { + // parse .debug$S input.debug_s_arr = push_array(tp_arena->v[0], CV_DebugS, input.obj_count); tp_for_parallel_prof(tp, tp_arena, obj_count, lnk_parse_debug_s_task, &input, "Parse .debug$S"); + // collect .debug$P and .debug$T String8Array *raw_debug_p_arr = push_array(scratch.arena, String8Array, obj_count); String8Array *raw_debug_t_arr = push_array(scratch.arena, String8Array, obj_count); for EachIndex(obj_idx, obj_count) { LNK_Obj *obj = obj_arr[obj_idx]; if (obj->debug_t_sect_idx < obj->header.section_count_no_null) { - LNK_ObjSection section = lnk_obj_section_from_sect_idx(obj, obj->debug_t_sect_idx); + LNK_ObjSection debug_t_sect = lnk_obj_section_from_sect_idx(obj, obj->debug_t_sect_idx); raw_debug_t_arr[obj_idx].count = 1; raw_debug_t_arr[obj_idx].v = push_array(scratch.arena, String8, 1); - raw_debug_t_arr[obj_idx].v[0] = str8_substr(obj->data, section.frange); + raw_debug_t_arr[obj_idx].v[0] = str8_substr(obj->data, debug_t_sect.frange); } + if (obj->debug_p_sect_idx < obj->header.section_count_no_null) { - LNK_ObjSection section = lnk_obj_section_from_sect_idx(obj, obj->debug_p_sect_idx); + LNK_ObjSection debug_p_sect = lnk_obj_section_from_sect_idx(obj, obj->debug_p_sect_idx); raw_debug_p_arr[obj_idx].count = 1; raw_debug_p_arr[obj_idx].v = push_array(scratch.arena, String8, 1); - raw_debug_p_arr[obj_idx].v[0] = str8_substr(obj->data, section.frange); + raw_debug_p_arr[obj_idx].v[0] = str8_substr(obj->data, debug_p_sect.frange); } } LNK_ParseCvTypes parse_types = { .input = &input }; + + // parse .debug$P debug_p_arr = push_array(tp_arena->v[0], CV_DebugT, obj_count); parse_types.raw_types = raw_debug_p_arr; parse_types.out_types = debug_p_arr; tp_for_parallel_prof(tp, 0, obj_count, lnk_strip_debug_t_sig_task, &parse_types, "Strip .debug$P"); tp_for_parallel_prof(tp, tp_arena, obj_count, lnk_parse_debug_t_task, &parse_types, "Parse .debug$P"); + // parse .debug$T input.debug_t_arr = push_array(tp_arena->v[0], CV_DebugT, obj_count); parse_types.raw_types = raw_debug_t_arr; parse_types.out_types = input.debug_t_arr; tp_for_parallel_prof(tp, 0, obj_count, lnk_strip_debug_t_sig_task, &parse_types, "Strip .debug$T"); tp_for_parallel_prof(tp, tp_arena, obj_count, lnk_parse_debug_t_task, &parse_types, "Parse .debug$T"); + // parse .debug$H input.debug_h_arr = push_array(tp_arena->v[0], CV_DebugH, input.obj_count); if (config->ghash) { tp_for_parallel_prof(tp, tp_arena, obj_count, lnk_parse_debug_h_task, &input, "Parse .debug$H"); @@ -961,6 +954,40 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config, ProfBegin("Set up PCH"); { + // pre-build PCH obj hash map (obj_path, obj_idx) + HashMap debug_p_hm_path = {0}; + HashMap debug_p_hm_sig = {0}; + for EachIndex(i, input.debug_p_indices.count) { + U64 obj_idx = input.debug_p_indices.v[i]; + LNK_Obj *obj = obj_arr[obj_idx]; + + // register PCH signature + CV_DebugT debug_p = input.debug_t_arr[obj_idx]; + if (debug_p.count > 0) { + CV_Leaf lf = cv_debug_t_get_leaf(&debug_p, debug_p.count - 1); + if (lf.kind == CV_LeafKind_ENDPRECOMP && lf.data.size <= sizeof(CV_LeafEndPreComp)) { + CV_LeafEndPreComp *ender = str8_deserial_get_raw_ptr(lf.data, 0, sizeof(*ender)); + if (ender->sig) { + U64 *extant_obj_idx = hash_map_search_u64_u64(&debug_p_hm_sig, ender->sig); + if (extant_obj_idx == 0) { + hash_map_push_u64_u64(scratch.arena, &debug_p_hm_sig, ender->sig, obj_idx); + } else { + LNK_Obj *extant_obj = obj_arr[*extant_obj_idx]; + lnk_log(LNK_Log_Debug, "%S: PCH signature is already defined in %S", lnk_loc_from_obj(scratch.arena, obj), lnk_loc_from_obj(scratch.arena, extant_obj)); + } + } + } + } + + // register PCH path + String8 obj_path = path_absolute_dst_from_relative_dst_src(scratch.arena, obj_arr[obj_idx]->path, config->work_dir); + if (hash_map_search_path_u64(&debug_p_hm_path, obj_path)) { + lnk_error_obj(LNK_Warning_DuplicateObjPath, obj, "duplicate obj path %S", obj_path); + } else { + hash_map_push_path_u64(scratch.arena, &debug_p_hm_path, obj_path, obj_idx); + } + } + for EachIndex(i, input.int_obj_indices.count) { U64 obj_idx = input.int_obj_indices.v[i]; CV_DebugT *debug_t = &input.debug_t_arr[obj_idx]; @@ -970,15 +997,18 @@ lnk_make_code_view_input(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config, // find PCH obj CV_PrecompInfo precomp = cv_precomp_info_from_leaf(cv_debug_t_get_leaf(debug_t, 0)); - String8 obj_path = path_absolute_dst_from_relative_dst_src(scratch.arena, precomp.obj_name, work_dir); - U64 *debug_p_obj_idx_ptr = hash_map_search_path_u64(&debug_p_hm, obj_path); + String8 obj_path = path_absolute_dst_from_relative_dst_src(scratch.arena, precomp.obj_name, config->work_dir); + U64 *debug_p_obj_idx_ptr = hash_map_search_path_u64(&debug_p_hm_path, obj_path); + if (debug_p_obj_idx_ptr == 0) { + debug_p_obj_idx_ptr = hash_map_search_u64_u64(&debug_p_hm_sig, precomp.sig); + } // try alternative directory for the PCH if (debug_p_obj_idx_ptr == 0) { String8 obj_name = str8_skip_last_slash(obj_path); for EachNode(alt_dir_n, String8Node, config->alt_pch_dirs.first) { String8 alt_obj_path = str8f(scratch.arena, "%S/%S", alt_dir_n->string, obj_name); - debug_p_obj_idx_ptr = hash_map_search_path_u64(&debug_p_hm, alt_obj_path); + debug_p_obj_idx_ptr = hash_map_search_path_u64(&debug_p_hm_path, alt_obj_path); if (debug_p_obj_idx_ptr) { break; } } } diff --git a/src/linker/tests/linker_tests.c b/src/linker/tests/linker_tests.c index 23b5bf0c..014708bf 100644 --- a/src/linker/tests/linker_tests.c +++ b/src/linker/tests/linker_tests.c @@ -5073,6 +5073,193 @@ TEST(debug_p_sig_mismatch) T_Ok(found_error); } +TEST(pch_sig_fallback) +{ + String8 a_obj_file_path = t_make_file_path(arena, str8_lit("a.obj")); + String8 b_obj_file_path = t_make_file_path(arena, str8_lit("b.obj")); + U32 a_sig = 0xCAFEBABE; + U32 b_sig = 0xCAFEBABE; + + String8 a_debug_s; + { + String8List srl; + str8_serial_begin(arena, &srl); + + CV_Signature sig = CV_Signature_C13; + str8_serial_push_struct(arena, &srl, &sig); + + CV_C13SubSectionHeader *ss_header = str8_serial_push_size(arena, &srl, sizeof(*ss_header)); + U64 ss_start_off = srl.total_size; + + CV_SymObjName obj_name = {0}; + obj_name.sig = a_sig; + String8 obj_name_string = a_obj_file_path; + str8_serial_push_u16(arena, &srl, sizeof(CV_SymKind) + sizeof(obj_name) + obj_name_string.size + 1); + str8_serial_push_u16(arena, &srl, CV_SymKind_OBJNAME); + str8_serial_push_struct(arena, &srl, &obj_name); + str8_serial_push_cstr(arena, &srl, obj_name_string); + str8_serial_push_align(arena, &srl, CV_SymbolAlign); + + String8 comp3_data = cv_make_comp3(arena, + 0, + CV_Language_C, + CV_Arch_X64, + /* ver_fe_major */ 0, + /* ver_fe_minor */ 0, + /* ver_fe_build */ 0, + /* ver_feqfe */ 0, + /* ver_major */ 14, + /* ver_minor */ 36, + /* ver_build */ 32537, + /* ver_qfe */ 0, + str8_lit(BUILD_TITLE)); + str8_serial_push_u16(arena, &srl, sizeof(CV_SymKind) + comp3_data.size); + str8_serial_push_u16(arena, &srl, CV_SymKind_COMPILE3); + str8_serial_push_string(arena, &srl, comp3_data); + str8_serial_push_align(arena, &srl, CV_SymbolAlign); + + ss_header->kind = CV_C13SubSectionKind_Symbols; + ss_header->size = srl.total_size - ss_start_off; + str8_serial_push_align(arena, &srl, CV_C13SubSectionAlign); + + a_debug_s = str8_serial_end(arena, &srl); + } + String8 a_debug_p; + { + String8List srl; + str8_serial_begin(arena, &srl); + + // signature + CV_Signature sig = CV_Signature_C13; + str8_serial_push_struct(arena, &srl, &sig); + + // duplicate in a.obj + CV_LeafPointer ptr = { .itype = CV_BasicType_VOID }; + str8_serial_push_u16(arena, &srl, sizeof(CV_LeafKind) + sizeof(ptr)); + str8_serial_push_u16(arena, &srl, CV_LeafKind_POINTER); + str8_serial_push_struct(arena, &srl, &ptr); + str8_serial_push_align(arena, &srl, CV_LeafAlign); + + // unique procedure type + CV_LeafProcedure proc = { .ret_itype = 0x1000, .call_kind = CV_CallKind_NearPascal }; + str8_serial_push_u16(arena, &srl, sizeof(CV_LeafKind) + sizeof(proc)); + str8_serial_push_u16(arena, &srl, CV_LeafKind_PROCEDURE); + str8_serial_push_struct(arena, &srl, &proc); + str8_serial_push_align(arena, &srl, CV_LeafAlign); + + // PCH ender + CV_LeafEndPreComp endprecomp = { .sig = a_sig }; + str8_serial_push_u16(arena, &srl, sizeof(CV_LeafKind) + sizeof(endprecomp)); + str8_serial_push_u16(arena, &srl, CV_LeafKind_ENDPRECOMP); + str8_serial_push_struct(arena, &srl, &endprecomp); + str8_serial_push_align(arena, &srl, CV_LeafAlign); + + a_debug_p = str8_serial_end(arena, &srl); + } + + String8 b_debug_s; + { + String8List srl; + str8_serial_begin(arena, &srl); + + CV_Signature sig = CV_Signature_C13; + str8_serial_push_struct(arena, &srl, &sig); + + CV_C13SubSectionHeader *ss_header = str8_serial_push_size(arena, &srl, sizeof(*ss_header)); + U64 ss_start_off = srl.total_size; + + CV_SymObjName obj_name = {0}; + obj_name.sig = b_sig; + String8 obj_name_string = a_obj_file_path; + str8_serial_push_u16(arena, &srl, sizeof(CV_SymKind) + sizeof(obj_name) + obj_name_string.size + 1); + str8_serial_push_u16(arena, &srl, CV_SymKind_OBJNAME); + str8_serial_push_struct(arena, &srl, &obj_name); + str8_serial_push_cstr(arena, &srl, obj_name_string); + str8_serial_push_align(arena, &srl, CV_SymbolAlign); + + String8 comp3_data = cv_make_comp3(arena, + 0, + CV_Language_C, + CV_Arch_X64, + /* ver_fe_major */ 0, + /* ver_fe_minor */ 0, + /* ver_fe_build */ 0, + /* ver_feqfe */ 0, + /* ver_major */ 14, + /* ver_minor */ 36, + /* ver_build */ 32537, + /* ver_qfe */ 0, + str8_lit(BUILD_TITLE)); + str8_serial_push_u16(arena, &srl, sizeof(CV_SymKind) + comp3_data.size); + str8_serial_push_u16(arena, &srl, CV_SymKind_COMPILE3); + str8_serial_push_string(arena, &srl, comp3_data); + str8_serial_push_align(arena, &srl, CV_SymbolAlign); + + ss_header->kind = CV_C13SubSectionKind_Symbols; + ss_header->size = srl.total_size - ss_start_off; + str8_serial_push_align(arena, &srl, CV_C13SubSectionAlign); + + b_debug_s = str8_serial_end(arena, &srl); + } + + String8 b_debug_t; + { + String8List srl; + str8_serial_begin(arena, &srl); + + CV_Signature sig = CV_Signature_C13; + str8_serial_push_struct(arena, &srl, &sig); + + String8 corrupt_pch_path = str8_lit("corrupt-pch-file-path.obj"); + + CV_LeafPreComp precomp = { .start_index = CV_MinComplexTypeIndex, .count = 2, sig = b_sig }; + str8_serial_push_u16(arena, &srl, sizeof(CV_LeafKind) + sizeof(precomp) + corrupt_pch_path.size + 1); + str8_serial_push_u16(arena, &srl, CV_LeafKind_PRECOMP); + str8_serial_push_struct(arena, &srl, &precomp); + str8_serial_push_cstr(arena, &srl, corrupt_pch_path); + str8_serial_push_align(arena, &srl, CV_LeafAlign); + + CV_LeafPointer ptr = { .itype = CV_BasicType_VOID }; + str8_serial_push_u16(arena, &srl, sizeof(CV_LeafKind) + sizeof(CV_LeafPointer)); + str8_serial_push_u16(arena, &srl, CV_LeafKind_POINTER); + str8_serial_push_struct(arena, &srl, &ptr); + str8_serial_push_align(arena, &srl, CV_LeafAlign); + + CV_LeafProcedure proc = { .ret_itype = 0x1000, .call_kind = CV_CallKind_NearC }; + str8_serial_push_u16(arena, &srl, sizeof(CV_LeafKind) + sizeof(CV_LeafProcedure)); + str8_serial_push_u16(arena, &srl, CV_LeafKind_PROCEDURE); + str8_serial_push_struct(arena, &srl, &proc); + str8_serial_push_align(arena, &srl, CV_LeafAlign); + + b_debug_t = str8_serial_end(arena, &srl); + } + + String8 a_obj = t_coff_from_def_obj(arena, (T_COFF_DefObj){ + .machine = T_COFF_DefSetMachine(X64), + .sections = (T_COFF_DefSection[]){ + { "debug_p", ".debug$P", a_debug_p, .flags = "r:data@1", .raw_flags = COFF_SectionFlag_MemDiscardable }, + { "debug_s", ".debug$S", a_debug_s, .flags = "r:data@1", .raw_flags = COFF_SectionFlag_MemDiscardable }, + {0} + } + }); + + String8 b_obj = t_coff_from_def_obj(arena, (T_COFF_DefObj){ + .machine = T_COFF_DefSetMachine(X64), + .sections = (T_COFF_DefSection[]){ + { "debug_t", ".debug$T", b_debug_t, .flags = "r:data@1", .raw_flags = COFF_SectionFlag_MemDiscardable }, + { "debug_s", ".debug$S", b_debug_s, .flags = "r:data@1", .raw_flags = COFF_SectionFlag_MemDiscardable }, + {0} + } + }); + + T_Ok(write_data_to_file_path(a_obj_file_path, a_obj)); + T_Ok(write_data_to_file_path(b_obj_file_path, b_obj)); + T_Ok(t_write_entry_obj()); + + t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /debug:full a.obj b.obj entry.obj"); + T_Ok(g_last_exit_code == 0); +} + TEST(debug_p_and_debug_t_in_obj) { U32 pch_sig = 0xCAFEBABE;