From 2d827ac15d1524efefd3cc3ec64ce3ffc42b29b9 Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Mon, 20 Jul 2026 14:17:31 -0700 Subject: [PATCH] make const data eligible for ICF --- src/linker/lnk.c | 14 +++++++++++++- src/linker/tests/linker_tests.c | 13 +++++++++---- src/msvc_crt/msvc_crt.h | 2 +- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/linker/lnk.c b/src/linker/lnk.c index 3581d12f..35cda232 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -2977,6 +2977,7 @@ typedef enum LNK_ICF_ColorSpace LNK_ICF_ColorSpace_Code, LNK_ICF_ColorSpace_Unwind, LNK_ICF_ColorSpace_VFTable, + LNK_ICF_ColorSpace_ConstData, // string literals, float consts, const tables (/Gw and /GF) LNK_ICF_ColorSpace_COUNT, } LNK_ICF_ColorSpace; @@ -2989,6 +2990,7 @@ lnk_string_from_icf_color_space(LNK_ICF_ColorSpace color_space) case LNK_ICF_ColorSpace_Code: { result = str8_lit("Code"); } break; case LNK_ICF_ColorSpace_Unwind: { result = str8_lit("Unwind"); } break; case LNK_ICF_ColorSpace_VFTable: { result = str8_lit("VFTables"); } break; + case LNK_ICF_ColorSpace_ConstData: { result = str8_lit("ConstData"); } break; case LNK_ICF_ColorSpace_COUNT: { result = str8_lit("Unknown"); } break; } return result; @@ -3017,13 +3019,23 @@ lnk_icf_color_space_from_section(LNK_Obj *obj, U32 sect_idx) if (str8_match(section_name, str8_lit(".xdata"), 0) || str8_match(section_name, str8_lit(".pdata"), 0)) { result = LNK_ICF_ColorSpace_Unwind; } else { - // fold MSVC vftables separately from other read-only data LNK_ObjSymbolRef symlink_ref = {0}; if (lnk_obj_get_comdat_symlink(obj, sect_idx + 1, &symlink_ref)) { String8 symlink_name = lnk_symbol_name_from_coff_symbol_idx(symlink_ref.obj, symlink_ref.symbol_idx); + // fold MSVC vftables separately from other read-only data if (str8_starts_with(symlink_name, str8_lit(MSCRT_VFTABLE_SYMBOL_PREFIX))) { result = LNK_ICF_ColorSpace_VFTable; } + // fold ordinary read-only data COMDATs; do not fold declarations and vtables that require unique addresses + else if (!str8_starts_with(symlink_name, str8_lit(MSCRT_VBTABLE_SYMBOL_PREFIX))) { + COFF_ComdatSelectType select = COFF_ComdatSelect_Null; + if (lnk_try_comdat_props_from_section_number(obj, sect_idx + 1, &select, 0, 0, 0)) { + if (select == COFF_ComdatSelect_Any || select == COFF_ComdatSelect_SameSize || + select == COFF_ComdatSelect_ExactMatch || select == COFF_ComdatSelect_Largest) { + result = LNK_ICF_ColorSpace_ConstData; + } + } + } } } } diff --git a/src/linker/tests/linker_tests.c b/src/linker/tests/linker_tests.c index 58801bec..99b0b0ad 100644 --- a/src/linker/tests/linker_tests.c +++ b/src/linker/tests/linker_tests.c @@ -9249,7 +9249,7 @@ TEST(icf_readonly_non_vftable_data_policy) { U8 data_bytes[] = { 1, 2, 3, 4 }; U8 entry_text[] = { 0xc3 }; - U8 addresses[2 * sizeof(U64)] = {0}; + U8 addresses[4 * sizeof(U64)] = {0}; T_Ok(t_write_def_obj("icf_rdata_policy.obj", (T_COFF_DefObj){ .machine = T_COFF_DefSetMachine(X64), @@ -9257,12 +9257,15 @@ TEST(icf_readonly_non_vftable_data_policy) { "entry", ".text", str8_array_fixed(entry_text), .flags = "rx:code@1" }, { "data_a", ".rdata$mn", str8_array_fixed(data_bytes), .flags = "r:data@1", .raw_flags = COFF_SectionFlag_LnkCOMDAT }, { "data_b", ".rdata$mn", str8_array_fixed(data_bytes), .flags = "r:data@1", .raw_flags = COFF_SectionFlag_LnkCOMDAT }, - { "addresses", ".data", str8_array_fixed(addresses), .flags = "rw:data@1", .relocs = (T_COFF_DefReloc[]){ T_COFF_DefReloc(X64_Addr64, 0, "data_a"), T_COFF_DefReloc(X64_Addr64, 8, "data_b"), {0} } }, + { "data_c", ".rdata$mn", str8_array_fixed(data_bytes), .flags = "r:data@1", .raw_flags = COFF_SectionFlag_LnkCOMDAT }, + { "data_d", ".rdata$mn", str8_array_fixed(data_bytes), .flags = "r:data@1", .raw_flags = COFF_SectionFlag_LnkCOMDAT }, + { "addresses", ".data", str8_array_fixed(addresses), .flags = "rw:data@1", .relocs = (T_COFF_DefReloc[]){ T_COFF_DefReloc(X64_Addr64, 0, "data_a"), T_COFF_DefReloc(X64_Addr64, 8, "data_b"), T_COFF_DefReloc(X64_Addr64, 16, "data_c"), T_COFF_DefReloc(X64_Addr64, 24, "data_d"), {0} } }, {0} }, .symbols = (T_COFF_DefSymbol[]){ T_COFF_DefSymbol_Secdef("data_a", COFF_ComdatSelect_NoDuplicates), T_COFF_DefSymbol_Secdef("data_b", COFF_ComdatSelect_NoDuplicates), - T_COFF_DefSymbol_ExternFunc("entry", "entry", 0), T_COFF_DefSymbol_Extern("data_a", "data_a", 0), T_COFF_DefSymbol_Extern("data_b", "data_b", 0), T_COFF_DefSymbol_Extern("addresses", "addresses", 0), + T_COFF_DefSymbol_Secdef("data_c", COFF_ComdatSelect_Any), T_COFF_DefSymbol_Secdef("data_d", COFF_ComdatSelect_Any), + T_COFF_DefSymbol_ExternFunc("entry", "entry", 0), T_COFF_DefSymbol_Extern("data_a", "data_a", 0), T_COFF_DefSymbol_Extern("data_b", "data_b", 0), T_COFF_DefSymbol_Extern("data_c", "data_c", 0), T_COFF_DefSymbol_Extern("data_d", "data_d", 0), T_COFF_DefSymbol_Extern("addresses", "addresses", 0), {0} } })); @@ -9270,11 +9273,13 @@ TEST(icf_readonly_non_vftable_data_policy) t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /opt:ref,icf /include:addresses icf_rdata_policy.obj"); T_Ok(g_last_exit_code == 0); - U64 vaddrs[2] = {0}; + U64 vaddrs[4] = {0}; T_Ok(t_read_exe_data_vaddrs(arena, str8_lit("a.exe"), vaddrs, ArrayCount(vaddrs))); T_Ok(vaddrs[0] != 0); T_Ok(vaddrs[1] != 0); T_Ok(vaddrs[0] != vaddrs[1]); + T_Ok(vaddrs[2] != 0); + T_Ok(vaddrs[2] == vaddrs[3]); } TEST(icf_cpp_identical_functions_fold) diff --git a/src/msvc_crt/msvc_crt.h b/src/msvc_crt/msvc_crt.h index 9932ee73..fc9a96be 100644 --- a/src/msvc_crt/msvc_crt.h +++ b/src/msvc_crt/msvc_crt.h @@ -35,6 +35,7 @@ // MSVC C++ vftable symbols start with this decorated-name prefix. #define MSCRT_VFTABLE_SYMBOL_PREFIX "??_7" +#define MSCRT_VBTABLE_SYMBOL_PREFIX "??_8" //////////////////////////////// @@ -380,4 +381,3 @@ mscrt_catch_blocks_from_data_x8664(Arena *arena, internal String8 mscrt_string_from_eh_adjectives(Arena *arena, MSCRT_EhHandlerTypeFlags adjectives); #endif // MSVC_CRT -