fallback on PCH signature when file path match fails

This commit is contained in:
Nikita Smith
2026-06-13 06:20:40 -07:00
committed by Ryan Fleury
parent 6ca7f162aa
commit c030488814
4 changed files with 249 additions and 32 deletions
+2 -2
View File
@@ -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
+8 -8
View File
@@ -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:
+52 -22
View File
@@ -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; }
}
}
+187
View File
@@ -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;