From 89e2ea15ad8ed490e6c3c8a54240e8a7408835af Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Wed, 28 May 2025 09:42:05 -0700 Subject: [PATCH] add /RAD_REMOVE_SECTION switch and special case debug sections --- src/linker/lnk.c | 135 +++++++++++++++++++++------------- src/linker/lnk.h | 2 +- src/linker/lnk_config.c | 9 +++ src/linker/lnk_config.h | 12 +-- src/linker/lnk_debug_helper.c | 2 +- src/linker/lnk_obj.c | 24 +++--- src/linker/lnk_obj.h | 1 + 7 files changed, 116 insertions(+), 69 deletions(-) diff --git a/src/linker/lnk.c b/src/linker/lnk.c index d6d1ec90..4fdbd304 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -149,6 +149,10 @@ //////////////////////////////// +global read_only LNK_SectionContrib g_null_sc; + +//////////////////////////////// + internal LNK_Config * lnk_config_from_argcv(Arena *arena, int argc, char **argv) { @@ -204,6 +208,7 @@ lnk_config_from_argcv(Arena *arena, int argc, char **argv) #else lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_SuppressError, "%u", LNK_Error_InvalidTypeIndex); #endif + // default section merges lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Merge, ".xdata=.rdata"); lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Merge, ".tls=.data"); @@ -212,6 +217,9 @@ lnk_config_from_argcv(Arena *arena, int argc, char **argv) lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Merge, ".didat=.rdata"); lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Merge, ".RAD_LINKER_DEBUG_DIR=.rdata"); + // sections to remove from the image + lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_RemoveSection, ".debug"); + // set default max worker count if (lnk_cmd_line_has_switch(cmd_line, LNK_CmdSwitch_Rad_SharedThreadPool)) { lnk_cmd_line_push_optionf(scratch.arena, &cmd_line, LNK_CmdSwitch_Rad_SharedThreadPoolMaxWorkers, ""); @@ -1195,10 +1203,24 @@ THREAD_POOL_TASK_FUNC(lnk_obj_reloc_patcher) for (U64 sect_idx = 0; sect_idx < obj_header.section_count_no_null; sect_idx += 1) { COFF_SectionHeader *section_header = §ion_table[sect_idx]; - // was section discarded? + // was section removed? if (section_header->flags & COFF_SectionFlag_LnkRemove) { continue; } + if (section_header->flags & COFF_SectionFlag_CntUninitializedData) { + continue; + } + + // get section file range + Rng1U64 section_frange = rng_1u64(section_header->foff, section_header->foff + section_header->fsize); + + // get section bytes + String8 section_data; + if (lnk_is_coff_section_debug(obj, sect_idx)) { + section_data = str8_substr(obj->data, section_frange); + } else { + section_data = str8_substr(task->image_data, section_frange); + } // find section relocs COFF_RelocInfo reloc_info = coff_reloc_info_from_section_header(obj->data, section_header); @@ -1217,8 +1239,7 @@ THREAD_POOL_TASK_FUNC(lnk_obj_reloc_patcher) NotImplemented; } - // compute relocation file/virtual offsets - U64 reloc_foff = section_header->foff + reloc->apply_off; + // compute virtual offsets U64 reloc_voff = section_header->voff + reloc->apply_off; // compute symbol location values @@ -1267,16 +1288,16 @@ THREAD_POOL_TASK_FUNC(lnk_obj_reloc_patcher) } // read addend - Assert(reloc_foff + reloc_value.size <= task->image_data.size); + Assert(reloc_value.size <= section_data.size); U64 raw_addend = 0; - str8_deserial_read(task->image_data, reloc_foff, &raw_addend, reloc_value.size, 1); + str8_deserial_read(section_data, reloc->apply_off, &raw_addend, reloc_value.size, 1); // compute new reloc value S64 addend = extend_sign64(raw_addend, reloc_value.size); U64 reloc_result = reloc_value.value + addend; // commit new reloc value - MemoryCopy(task->image_data.str + reloc_foff, &reloc_result, reloc_value.size); + MemoryCopy(section_data.str + reloc->apply_off, &reloc_result, reloc_value.size); } } } @@ -2174,7 +2195,7 @@ lnk_pdata_is_before_x8664(void *raw_a, void *raw_b) } internal String8 -lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, LNK_ObjList obj_list) +lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, U64 objs_count, LNK_Obj **objs) { Temp scratch = scratch_begin(arena->v, arena->count); @@ -2186,14 +2207,8 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S lnk_section_table_push(sectab, str8_lit(".data"), PE_DATA_SECTION_FLAGS); lnk_section_table_push(sectab, str8_lit(".rdata"), PE_RDATA_SECTION_FLAGS); - // - // obj list -> array - // - U64 objs_count = obj_list.count; - LNK_Obj **objs = lnk_array_from_obj_list(scratch.arena, obj_list); - - ProfBegin("Remove Associatives"); { + ProfBegin("Remove Associative Sections"); for (U64 obj_idx = 0; obj_idx < objs_count; obj_idx += 1) { LNK_Obj *obj = objs[obj_idx]; String8 string_table = str8_substr(obj->data, obj->header.string_table_range); @@ -2227,8 +2242,8 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S } } } + ProfEnd(); } - ProfEnd(); { ProfBegin("Define And Count Sections"); @@ -2247,7 +2262,7 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { COFF_SectionHeader *sect_header = §ion_table[sect_idx]; - // discard section + // remove section if (sect_header->flags & COFF_SectionFlag_LnkRemove) { continue; } @@ -2257,19 +2272,13 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S String8 sect_name, sect_sort_idx; coff_parse_section_name(full_sect_name, §_name, §_sort_idx); - // remove debug sections - if (str8_match(sect_name, str8_lit(".debug"), 0)) { - sect_header->flags |= COFF_SectionFlag_LnkRemove; - continue; - } - - // strip linker flags - COFF_SectionFlags sect_flags = sect_header->flags & ~COFF_SectionFlags_LnkFlags; - // was section defined? + COFF_SectionFlags sect_flags = sect_header->flags & ~COFF_SectionFlags_LnkFlags; String8 sect_name_with_flags = lnk_make_name_with_flags(temp.arena, sect_name, sect_flags); LNK_SectionDefinition *sect_defn = 0; hash_table_search_string_raw(sect_defn_ht, sect_name_with_flags, §_defn); + + // create section definition if (sect_defn == 0) { sect_defn = push_array(temp.arena, LNK_SectionDefinition, 1); sect_defn->name = sect_name; @@ -2314,20 +2323,30 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S for (U64 defn_idx = 0; defn_idx < sect_defns_count; defn_idx += 1) { LNK_SectionDefinition *sect_defn = sect_defns[defn_idx]; + // do not create definitions for sections that are removed from the image + { + B32 skip = 0; + for (String8Node *name_n = config->remove_sections.first; name_n != 0; name_n = name_n->next) { + if (str8_match(sect_defn->name, name_n->string, 0)) { + skip = 1; + break; + } + } + if (skip) { continue; } + } + // warn about conflicting section flags for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { LNK_Section *sect = §_n->data; if (str8_match(sect->name, sect_defn->name, 0)) { if (sect->flags != sect_defn->flags) { LNK_Obj *obj = sect_defn->obj; - String8 string_table = str8_substr(obj->data, obj->header.string_table_range); - COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; - COFF_SectionHeader *sect_header = §ion_table[sect_defn->obj_sect_idx]; - String8 full_sect_name = coff_name_from_section_header(string_table, sect_header); U32 sect_number = sect_defn->obj_sect_idx + 1; + COFF_SectionHeader *sect_header = lnk_coff_section_header_from_section_number(obj, sect_number); + String8 sect_name = coff_name_from_section_header(str8_substr(obj->data, obj->header.string_table_range), sect_header); String8 expected_flags_str = coff_string_from_section_flags(temp.arena, sect->flags); String8 current_flags_str = coff_string_from_section_flags(temp.arena, sect_defn->flags); - lnk_error_obj(LNK_Warning_SectionFlagsConflict, sect_defn->obj, "detected section flags conflict in %S(No. %X); expected {%S} but got {%S}", full_sect_name, sect_number, expected_flags_str, current_flags_str); + lnk_error_obj(LNK_Warning_SectionFlagsConflict, sect_defn->obj, "detected section flags conflict in %S(No. %X); expected {%S} but got {%S}", sect_name, sect_number, expected_flags_str, current_flags_str); } } } @@ -2371,30 +2390,36 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S String8 sect_name, sect_sort_idx; coff_parse_section_name(full_sect_name, §_name, §_sort_idx); - // extract section bytes - String8 sect_data = str8_substr(obj->data, rng_1u64(sect_header->foff, sect_header->foff + sect_header->fsize)); - - // extract align - U16 sc_align = coff_align_size_from_section_flags(sect_header->flags); - if (sc_align == 0) { - sc_align = default_align; - } - // search for section to contribute COFF_SectionFlags flags = sect_header->flags & ~COFF_SectionFlags_LnkFlags; LNK_Section *sect = lnk_section_table_search(sectab, sect_name, flags); - String8Node *data_n = push_array(sectab->arena, String8Node, 1); - data_n->string = sect_data; + LNK_SectionContrib *sc; + if (sect) { + // extract align + U16 sc_align = coff_align_size_from_section_flags(sect_header->flags); + if (sc_align == 0) { + sc_align = default_align; + } - // fill out contrib - LNK_SectionContrib *sc = lnk_section_contrib_chunk_push(sect->contribs.first, 1); - sc->align = sc_align; - sc->data_list = data_n; - sc->u.obj_idx = obj_idx; - sc->u.obj_sect_idx = sect_idx; - sc->u.sort_idx_size = (U16)sect_sort_idx.size; - sc->u.sort_idx = sect_sort_idx.str; + // extract section bytes + String8 sect_data = str8_substr(obj->data, rng_1u64(sect_header->foff, sect_header->foff + sect_header->fsize)); + + String8Node *data_n = push_array(sectab->arena, String8Node, 1); + data_n->string = sect_data; + + // fill out contrib + sc = lnk_section_contrib_chunk_push(sect->contribs.first, 1); + sc->align = sc_align; + sc->data_list = data_n; + sc->u.obj_idx = obj_idx; + sc->u.obj_sect_idx = sect_idx; + sc->u.sort_idx_size = (U16)sect_sort_idx.size; + sc->u.sort_idx = sect_sort_idx.str; + } else { + // section was removed, fill slot with pointer to null contrib + sc = &g_null_sc; + } sect_map[obj_idx][sect_idx] = sc; } @@ -2619,7 +2644,7 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S U32 section_number = 0; U32 value = 0; { - COFF_SectionHeader *sect_header = lnk_section_header_from_section_number(obj, symbol.section_number); + COFF_SectionHeader *sect_header = lnk_coff_section_header_from_section_number(obj, symbol.section_number); if (~sect_header->flags & COFF_SectionFlag_LnkRemove) { LNK_SectionContrib *sc = sect_map[obj_idx][symbol.section_number-1]; section_number = safe_cast_u32(sc->u.sect_idx + 1); @@ -2933,7 +2958,9 @@ lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_S COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; for (U64 sect_idx = 0; sect_idx < obj->header.section_count_no_null; sect_idx += 1) { COFF_SectionHeader *sect_header = §ion_table[sect_idx]; - if (~sect_header->flags & COFF_SectionFlag_LnkRemove) { + B32 patch_section_header = (~sect_header->flags & COFF_SectionFlag_LnkRemove) && + !lnk_is_coff_section_debug(obj, sect_idx); + if (patch_section_header) { LNK_SectionContrib *sc = sect_map[obj_idx][sect_idx]; LNK_Section *sect = sects.v[sc->u.sect_idx]; if (~sect->flags & COFF_SectionFlag_CntUninitializedData) { @@ -4578,8 +4605,12 @@ lnk_run(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) } } break; case State_BuildImage: { + // obj list -> array + U64 objs_count = obj_list.count; + LNK_Obj **objs = lnk_array_from_obj_list(scratch.arena, obj_list); + // build image - image_data = lnk_build_win32_image(tp_arena, tp, config, symtab, obj_list); + image_data = lnk_build_win32_image(tp_arena, tp, config, symtab, objs_count, objs); // write image to disk in a background thread { diff --git a/src/linker/lnk.h b/src/linker/lnk.h index 4ee435fb..ebc84183 100644 --- a/src/linker/lnk.h +++ b/src/linker/lnk.h @@ -175,7 +175,7 @@ internal void lnk_queue_lib_member_input(Arena *arena, PathStyle pat internal String8List lnk_build_guard_tables(TP_Context *tp, LNK_SectionTable *sectab, LNK_SymbolTable *symtab, U64 objs_count, LNK_Obj **objs, COFF_MachineType machine, String8 entry_point_name, LNK_GuardFlags guard_flags, B32 emit_suppress_flag); internal String8List lnk_build_base_relocs(TP_Context *tp, TP_Arena *tp_temp, LNK_Config *config, U64 objs_count, LNK_Obj **objs); internal String8List lnk_build_win32_image_header(Arena *arena, LNK_SymbolTable *symtab, LNK_Config *config, LNK_SectionArray sect_arr, U64 expected_image_header_size); -internal String8 lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, LNK_ObjList obj_list); +internal String8 lnk_build_win32_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolTable *symtab, U64 obj_count, LNK_Obj **objs); // --- Logger ------------------------------------------------------------------ diff --git a/src/linker/lnk_config.c b/src/linker/lnk_config.c index 1ecbc2db..a1fca3da 100644 --- a/src/linker/lnk_config.c +++ b/src/linker/lnk_config.c @@ -145,6 +145,7 @@ global read_only LNK_CmdSwitch g_cmd_switch_map[] = { { LNK_CmdSwitch_Rad_PdbHashTypeNameLength, 0, "RAD_PDB_HASH_TYPE_NAME_LENGTH", ":#", "Number of hash bytes to use to replace type name. Default 8 bytes (Max 16)." }, { LNK_CmdSwitch_Rad_PdbHashTypeNameMap, 0, "RAD_PDB_HASH_TYPE_NAME_MAP", ":FILENAME", "Produce map file with hash -> type name mappings." }, { LNK_CmdSwitch_Rad_PdbHashTypeNames, 0, "RAD_PDB_HASH_TYPE_NAMES", ":{NONE|LENIENT|FULL}", "Replace type names in LF_STRUCTURE and LF_CLASS with hashes." }, + { LNK_CmdSwitch_Rad_RemoveSection, 0, "RAD_REMOVE_SECTION", ":NAME", "Removes a section from output image." }, { LNK_CmdSwitch_Rad_SectVirtOff, 0, "RAD_SECT_VIRT_OFF", ":#", "Set RVA where section data is placed in memory. For internal use only." }, { LNK_CmdSwitch_Rad_SharedThreadPool, 0, "RAD_SHARED_THREAD_POOL", "[:STRING]", "Default value \"" LNK_DEFAULT_THREAD_POOL_NAME "\"" }, { LNK_CmdSwitch_Rad_SharedThreadPoolMaxWorkers, 0, "RAD_SHARED_THREAD_POOL_MAX_WORKERS", ":#", "Sets maximum number of workers in a thread pool." }, @@ -1779,6 +1780,14 @@ lnk_apply_cmd_option_to_config(Arena *arena, LNK_Config *config, String8 cmd_nam lnk_cmd_switch_parse_u64(obj_path, lib_path, cmd_switch, value_strings, &config->pdb_hash_type_name_length, 0); } break; + case LNK_CmdSwitch_Rad_RemoveSection: { + String8 sect_name = {0}; + if (lnk_cmd_switch_parse_string(obj_path, lib_path, cmd_switch, value_strings, §_name)) { + sect_name = push_str8_copy(arena, sect_name); + str8_list_push(arena, &config->remove_sections, sect_name); + } + } break; + case LNK_CmdSwitch_Rad_SectVirtOff: { U64 sect_virt_off; if (lnk_cmd_switch_parse_u64(obj_path, lib_path, cmd_switch, value_strings, §_virt_off, LNK_ParseU64Flag_CheckUnder32bit)) { diff --git a/src/linker/lnk_config.h b/src/linker/lnk_config.h index 5a361e1d..fdf7b4ae 100644 --- a/src/linker/lnk_config.h +++ b/src/linker/lnk_config.h @@ -130,8 +130,8 @@ typedef enum LNK_CmdSwitch_Rad_CheckUnusedDelayLoadDll, LNK_CmdSwitch_Rad_ChunkMap, LNK_CmdSwitch_Rad_Debug, - LNK_CmdSwitch_Rad_DebugName, LNK_CmdSwitch_Rad_DebugAltPath, + LNK_CmdSwitch_Rad_DebugName, LNK_CmdSwitch_Rad_DelayBind, LNK_CmdSwitch_Rad_DoMerge, LNK_CmdSwitch_Rad_EnvLib, @@ -145,22 +145,23 @@ typedef enum LNK_CmdSwitch_Rad_OsVer, LNK_CmdSwitch_Rad_PageSize, LNK_CmdSwitch_Rad_PathStyle, - LNK_CmdSwitch_Rad_PdbHashTypeNames, - LNK_CmdSwitch_Rad_PdbHashTypeNameMap, LNK_CmdSwitch_Rad_PdbHashTypeNameLength, + LNK_CmdSwitch_Rad_PdbHashTypeNameMap, + LNK_CmdSwitch_Rad_PdbHashTypeNames, + LNK_CmdSwitch_Rad_RemoveSection, LNK_CmdSwitch_Rad_SectVirtOff, LNK_CmdSwitch_Rad_SharedThreadPool, LNK_CmdSwitch_Rad_SharedThreadPoolMaxWorkers, LNK_CmdSwitch_Rad_SuppressError, LNK_CmdSwitch_Rad_SymbolTableCapDefined, LNK_CmdSwitch_Rad_SymbolTableCapInternal, - LNK_CmdSwitch_Rad_SymbolTableCapWeak, LNK_CmdSwitch_Rad_SymbolTableCapLib, - LNK_CmdSwitch_Rad_WriteTempFiles, + LNK_CmdSwitch_Rad_SymbolTableCapWeak, LNK_CmdSwitch_Rad_TargetOs, LNK_CmdSwitch_Rad_TimeStamp, LNK_CmdSwitch_Rad_Version, LNK_CmdSwitch_Rad_Workers, + LNK_CmdSwitch_Rad_WriteTempFiles, LNK_CmdSwitch_Help, @@ -380,6 +381,7 @@ typedef struct LNK_Config String8 temp_pdb_name; String8 temp_rad_debug_name; String8 temp_rad_chunk_map_name; + String8List remove_sections; } LNK_Config; typedef enum diff --git a/src/linker/lnk_debug_helper.c b/src/linker/lnk_debug_helper.c index b4190db4..aadcb79b 100644 --- a/src/linker/lnk_debug_helper.c +++ b/src/linker/lnk_debug_helper.c @@ -64,7 +64,7 @@ lnk_make_dll_import_debug_symbols(Arena *arena, COFF_MachineType machine, String cv_symbol_list_push_data(scratch.arena, &symbol_list, CV_SymKind_COMPILE3, comp3_data); // S_END - cv_symbol_list_push_data(scratch.arena, &symbol_list, CV_SymKind_END, str8_zero()); + //cv_symbol_list_push_data(scratch.arena, &symbol_list, CV_SymKind_END, str8_zero()); // TODO: add thunks diff --git a/src/linker/lnk_obj.c b/src/linker/lnk_obj.c index b84254f3..ccdf54fa 100644 --- a/src/linker/lnk_obj.c +++ b/src/linker/lnk_obj.c @@ -420,6 +420,20 @@ lnk_coff_section_header_from_section_number(LNK_Obj *obj, U64 section_number) return section_header; } +internal B32 +lnk_is_coff_section_debug(LNK_Obj *obj, U64 sect_idx) +{ + String8 string_table = str8_substr(obj->data, obj->header.string_table_range); + COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, sect_idx+1); + + String8 full_name = coff_name_from_section_header(string_table, section_header); + String8 name, postfix; + coff_parse_section_name(full_name, &name, &postfix); + + B32 is_debug = str8_match(name, str8_lit(".debug"), 0); + return is_debug; +} + internal COFF_ParsedSymbol lnk_parsed_symbol_from_coff_symbol_idx(LNK_Obj *obj, U64 symbol_idx) { @@ -436,16 +450,6 @@ lnk_parsed_symbol_from_coff_symbol_idx(LNK_Obj *obj, U64 symbol_idx) return result; } -internal COFF_SectionHeader * -lnk_section_header_from_section_number(LNK_Obj *obj, U64 section_number) -{ - COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(obj->data, obj->header.section_table_range).str; - if (section_number > 0 && section_number <= obj->header.section_count_no_null) { - return §ion_table[section_number-1]; - } - return 0; -} - internal THREAD_POOL_TASK_FUNC(lnk_collect_obj_chunks_task) { diff --git a/src/linker/lnk_obj.h b/src/linker/lnk_obj.h index dd025992..a39748b2 100644 --- a/src/linker/lnk_obj.h +++ b/src/linker/lnk_obj.h @@ -108,6 +108,7 @@ internal U32 lnk_obj_get_vol_md(LNK_Obj *obj); internal COFF_ParsedSymbol lnk_parsed_symbol_from_coff(LNK_Obj *obj, void *coff_symbol); internal COFF_ParsedSymbol lnk_parsed_symbol_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 B32 lnk_is_coff_section_debug(LNK_Obj *obj, U64 sect_idx); // --- Helpers -----------------------------------------------------------------