From 89a5acba2cab38cb05cffbcf4b7d0b3850498c7f Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Fri, 10 Apr 2026 16:45:50 -0700 Subject: [PATCH] WIP stripped PDB --- src/linker/codeview_ext/codeview.c | 17 ++++++ src/linker/codeview_ext/codeview.h | 1 + src/linker/lnk.c | 73 ++++++++++++++++++++++++- src/linker/lnk_config.c | 8 +++ src/linker/lnk_config.h | 4 +- src/linker/lnk_debug_info.c | 88 +++++++++++++++++------------- src/linker/lnk_debug_info.h | 1 + 7 files changed, 152 insertions(+), 40 deletions(-) diff --git a/src/linker/codeview_ext/codeview.c b/src/linker/codeview_ext/codeview.c index fe24e48b..235d0b45 100644 --- a/src/linker/codeview_ext/codeview.c +++ b/src/linker/codeview_ext/codeview.c @@ -466,6 +466,23 @@ cv_make_pub32(Arena *arena, CV_Pub32Flags flags, U32 off, U16 isect, String8 nam return symbol; } +internal B32 +cv_is_gproc(CV_Symbol symbol) +{ + return symbol.kind == CV_SymKind_GPROC32 || + symbol.kind == CV_SymKind_GPROCMIPS || + symbol.kind == CV_SymKind_GPROCIA64 || + symbol.kind == CV_SymKind_GPROC32_ID || + symbol.kind == CV_SymKind_GPROCMIPS_ID || + symbol.kind == CV_SymKind_GPROCIA64_ID || + symbol.kind == CV_SymKind_GPROC16 || + symbol.kind == CV_SymKind_GPROC32_16t || + symbol.kind == CV_SymKind_GPROCMIPS_16t || + symbol.kind == CV_SymKind_GPROC32_ST || + symbol.kind == CV_SymKind_GPROCMIPS_ST || + symbol.kind == CV_SymKind_GPROCIA64_ST; +} + internal B32 cv_is_lproc(CV_Symbol symbol) { diff --git a/src/linker/codeview_ext/codeview.h b/src/linker/codeview_ext/codeview.h index 3b477148..6c0219c6 100644 --- a/src/linker/codeview_ext/codeview.h +++ b/src/linker/codeview_ext/codeview.h @@ -364,6 +364,7 @@ internal CV_Symbol cv_make_pub32(Arena *arena, CV_Pub32Flags flags, U32 off, internal U64 cv_read_symbol(String8 raw_data, U64 off, U64 align, CV_Symbol *symbol_out); internal CV_Symbol cv_symbol_from_string(String8 raw_data); +internal B32 cv_is_gproc(CV_Symbol symbol); internal B32 cv_is_lproc(CV_Symbol symbol); internal B32 cv_is_obj_info(CV_Symbol symbol); internal CV_ObjInfo cv_obj_info_from_symbol(CV_Symbol symbol); diff --git a/src/linker/lnk.c b/src/linker/lnk.c index 0ae43fdc..14642ae5 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -2123,7 +2123,7 @@ lnk_link_image(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer } ProfBegin("Build * Debug Directories *"); - if (config->debug_mode != LNK_DebugMode_None && config->debug_mode != LNK_DebugMode_Null) { + if (lnk_do_debug_info(config)) { String8 pdb_dir_obj = pe_make_debug_directory_pdb_obj(arena->v[0], config->machine, config->guid, config->age, config->time_stamp, config->pdb_alt_path); lnk_inputer_push_obj_linkgen(inputer, 0, str8_lit("* Debug Directory PDB *"), pdb_dir_obj); } @@ -5298,6 +5298,77 @@ lnk_run(TP_Context *tp, TP_Arena *arena, LNK_Config *config) lnk_timer_end(LNK_Timer_Pdb); } + // + // stripped PDB + // + if (config->pdb_stripped_name.size != 0) { + CV_DebugS *debug_s_arr = push_array(scratch.arena, CV_DebugS, cv.obj_count); + for EachIndex(obj_idx, cv.obj_count) { + + CV_DebugS *debug_s_dst = &debug_s_arr[obj_idx]; + CV_DebugS *debug_s_src = &cv.debug_s_arr[obj_idx]; + String8List *dst = &debug_s_dst->data_list[CV_C13SubSectionIdxKind_Symbols]; + String8List *src = &debug_s_src->data_list[CV_C13SubSectionIdxKind_Symbols]; + + U64 proc_count = 0; + U64 proc_size = 0; + U64 section = 0; + for EachNode(n, String8Node, src->first) { + for (U64 cursor = 0; cursor < n->string.size; ) { + U64 c = cursor; + CV_Symbol symbol = {0}; + TryReadBreak(cv_read_symbol(n->string, cursor, CV_SymbolAlign, &symbol), cursor); + if (symbol.kind == CV_SymKind_SKIP) { continue; } + if (cv_is_lproc(symbol)) { + proc_count += 1; + proc_size += AlignPow2(symbol.data.size, CV_SymbolAlign); + } + } + section += 1; + } + + if (proc_count) { + U64 end_count = proc_count; + U64 symbol_count = proc_count + end_count; + U64 buffer_size = proc_size + sizeof(CV_SymbolHeader) * symbol_count; + U8 *buffer = push_array(scratch.arena, U8, buffer_size); + U64 buffer_cursor = 0; + + for EachNode(n, String8Node, src->first) { + for (U64 cursor = 0; cursor < n->string.size; ) { + CV_Symbol symbol = {0}; + TryReadBreak(cv_read_symbol(n->string, cursor, CV_SymbolAlign, &symbol), cursor); + if (symbol.kind == CV_SymKind_SKIP) { continue; } + if (cv_is_lproc(symbol)) { + CV_SymProc32 *src_proc = (CV_SymProc32 *)symbol.data.str; + src_proc->itype = 0; + + CV_Symbol end_symbol = { .kind = CV_SymKind_END }; + + buffer_cursor += cv_write_symbol(buffer, buffer_cursor, buffer_size, &symbol, CV_SymbolAlign); + buffer_cursor += cv_write_symbol(buffer, buffer_cursor, buffer_size, &end_symbol, CV_SymbolAlign); + } + } + } + Assert(buffer_cursor == buffer_size); + + str8_list_push(scratch.arena, dst, str8(buffer, buffer_size)); + } + } + + LNK_CodeViewInput stripped_cv = {0}; + stripped_cv.io_flags = config->io_flags; + stripped_cv.is_stripped = 1; + stripped_cv.obj_arr = cv.obj_arr; + stripped_cv.obj_count = cv.obj_count; + stripped_cv.count = cv.obj_count; + stripped_cv.debug_s_arr = debug_s_arr; + stripped_cv.symbol_input_ranges = push_array(scratch.arena, Rng1U64, tp->worker_count); + + String8List pdb_data = lnk_build_pdb(tp, arena, image_ctx.image_data, config, symtab, &stripped_cv, (LNK_MergedTypes){0}); + lnk_write_data_list_to_file_path(config->pdb_stripped_name, str8f(scratch.arena, "%S.tmp", config->pdb_stripped_name), pdb_data); + } + lnk_timer_end(LNK_Timer_Debug); ProfEnd(); } diff --git a/src/linker/lnk_config.c b/src/linker/lnk_config.c index ffb665b5..62b84851 100644 --- a/src/linker/lnk_config.c +++ b/src/linker/lnk_config.c @@ -53,6 +53,7 @@ global read_only LNK_CmdSwitch g_cmd_switch_map[] = { LNK_CmdSwitch_Pdb, 0, "PDB", ":FILENAME", "File name of the output PDB." }, { LNK_CmdSwitch_PdbAltPath, 0, "PDBALTPATH", ":PATH", "Alternative output path for the PDB." }, { LNK_CmdSwitch_PdbPageSize, 0, "PDBPAGESIZE", ":#", "Page size must be power of two." }, + { LNK_CmdSwitch_PdbStripped, 0, "PDBSTRIPPED", ":FILENAME", "Create a stripped PDB containing public symbols, a section map, and a list of object files." }, { LNK_CmdSwitch_Release, 1, "RELEASE", "", "Write image checksum." }, { LNK_CmdSwitch_Stack, 1, "STACK", ":RESERVE[,COMMIT]", "Set reserve and commit size for the stack." }, { LNK_CmdSwitch_SubSystem, 1, "SUBSYSTEM", ":{CONSOLE|NATIVE|WINDOWS}[,#[.##]]", "Set subsystem for the image." }, @@ -1721,6 +1722,13 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List } } break; + case LNK_CmdSwitch_PdbStripped: { + String8 file_name; + if (lnk_cmd_switch_parse_string(obj, cmd_switch, value_strings, &file_name)) { + config->pdb_stripped_name = str8_copy(config->arena, file_name); + } + } break; + case LNK_CmdSwitch_Release: { if (value_strings.node_count == 0) { config->flags |= LNK_ConfigFlag_WriteImageChecksum; diff --git a/src/linker/lnk_config.h b/src/linker/lnk_config.h index 56b1eb4a..e3bb81ba 100644 --- a/src/linker/lnk_config.h +++ b/src/linker/lnk_config.h @@ -50,8 +50,8 @@ typedef enum LNK_CmdSwitch_FailIfMismatch, LNK_CmdSwitch_FileAlign, LNK_CmdSwitch_Fixed, - LNK_CmdSwitch_FunctionPadMin, LNK_CmdSwitch_Force, + LNK_CmdSwitch_FunctionPadMin, LNK_CmdSwitch_Heap, LNK_CmdSwitch_HighEntropyVa, LNK_CmdSwitch_Ignore, @@ -79,6 +79,7 @@ typedef enum LNK_CmdSwitch_Pdb, LNK_CmdSwitch_PdbAltPath, LNK_CmdSwitch_PdbPageSize, + LNK_CmdSwitch_PdbStripped, LNK_CmdSwitch_Release, LNK_CmdSwitch_Stack, LNK_CmdSwitch_SubSystem, @@ -312,6 +313,7 @@ typedef struct LNK_Config String8List raw_cmd_line; String8 pdb_name; String8 pdb_alt_path; + String8 pdb_stripped_name; String8 mt_path; LNK_TypeNameHashMode pdb_hash_type_names; String8 pdb_hash_type_name_map; diff --git a/src/linker/lnk_debug_info.c b/src/linker/lnk_debug_info.c index 59f28680..1f720f57 100644 --- a/src/linker/lnk_debug_info.c +++ b/src/linker/lnk_debug_info.c @@ -2046,30 +2046,33 @@ lnk_write_debug_s_to_pdb_module(PDB_DbiModule *mod, CV_DebugS debug_s, String8No mod->c13_data_size = 0; mod->globrefs_size = 0; - // signature - U64 sig_size = str8_buffer_write_u32(buf, buf_pos, CV_Signature_C13); - mod->sym_data_size += sig_size; - mod_cursor += sig_size; - - // write symbols String8List symbols = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_Symbols); - U64 scope_depth = 0; - for EachNode(n, String8Node, symbols.first) { - for (U64 cursor = 0; cursor + sizeof(CV_SymbolHeader) <= n->string.size; ) { - CV_Symbol symbol = {0}; - TryReadBreak(cv_read_symbol(n->string, cursor, CV_SymbolAlign, &symbol), cursor); - if (symbol.kind == CV_SymKind_SKIP) { continue; } - if (cv_is_global_symbol(symbol.kind)) { continue; } - else if (cv_is_typedef(symbol.kind) && scope_depth == 0) { continue; } - else if (symbol.kind == 0x1176) { continue; } + if (symbols.total_size) { + // signature + U64 sig_size = str8_buffer_write_u32(buf, buf_pos, CV_Signature_C13); + mod->sym_data_size += sig_size; + mod_cursor += sig_size; - if (cv_is_scope_symbol(symbol.kind)) { scope_depth += 1; } - else if (cv_is_end_symbol(symbol.kind)) { scope_depth -= 1; } + // write symbols + U64 scope_depth = 0; + for EachNode(n, String8Node, symbols.first) { + for (U64 cursor = 0; cursor + sizeof(CV_SymbolHeader) <= n->string.size; ) { + CV_Symbol symbol = {0}; + TryReadBreak(cv_read_symbol(n->string, cursor, CV_SymbolAlign, &symbol), cursor); + if (symbol.kind == CV_SymKind_SKIP) { continue; } - U64 symbol_size = cv_write_symbol_buf(buf, buf_pos, &symbol, PDB_SYMBOL_ALIGN); - mod_cursor += symbol_size; - mod->sym_data_size += symbol_size; + if (cv_is_global_symbol(symbol.kind)) { continue; } + else if (cv_is_typedef(symbol.kind) && scope_depth == 0) { continue; } + else if (symbol.kind == 0x1176) { continue; } + + if (cv_is_scope_symbol(symbol.kind)) { scope_depth += 1; } + else if (cv_is_end_symbol(symbol.kind)) { scope_depth -= 1; } + + U64 symbol_size = cv_write_symbol_buf(buf, buf_pos, &symbol, PDB_SYMBOL_ALIGN); + mod_cursor += symbol_size; + mod->sym_data_size += symbol_size; + } } } @@ -2112,10 +2115,12 @@ lnk_write_debug_s_to_pdb_module(PDB_DbiModule *mod, CV_DebugS debug_s, String8No } // write global refs - String8List globrefs = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_GlobalRefs); - mod->globrefs_size += str8_buffer_write_u32(buf, buf_pos, safe_cast_u32(globrefs.total_size)); - mod->globrefs_size += str8_buffer_write_string_list(buf, buf_pos, globrefs); - mod_cursor += mod->globrefs_size; + if (mod->sym_data_size) { + String8List globrefs = cv_sub_section_from_debug_s(debug_s, CV_C13SubSectionKind_GlobalRefs); + mod->globrefs_size += str8_buffer_write_u32(buf, buf_pos, safe_cast_u32(globrefs.total_size)); + mod->globrefs_size += str8_buffer_write_string_list(buf, buf_pos, globrefs); + mod_cursor += mod->globrefs_size; + } return mod_cursor; } @@ -2153,6 +2158,9 @@ THREAD_POOL_TASK_FUNC(lnk_write_pdb_modules) U64 obj_idx = obj_indices.v[i]; PDB_DbiModule *mod = task->mod_arr[obj_idx]; + + if (mod->sn == MSF_INVALID_STREAM_NUMBER) { continue; } + CV_DebugS debug_s = task->cv->debug_s_arr[obj_idx]; String8List mod_data = msf_data_from_sn(temp.arena, task->pdb->msf, mod->sn); @@ -2162,10 +2170,12 @@ THREAD_POOL_TASK_FUNC(lnk_write_pdb_modules) lnk_write_debug_s_to_pdb_module(mod, debug_s, &buf, &pos); // sub range symbol data pages and patch symbol tree offsets - Rng1U64 sym_data_range = r1u64(sizeof(CV_Signature), mod->sym_data_size); - String8List mod_symbols = str8_list_substr(temp.arena, mod_data, sym_data_range); - Assert(mod_symbols.total_size == dim_1u64(sym_data_range)); - cv_patch_symbol_tree_offsets(mod_symbols, sizeof(CV_Signature), PDB_SYMBOL_ALIGN); + if (mod->sym_data_size) { + Rng1U64 sym_data_range = r1u64(sizeof(CV_Signature), mod->sym_data_size); + String8List mod_symbols = str8_list_substr(temp.arena, mod_data, sym_data_range); + Assert(mod_symbols.total_size == dim_1u64(sym_data_range)); + cv_patch_symbol_tree_offsets(mod_symbols, sizeof(CV_Signature), PDB_SYMBOL_ALIGN); + } } temp_end(temp); @@ -2204,11 +2214,14 @@ THREAD_POOL_TASK_FUNC(lnk_write_pdb_modules) for EachIndex(i, obj_indices.count) { Temp temp = temp_begin(scratch.arena); - U64 obj_idx = obj_indices.v[i]; - PDB_DbiModule *mod = task->mod_arr[obj_idx]; - String8List mod_data = msf_data_from_sn(temp.arena, task->pdb->msf, mod->sn); - Rng1U64 c13_data_range = r1u64(mod->sym_data_size + mod->c11_data_size, mod->sym_data_size + mod->c11_data_size + mod->c13_data_size); - String8List c13_data = str8_list_substr(temp.arena, mod_data, c13_data_range); + U64 obj_idx = obj_indices.v[i]; + PDB_DbiModule *mod = task->mod_arr[obj_idx]; + + if (mod->sn == MSF_INVALID_STREAM_NUMBER) { continue; } + + String8List mod_data = msf_data_from_sn(temp.arena, task->pdb->msf, mod->sn); + Rng1U64 c13_data_range = r1u64(mod->sym_data_size + mod->c11_data_size, mod->sym_data_size + mod->c11_data_size + mod->c13_data_size); + String8List c13_data = str8_list_substr(temp.arena, mod_data, c13_data_range); CV_DebugS debug_s = task->cv->debug_s_arr[obj_idx]; String8 string_table = cv_string_table_from_debug_s(debug_s); @@ -2359,9 +2372,11 @@ lnk_build_pdb(TP_Context *tp, TP_Arena *tp_arena, String8 image_data, LNK_Config .image_section_virt_ranges.v = push_array(scratch.arena, Rng1U64, task.image_section_table_count), .image_section_file_ranges.v = push_array(scratch.arena, Rng1U64, task.image_section_table_count), }; + // set min type indices for EachElement(ti_source, cv_types.min_type_indices) { task.pdb->type_servers[ti_source]->ti_lo = cv_types.min_type_indices[ti_source]; } + // per worker obj indices { U64 objs_per_worker = CeilIntegerDiv(cv->obj_count, tp->worker_count); task.obj_indices = push_array(scratch.arena, U32Array, tp->worker_count); @@ -2372,10 +2387,7 @@ lnk_build_pdb(TP_Context *tp, TP_Arena *tp_arena, String8 image_data, LNK_Config } } - // move patched type data - // - // leaf data is stored in g_file_arena which has linker's life-time - // and this way we skip redundant leaf copy to the type server to make things faster + // push types pdb_type_server_push_parallel(tp, task.pdb->type_servers[CV_TypeIndexSource_IPI], cv_types.count[CV_TypeIndexSource_IPI], cv_types.v[CV_TypeIndexSource_IPI]); pdb_type_server_push_parallel(tp, task.pdb->type_servers[CV_TypeIndexSource_TPI], cv_types.count[CV_TypeIndexSource_TPI], cv_types.v[CV_TypeIndexSource_TPI]); @@ -2453,7 +2465,7 @@ lnk_build_pdb(TP_Context *tp, TP_Arena *tp_arena, String8 image_data, LNK_Config } ProfEnd(); - pdb_build(tp, tp_arena, task.pdb, task.string_ht, 0); + pdb_build(tp, tp_arena, task.pdb, task.string_ht, 0, cv->is_stripped); MSF_Error msf_err = msf_build(task.pdb->msf); if (msf_err != MSF_Error_OK) { diff --git a/src/linker/lnk_debug_info.h b/src/linker/lnk_debug_info.h index b70bf6a0..3ab66c4a 100644 --- a/src/linker/lnk_debug_info.h +++ b/src/linker/lnk_debug_info.h @@ -27,6 +27,7 @@ typedef struct { LNK_IO_Flags io_flags; U64 obj_count; + B32 is_stripped; U64 count; LNK_Obj **obj_arr;