mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-08-08 14:58:08 +00:00
implement /OPT:ICF
Add identical COMDAT folding using a color-refinement over foldable read-only sections. Hash section contents, relocation targets, and target colors until partitions stabilize, then redirect folded COMDAT symlinks and discard duplicate sections. Honor .llvm_addrsig via /LLVM_ADDRSIG so address-significant sections are not folded, keep separate color space for code, unwind info, and MSVC vftables, and update COMDAT symbol/section mapping after folding. Add linker tests covering function folding, relocation-sensitive folds, alignment, color-space separation, symlink chains, .llvm_addrsig, pdata/xdata, and C++ ICF cases.
This commit is contained in:
committed by
Ryan Fleury
parent
1ea9fae835
commit
d34e1a7802
+641
-84
@@ -171,6 +171,8 @@ lnk_make_default_cmd_line(Arena *arena, LNK_CmdLine user_cmd_line)
|
|||||||
"/RAD_SORT_IMPORTS",
|
"/RAD_SORT_IMPORTS",
|
||||||
(char*)str8f(scratch.arena, "/RAD_MT_PATH:%s", LNK_MANIFEST_MERGE_TOOL_NAME).str,
|
(char*)str8f(scratch.arena, "/RAD_MT_PATH:%s", LNK_MANIFEST_MERGE_TOOL_NAME).str,
|
||||||
(char*)str8f(scratch.arena, "/RAD_DATA_DIR_COUNT:%u", PE_DataDirectoryIndex_COUNT).str,
|
(char*)str8f(scratch.arena, "/RAD_DATA_DIR_COUNT:%u", PE_DataDirectoryIndex_COUNT).str,
|
||||||
|
|
||||||
|
"/LLVM_ADDRSIG",
|
||||||
};
|
};
|
||||||
|
|
||||||
char *push_opts[] = {
|
char *push_opts[] = {
|
||||||
@@ -234,11 +236,10 @@ lnk_make_default_cmd_line(Arena *arena, LNK_CmdLine user_cmd_line)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// when /FORCE is specified on the command line, do not stop on these errors
|
// when /FORCE is specified on the command line, do not stop on these errors
|
||||||
#if 0
|
if (lnk_cmd_line_has_switch(user_cmd_line, LNK_CmdSwitch_Force)) {
|
||||||
if (lnk_cmd_line_has_switch(cmd_line, LNK_CmdSwitch_Force)) {
|
|
||||||
g_error_mode_arr[LNK_Error_UnresolvedSymbol] = LNK_ErrorMode_Continue;
|
g_error_mode_arr[LNK_Error_UnresolvedSymbol] = LNK_ErrorMode_Continue;
|
||||||
|
g_error_mode_arr[LNK_Error_RelocationAgainstRemovedSection] = LNK_ErrorMode_Continue;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#undef DefaultOpt
|
#undef DefaultOpt
|
||||||
#undef PushOpt
|
#undef PushOpt
|
||||||
@@ -2397,19 +2398,31 @@ lnk_link_image(TP_Context *tp, TP_Arena *arena, LNK_Config *config, LNK_Inputer
|
|||||||
scratch_end(debug_scratch);
|
scratch_end(debug_scratch);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: /FORCE
|
if (unresolved_symbols_count && !config->force) {
|
||||||
if (unresolved_symbols_count) {
|
|
||||||
lnk_exit(LNK_Error_UnresolvedSymbol);
|
lnk_exit(LNK_Error_UnresolvedSymbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfEnd();
|
ProfEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
LNK_Obj **objs = 0;
|
||||||
|
|
||||||
//
|
//
|
||||||
// discard COMDAT sections that are not referenced
|
// discard COMDAT sections that are not referenced
|
||||||
//
|
//
|
||||||
if (config->opt_ref == LNK_SwitchState_Yes) {
|
if (config->opt_ref == LNK_SwitchState_Yes) {
|
||||||
lnk_opt_ref(tp, symtab, config, link->objs);
|
if (objs == 0) { objs = lnk_array_from_obj_list(scratch.arena, link->objs); }
|
||||||
|
lnk_opt_ref(tp, symtab, config, objs, link->objs.count);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// fold duplicate sections
|
||||||
|
//
|
||||||
|
if (config->opt_icf == LNK_SwitchState_Yes) {
|
||||||
|
if (objs == 0) { objs = lnk_array_from_obj_list(scratch.arena, link->objs); }
|
||||||
|
lnk_opt_icf(tp, symtab, config, objs, link->objs.count);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -2503,18 +2516,109 @@ lnk_reloc_ref_batch_list_concat_in_place_atomic(LNK_RelocRefsBatchList *list, LN
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal U32Array *
|
||||||
|
lnk_obj_indices_from_section_counts(Arena *arena, U64 worker_count, LNK_Obj **objs, U64 objs_count)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(&arena, 1);
|
||||||
|
|
||||||
|
U64 *worker_section_counts = push_array(scratch.arena, U64, worker_count);
|
||||||
|
U64 *worker_obj_counts = push_array(scratch.arena, U64, worker_count);
|
||||||
|
U32Array *obj_indices = push_array(arena, U32Array, worker_count);
|
||||||
|
|
||||||
|
for EachIndex(obj_idx, objs_count) {
|
||||||
|
U64 min_worker_idx = 0;
|
||||||
|
for (U64 worker_idx = 1; worker_idx < worker_count; worker_idx += 1) {
|
||||||
|
if (worker_section_counts[worker_idx] < worker_section_counts[min_worker_idx]) {
|
||||||
|
min_worker_idx = worker_idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
worker_section_counts[min_worker_idx] += objs[obj_idx]->header.section_count_no_null;
|
||||||
|
worker_obj_counts[min_worker_idx] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for EachIndex(worker_idx, worker_count) {
|
||||||
|
obj_indices[worker_idx].v = push_array_no_zero(arena, U32, worker_obj_counts[worker_idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryZero(worker_section_counts, sizeof(worker_section_counts[0])*worker_count);
|
||||||
|
MemoryZero(worker_obj_counts, sizeof(worker_obj_counts[0])*worker_count);
|
||||||
|
|
||||||
|
for EachIndex(obj_idx, objs_count) {
|
||||||
|
U64 min_worker_idx = 0;
|
||||||
|
for (U64 worker_idx = 1; worker_idx < worker_count; worker_idx += 1) {
|
||||||
|
if (worker_section_counts[worker_idx] < worker_section_counts[min_worker_idx]) {
|
||||||
|
min_worker_idx = worker_idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
U32Array *worker_obj_indices = &obj_indices[min_worker_idx];
|
||||||
|
worker_obj_indices->v[worker_obj_counts[min_worker_idx]++] = (U32)obj_idx;
|
||||||
|
worker_obj_indices->count += 1;
|
||||||
|
worker_section_counts[min_worker_idx] += objs[obj_idx]->header.section_count_no_null;
|
||||||
|
}
|
||||||
|
|
||||||
|
scratch_end(scratch);
|
||||||
|
return obj_indices;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal B32
|
||||||
|
lnk_resolve_reloc_target_symbol(Arena *arena, LNK_SymbolTable *symtab, LNK_ObjSymbolRef symbol, String8 pass_name, LNK_ObjSymbolRef *resolved_symbol_out)
|
||||||
|
{
|
||||||
|
B32 is_resolved = 1;
|
||||||
|
|
||||||
|
Temp temp = temp_begin(arena);
|
||||||
|
HashMap seen_hm = {0};
|
||||||
|
B32 keep_walking = 1;
|
||||||
|
LNK_ObjSymbolRef result = symbol;
|
||||||
|
do {
|
||||||
|
// detect cyclic chains
|
||||||
|
U64 symbol_key = ((U64)result.obj->input_idx << 32ull) | (U64)result.symbol_idx;
|
||||||
|
if (hash_map_search_u64_u64(&seen_hm, symbol_key) == 0) {
|
||||||
|
hash_map_push_u64_u64(temp.arena, &seen_hm, symbol_key, 1);
|
||||||
|
} else {
|
||||||
|
COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_coff_symbol_idx(symbol.obj, symbol.symbol_idx);
|
||||||
|
lnk_error_obj(LNK_Warning_CyclicSymbol, symbol.obj, "symbol %S forms a cyclic chain (%S)", symbol_parsed.name, pass_name);
|
||||||
|
MemoryZeroStruct(&result);
|
||||||
|
is_resolved = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// unpack symbol
|
||||||
|
COFF_ParsedSymbol result_parsed = lnk_parsed_symbol_from_coff_symbol_idx(result.obj, result.symbol_idx);
|
||||||
|
COFF_SymbolValueInterpType result_interp = coff_interp_from_parsed_symbol(result_parsed);
|
||||||
|
|
||||||
|
// resolve symbol
|
||||||
|
LNK_ObjSymbolRef next_ref = {0};
|
||||||
|
if (lnk_resolve_symbol(symtab, result, &next_ref)) {
|
||||||
|
keep_walking = (result_interp == COFF_SymbolValueInterp_Weak || result_interp == COFF_SymbolValueInterp_Undefined);
|
||||||
|
result = next_ref;
|
||||||
|
} else {
|
||||||
|
keep_walking = 0;
|
||||||
|
}
|
||||||
|
} while (keep_walking);
|
||||||
|
|
||||||
|
if (resolved_symbol_out) {
|
||||||
|
*resolved_symbol_out = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_end(temp);
|
||||||
|
return is_resolved;
|
||||||
|
}
|
||||||
|
|
||||||
internal
|
internal
|
||||||
THREAD_POOL_TASK_FUNC(lnk_walk_relocs_and_mark_ref_sections_task)
|
THREAD_POOL_TASK_FUNC(lnk_opt_ref_task)
|
||||||
{
|
{
|
||||||
ProfBeginFunction();
|
ProfBeginFunction();
|
||||||
|
|
||||||
Temp scratch = scratch_begin(0,0);
|
Temp scratch = scratch_begin(0,0);
|
||||||
Temp scratch2 = scratch_begin(&scratch.arena, 1);
|
Temp scratch2 = scratch_begin(&scratch.arena, 1);
|
||||||
|
|
||||||
LNK_OptRefTask *task = raw_task;
|
LNK_OptTask *task = raw_task;
|
||||||
LNK_SymbolTable *symtab = task->symtab;
|
LNK_SymbolTable *symtab = task->symtab;
|
||||||
LNK_Config *config = task->config;
|
LNK_Config *config = task->config;
|
||||||
LNK_ObjList objs = task->objs;
|
LNK_Obj **objs = task->objs;
|
||||||
|
U64 objs_count = task->objs_count;
|
||||||
|
|
||||||
U8 **is_live = 0;
|
U8 **is_live = 0;
|
||||||
U64 *active_thread_count = 0;
|
U64 *active_thread_count = 0;
|
||||||
@@ -2524,17 +2628,16 @@ THREAD_POOL_TASK_FUNC(lnk_walk_relocs_and_mark_ref_sections_task)
|
|||||||
global_batch_list = push_array(scratch.arena, LNK_RelocRefsBatchList, 1);
|
global_batch_list = push_array(scratch.arena, LNK_RelocRefsBatchList, 1);
|
||||||
|
|
||||||
// alloc live flags and set live status on every non-COMDAT section
|
// alloc live flags and set live status on every non-COMDAT section
|
||||||
is_live = push_array_no_zero(scratch.arena, U8 *, objs.count);
|
is_live = push_array_no_zero(scratch.arena, U8 *, objs_count);
|
||||||
{
|
{
|
||||||
U64 obj_idx = 0;
|
for EachIndex(obj_idx, objs_count) {
|
||||||
for EachNode(n, LNK_ObjNode, task->objs.first) {
|
LNK_Obj *obj = objs[obj_idx];
|
||||||
is_live[obj_idx] = push_array(scratch.arena, U8, n->data.header.section_count_no_null + 1);
|
|
||||||
|
|
||||||
for EachIndex(sect_idx, n->data.header.section_count_no_null) {
|
is_live[obj_idx] = push_array(scratch.arena, U8, obj->header.section_count_no_null + 1);
|
||||||
is_live[obj_idx][sect_idx + 1] = !(n->data.section_flags[sect_idx] & COFF_SectionFlag_LnkCOMDAT);
|
|
||||||
|
for EachIndex(sect_idx, obj->header.section_count_no_null) {
|
||||||
|
is_live[obj_idx][sect_idx + 1] = !(obj->section_flags[sect_idx] & COFF_SectionFlag_LnkCOMDAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj_idx += 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2561,8 +2664,9 @@ THREAD_POOL_TASK_FUNC(lnk_walk_relocs_and_mark_ref_sections_task)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// push task for every non-COMDAT section
|
// push task for every non-COMDAT section
|
||||||
for EachNode(obj_n, LNK_ObjNode, objs.first) {
|
for EachIndex(obj_idx, objs_count) {
|
||||||
LNK_Obj *obj = &obj_n->data;
|
LNK_Obj *obj = objs[obj_idx];
|
||||||
|
|
||||||
for EachIndex(sect_idx, obj->header.section_count_no_null) {
|
for EachIndex(sect_idx, obj->header.section_count_no_null) {
|
||||||
U32 section_number = sect_idx+1;
|
U32 section_number = sect_idx+1;
|
||||||
COFF_SectionFlags section_flags = obj->section_flags[sect_idx];
|
COFF_SectionFlags section_flags = obj->section_flags[sect_idx];
|
||||||
@@ -2610,37 +2714,7 @@ THREAD_POOL_TASK_FUNC(lnk_walk_relocs_and_mark_ref_sections_task)
|
|||||||
|
|
||||||
// reloc -> symbol
|
// reloc -> symbol
|
||||||
LNK_ObjSymbolRef ref_symbol = (LNK_ObjSymbolRef){ .obj = batch->v[i].obj, .symbol_idx = reloc->isymbol };
|
LNK_ObjSymbolRef ref_symbol = (LNK_ObjSymbolRef){ .obj = batch->v[i].obj, .symbol_idx = reloc->isymbol };
|
||||||
{
|
lnk_resolve_reloc_target_symbol(scratch2.arena, symtab, ref_symbol, str8_lit("/OPT:REF"), &ref_symbol);
|
||||||
Temp temp = temp_begin(scratch2.arena);
|
|
||||||
HashMap seen_hm = {0};
|
|
||||||
B32 keep_walking = 1;
|
|
||||||
do {
|
|
||||||
// detect cyclic chains
|
|
||||||
U64 symbol_key = ((U64)ref_symbol.obj->input_idx << 32ull) | (U64)ref_symbol.symbol_idx;
|
|
||||||
if (hash_map_search_u64_u64(&seen_hm, symbol_key) == 0) {
|
|
||||||
hash_map_push_u64_u64(temp.arena, &seen_hm, symbol_key, 1);
|
|
||||||
} else {
|
|
||||||
COFF_ParsedSymbol reloc_parsed = lnk_parsed_symbol_from_coff_symbol_idx(batch->v[i].obj, reloc->isymbol);
|
|
||||||
lnk_error_obj(LNK_Warning_CyclicSymbol, batch->v[i].obj, "symbol %S forms a cyclic chain (/OPT:REF)", reloc_parsed.name);
|
|
||||||
MemoryZeroStruct(&ref_symbol);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// unpack symbol
|
|
||||||
COFF_ParsedSymbol ref_parsed = lnk_parsed_symbol_from_coff_symbol_idx(ref_symbol.obj, ref_symbol.symbol_idx);
|
|
||||||
COFF_SymbolValueInterpType ref_interp = coff_interp_from_parsed_symbol(ref_parsed);
|
|
||||||
|
|
||||||
// resolve symbol
|
|
||||||
LNK_ObjSymbolRef next_ref = {0};
|
|
||||||
if (lnk_resolve_symbol(symtab, ref_symbol, &next_ref)) {
|
|
||||||
keep_walking = (ref_interp == COFF_SymbolValueInterp_Weak || ref_interp == COFF_SymbolValueInterp_Undefined);
|
|
||||||
ref_symbol = next_ref;
|
|
||||||
} else {
|
|
||||||
keep_walking = 0;
|
|
||||||
}
|
|
||||||
} while (keep_walking);
|
|
||||||
temp_end(temp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip unresolved symbol
|
// skip unresolved symbol
|
||||||
if (ref_symbol.obj == 0) { continue; }
|
if (ref_symbol.obj == 0) { continue; }
|
||||||
@@ -2740,8 +2814,8 @@ THREAD_POOL_TASK_FUNC(lnk_walk_relocs_and_mark_ref_sections_task)
|
|||||||
if (task_id == 0) {
|
if (task_id == 0) {
|
||||||
ProfBegin("Remove Unreachable Sections");
|
ProfBegin("Remove Unreachable Sections");
|
||||||
|
|
||||||
for EachNode(obj_n, LNK_ObjNode, objs.first) {
|
for EachIndex(obj_idx, objs_count) {
|
||||||
LNK_Obj *obj = &obj_n->data;
|
LNK_Obj *obj = objs[obj_idx];
|
||||||
for EachIndex(sect_idx, obj->header.section_count_no_null) {
|
for EachIndex(sect_idx, obj->header.section_count_no_null) {
|
||||||
U32 section_number = sect_idx+1;
|
U32 section_number = sect_idx+1;
|
||||||
COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, section_number);
|
COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, section_number);
|
||||||
@@ -2756,8 +2830,8 @@ THREAD_POOL_TASK_FUNC(lnk_walk_relocs_and_mark_ref_sections_task)
|
|||||||
enum { Stat_Null, Stat_Code, Stat_Data, Stat_Debug, Stat_Count };
|
enum { Stat_Null, Stat_Code, Stat_Data, Stat_Debug, Stat_Count };
|
||||||
Stat stats[Stat_Count] = {0};
|
Stat stats[Stat_Count] = {0};
|
||||||
|
|
||||||
for EachNode(obj_n, LNK_ObjNode, objs.first) {
|
for EachIndex(obj_idx, objs_count) {
|
||||||
LNK_Obj *obj = &obj_n->data;
|
LNK_Obj *obj = objs[obj_idx];
|
||||||
|
|
||||||
for EachIndex(sect_idx, obj->header.section_count_no_null) {
|
for EachIndex(sect_idx, obj->header.section_count_no_null) {
|
||||||
U32 section_number = sect_idx+1;
|
U32 section_number = sect_idx+1;
|
||||||
@@ -2813,14 +2887,503 @@ THREAD_POOL_TASK_FUNC(lnk_walk_relocs_and_mark_ref_sections_task)
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal void
|
internal void
|
||||||
lnk_opt_ref(TP_Context *tp, LNK_SymbolTable *symtab, LNK_Config *config, LNK_ObjList objs)
|
lnk_opt_ref(TP_Context *tp, LNK_SymbolTable *symtab, LNK_Config *config, LNK_Obj **objs, U64 objs_count)
|
||||||
{
|
{
|
||||||
ProfScope("Mark Live Sections")
|
Temp scratch = scratch_begin(0,0);
|
||||||
tp_for_parallel(tp,
|
U32Array *obj_indices = lnk_obj_indices_from_section_counts(scratch.arena, tp->worker_count, objs, objs_count);
|
||||||
0,
|
|
||||||
tp->worker_count,
|
ProfScope("/OPT:REF")
|
||||||
lnk_walk_relocs_and_mark_ref_sections_task,
|
{
|
||||||
&(LNK_OptRefTask){ .symtab = symtab, .config = config, .objs = objs });
|
LNK_OptTask task = { .symtab = symtab, .config = config, .objs = objs, .objs_count = objs_count, .obj_indices = obj_indices };
|
||||||
|
tp_for_parallel(tp, 0, tp->worker_count, lnk_opt_ref_task, &task);
|
||||||
|
}
|
||||||
|
|
||||||
|
scratch_end(scratch);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef enum LNK_ICF_ColorSpace
|
||||||
|
{
|
||||||
|
LNK_ICF_ColorSpace_Null,
|
||||||
|
LNK_ICF_ColorSpace_Code,
|
||||||
|
LNK_ICF_ColorSpace_Unwind,
|
||||||
|
LNK_ICF_ColorSpace_VFTable,
|
||||||
|
LNK_ICF_ColorSpace_COUNT,
|
||||||
|
} LNK_ICF_ColorSpace;
|
||||||
|
|
||||||
|
internal String8
|
||||||
|
lnk_string_from_icf_color_space(LNK_ICF_ColorSpace color_space)
|
||||||
|
{
|
||||||
|
String8 result = str8_lit("Unknown");
|
||||||
|
switch (color_space) {
|
||||||
|
case LNK_ICF_ColorSpace_Null: { result = str8_lit("Null"); } break;
|
||||||
|
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_COUNT: { result = str8_lit("Unknown"); } break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal LNK_ICF_ColorSpace
|
||||||
|
lnk_icf_color_space_from_section(LNK_Obj *obj, U32 sect_idx)
|
||||||
|
{
|
||||||
|
LNK_ICF_ColorSpace result = LNK_ICF_ColorSpace_Null;
|
||||||
|
|
||||||
|
// fold read-only COMDAT sections
|
||||||
|
COFF_SectionFlags expected_flags = COFF_SectionFlag_LnkCOMDAT | COFF_SectionFlag_MemRead;
|
||||||
|
COFF_SectionFlags exclude_flags = COFF_SectionFlag_LnkRemove | COFF_SectionFlag_MemWrite | LNK_SECTION_FLAG_NOICF;
|
||||||
|
|
||||||
|
if ((obj->section_flags[sect_idx] & expected_flags) == expected_flags && (obj->section_flags[sect_idx] & exclude_flags) == 0) {
|
||||||
|
// fold code
|
||||||
|
if (obj->section_flags[sect_idx] & COFF_SectionFlag_CntCode) {
|
||||||
|
result = LNK_ICF_ColorSpace_Code;
|
||||||
|
}
|
||||||
|
// fold data
|
||||||
|
else if (obj->section_flags[sect_idx] & COFF_SectionFlag_CntInitializedData) {
|
||||||
|
COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, sect_idx + 1);
|
||||||
|
String8 section_name = str8_cstring_capped(section_header->name, section_header->name + sizeof(section_header->name));
|
||||||
|
|
||||||
|
// fold unwind info
|
||||||
|
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)) {
|
||||||
|
COFF_ParsedSymbol symlink_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symlink_ref.obj, symlink_ref.symbol_idx);
|
||||||
|
if (str8_starts_with(symlink_symbol.name, str8_lit("??_7"))) {
|
||||||
|
result = LNK_ICF_ColorSpace_VFTable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal
|
||||||
|
THREAD_POOL_TASK_FUNC(lnk_opt_icf_task)
|
||||||
|
{
|
||||||
|
ProfBeginFunction();
|
||||||
|
Temp scratch = scratch_begin(&arena,1);
|
||||||
|
|
||||||
|
//
|
||||||
|
// OPT uses a color-refinement algorithm for folding duplicate sections.
|
||||||
|
// If a color group contains multiple distinct hashes, the group is split
|
||||||
|
// and a new refinement pass is run. By default, the algorithm loops until
|
||||||
|
// partitions stabilize. Equivalence is established by comparing cryptographic
|
||||||
|
// 128-bit hashes; in theory, the chance of collisions are near the birthday
|
||||||
|
// paradox with BLAKE3 and the other downside it is susceptible to adversarial
|
||||||
|
// inputs.
|
||||||
|
//
|
||||||
|
|
||||||
|
LNK_OptTask *task = raw_task;
|
||||||
|
LNK_Obj **objs = task->objs;
|
||||||
|
|
||||||
|
if (task_id == 0) {
|
||||||
|
lnk_log(LNK_Log_Debug, "/OPT:ICF:");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task->config->llvm_addrsig == LNK_SwitchState_Yes) {
|
||||||
|
// .llvm_addrsig is an array of ULEB128 symbol indices, which mark sections
|
||||||
|
// whose addresses are significant
|
||||||
|
for EachIndex(i, task->obj_indices[task_id].count) {
|
||||||
|
U64 obj_idx = task->obj_indices[task_id].v[i];
|
||||||
|
LNK_Obj *obj = task->objs[obj_idx];
|
||||||
|
|
||||||
|
if (obj->llvm_addrsig_sect_idx >= obj->header.section_count_no_null) { continue; }
|
||||||
|
|
||||||
|
String8 symbol_table = lnk_coff_symbol_table_from_obj(obj);
|
||||||
|
String8 string_table = lnk_coff_string_table_from_obj(obj);
|
||||||
|
COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, obj->llvm_addrsig_sect_idx + 1);
|
||||||
|
String8 section_data = str8_substr(obj->data, r1u64s(section_header->foff, section_header->fsize));
|
||||||
|
|
||||||
|
// parse symbol indices and mark selected sections with NOICF flag
|
||||||
|
for (U64 off = 0; off < section_data.size;) {
|
||||||
|
U64 symbol_off = off;
|
||||||
|
U64 symbol_idx = 0;
|
||||||
|
off += str8_deserial_read_uleb128(section_data, off, &symbol_idx);
|
||||||
|
if (symbol_off == off) { break; }
|
||||||
|
|
||||||
|
if (symbol_idx < obj->header.symbol_count) {
|
||||||
|
LNK_ObjSymbolRef target_ref = { .obj = obj, .symbol_idx = symbol_idx };
|
||||||
|
B32 is_symbol_found = lnk_resolve_reloc_target_symbol(scratch.arena, task->symtab, target_ref, str8_lit("/OPT:ICF"), &target_ref);
|
||||||
|
if (is_symbol_found) {
|
||||||
|
COFF_ParsedSymbol symbol = coff_parse_symbol(target_ref.obj->header, string_table, symbol_table, target_ref.symbol_idx);
|
||||||
|
if (coff_interp_from_parsed_symbol(symbol) == COFF_SymbolValueInterp_Regular) {
|
||||||
|
obj->section_flags[symbol.section_number - 1] |= LNK_SECTION_FLAG_NOICF;
|
||||||
|
} else {
|
||||||
|
lnk_error_obj(LNK_Error_IllData, obj, ".llvm_addrsig: skip symbol 0x%x at offset 0x%x; symbol index must address a section-based symbol\n", symbol_idx, symbol_off);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lnk_error_obj(LNK_Error_IllData, obj, ".llvm_addrsig: contains out of bounds symbol index 0x%x at offset 0x%x\n", symbol_idx, symbol_off);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
barrier_wait(tp->barrier);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// step 1: fill out color map and contributions
|
||||||
|
//
|
||||||
|
|
||||||
|
// alloc total section counter
|
||||||
|
U64 *contrib_counts = 0;
|
||||||
|
if (task_id == 0) {
|
||||||
|
contrib_counts = push_array(scratch.arena, U64, task->objs_count);
|
||||||
|
}
|
||||||
|
tp_broadcast(&contrib_counts);
|
||||||
|
|
||||||
|
// count contributions
|
||||||
|
for EachIndex(i, task->obj_indices[task_id].count) {
|
||||||
|
U64 obj_idx = task->obj_indices[task_id].v[i];
|
||||||
|
LNK_Obj *obj = objs[obj_idx];
|
||||||
|
for EachIndex(sect_idx, obj->header.section_count_no_null) {
|
||||||
|
if (lnk_icf_color_space_from_section(obj, sect_idx)) {
|
||||||
|
contrib_counts[obj_idx] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
barrier_wait(tp->barrier);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct {
|
||||||
|
U128 hash;
|
||||||
|
U64 old_color;
|
||||||
|
} key;
|
||||||
|
U32 obj_idx;
|
||||||
|
U32 sect_idx;
|
||||||
|
} Contrib;
|
||||||
|
|
||||||
|
U64 contrib_count = sum_array_u64(task->objs_count, contrib_counts);
|
||||||
|
U64 *contrib_offsets = 0;
|
||||||
|
U64 **color_map = 0;
|
||||||
|
Contrib *contribs = 0;
|
||||||
|
U32 *is_part_stable = 0;
|
||||||
|
U64 *next_color = 0;
|
||||||
|
if (task_id == 0) {
|
||||||
|
contrib_offsets = offsets_from_counts_array_u64(scratch.arena, contrib_counts, task->objs_count);
|
||||||
|
color_map = push_array(scratch.arena, U64 *, task->objs_count);
|
||||||
|
for EachIndex(obj_idx, task->objs_count) {
|
||||||
|
LNK_Obj *obj = objs[obj_idx];
|
||||||
|
color_map[obj_idx] = push_array(scratch.arena, U64, obj->header.section_count_no_null);
|
||||||
|
}
|
||||||
|
contribs = push_array(scratch.arena, Contrib, contrib_count);
|
||||||
|
is_part_stable = push_array(scratch.arena, U32, 1);
|
||||||
|
next_color = push_array(scratch.arena, U64, 1);
|
||||||
|
*next_color = LNK_ICF_ColorSpace_COUNT;
|
||||||
|
|
||||||
|
lnk_log(LNK_Log_Debug, " Contrib count: %S", str8_from_count(scratch.arena, contrib_count));
|
||||||
|
}
|
||||||
|
tp_broadcast(&contrib_offsets);
|
||||||
|
tp_broadcast(&color_map);
|
||||||
|
tp_broadcast(&contribs);
|
||||||
|
tp_broadcast(&is_part_stable);
|
||||||
|
tp_broadcast(&next_color);
|
||||||
|
|
||||||
|
for EachIndex(i, task->obj_indices[task_id].count) {
|
||||||
|
U64 obj_idx = task->obj_indices[task_id].v[i];
|
||||||
|
LNK_Obj *obj = objs[obj_idx];
|
||||||
|
U64 cursor = 0;
|
||||||
|
for EachIndex(sect_idx, obj->header.section_count_no_null) {
|
||||||
|
LNK_ICF_ColorSpace color_space = lnk_icf_color_space_from_section(obj, sect_idx);
|
||||||
|
if (color_space == LNK_ICF_ColorSpace_Null) {
|
||||||
|
// assign unique color to sections that are not foldable
|
||||||
|
color_map[obj_idx][sect_idx] = ins_atomic_u64_inc_eval(next_color);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// compute contribution index
|
||||||
|
U64 contrib_idx = contrib_offsets[obj_idx] + cursor++;
|
||||||
|
|
||||||
|
// seed foldable sections with a content-derived color
|
||||||
|
COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(obj, sect_idx + 1);
|
||||||
|
String8 section_data = str8_substr(obj->data, r1u64s(section_header->foff, section_header->fsize));
|
||||||
|
XXH3_state_t hasher;
|
||||||
|
XXH3_64bits_reset(&hasher);
|
||||||
|
XXH3_64bits_update(&hasher, &color_space, sizeof(color_space));
|
||||||
|
XXH3_64bits_update(&hasher, section_data.str, section_data.size);
|
||||||
|
|
||||||
|
// fill out contribution
|
||||||
|
contribs[contrib_idx] = (Contrib){
|
||||||
|
.obj_idx = safe_cast_u32(obj->input_idx),
|
||||||
|
.sect_idx = safe_cast_u32(sect_idx),
|
||||||
|
};
|
||||||
|
|
||||||
|
// assign content-derived starting color value
|
||||||
|
color_map[obj_idx][sect_idx] = XXH3_64bits_digest(&hasher) | (1ull << 63);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
barrier_wait(tp->barrier);
|
||||||
|
|
||||||
|
//
|
||||||
|
// step 2: refine equivalence classes
|
||||||
|
//
|
||||||
|
|
||||||
|
U64 iter_count = 0;
|
||||||
|
for (;; iter_count += 1) {
|
||||||
|
barrier_wait(tp->barrier);
|
||||||
|
// reset color status tracker
|
||||||
|
if (task_id == 0) {
|
||||||
|
*is_part_stable = 1;
|
||||||
|
}
|
||||||
|
barrier_wait(tp->barrier);
|
||||||
|
|
||||||
|
// compute colored hashes
|
||||||
|
for EachIndex(i, task->obj_indices[task_id].count) {
|
||||||
|
U64 obj_idx = task->obj_indices[task_id].v[i];
|
||||||
|
LNK_Obj *obj = objs[obj_idx];
|
||||||
|
COFF_SectionHeader *section_table = lnk_coff_section_table_from_obj(obj);
|
||||||
|
U64 contrib_cursor = 0;
|
||||||
|
for EachIndex(sect_idx, obj->header.section_count_no_null) {
|
||||||
|
LNK_ICF_ColorSpace color_space = lnk_icf_color_space_from_section(obj, sect_idx);
|
||||||
|
if (color_space == LNK_ICF_ColorSpace_Null) { continue; }
|
||||||
|
|
||||||
|
// parse relocations
|
||||||
|
COFF_SectionHeader *section_header = section_table + sect_idx;
|
||||||
|
COFF_RelocArray relocs = lnk_coff_relocs_from_section_header(obj, section_header);
|
||||||
|
|
||||||
|
U64 contrib_idx = contrib_offsets[obj_idx] + contrib_cursor;
|
||||||
|
contrib_cursor += 1;
|
||||||
|
contribs[contrib_idx].key.old_color = color_map[obj_idx][sect_idx];
|
||||||
|
|
||||||
|
// parse section data
|
||||||
|
String8 section_data = str8_substr(obj->data, r1u64s(section_header->foff, section_header->fsize));
|
||||||
|
|
||||||
|
blake3_hasher hasher; blake3_hasher_init(&hasher);
|
||||||
|
|
||||||
|
// mix section non-recursive properties
|
||||||
|
blake3_hasher_update(&hasher, section_data.str, section_data.size);
|
||||||
|
blake3_hasher_update(&hasher, &relocs.count, sizeof(relocs.count));
|
||||||
|
blake3_hasher_update(&hasher, &color_space, sizeof(color_space));
|
||||||
|
|
||||||
|
for EachIndex(reloc_idx, relocs.count) {
|
||||||
|
COFF_Reloc *r = &relocs.v[reloc_idx];
|
||||||
|
|
||||||
|
// resolve symbol referenced by the relocation
|
||||||
|
LNK_ObjSymbolRef target_ref = { .obj = obj, .symbol_idx = r->isymbol };
|
||||||
|
B32 is_symbol_found = lnk_resolve_reloc_target_symbol(scratch.arena, task->symtab, target_ref, str8_lit("/OPT:ICF"), &target_ref);
|
||||||
|
|
||||||
|
COFF_SymbolValueInterpType target_interp;
|
||||||
|
U64 target_id;
|
||||||
|
U32 target_value;
|
||||||
|
|
||||||
|
if (is_symbol_found) {
|
||||||
|
// parse COFF symbol and interpret the target symbol kind
|
||||||
|
COFF_ParsedSymbol target_symbol = lnk_parsed_symbol_from_coff_symbol_idx(target_ref.obj, target_ref.symbol_idx);
|
||||||
|
target_interp = coff_interp_from_parsed_symbol(target_symbol);
|
||||||
|
|
||||||
|
switch (target_interp) {
|
||||||
|
case COFF_SymbolValueInterp_Regular: {
|
||||||
|
// color relocation with the referenced section
|
||||||
|
target_id = color_map[target_ref.obj->input_idx][target_symbol.section_number - 1];
|
||||||
|
target_value = target_symbol.value;
|
||||||
|
} break;
|
||||||
|
default: {
|
||||||
|
// color relocation with unique symbol ref
|
||||||
|
target_id = Compose64Bit(target_ref.obj->input_idx, target_ref.symbol_idx);
|
||||||
|
target_value = target_symbol.value;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// color relocation with unique symbol ref
|
||||||
|
target_interp = max_U32;
|
||||||
|
target_id = Compose64Bit(obj_idx, r->isymbol);
|
||||||
|
target_value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mix relocation properties
|
||||||
|
blake3_hasher_update(&hasher, &r->apply_off, sizeof(r->apply_off));
|
||||||
|
blake3_hasher_update(&hasher, &r->type, sizeof(r->type));
|
||||||
|
blake3_hasher_update(&hasher, &target_interp, sizeof(target_interp));
|
||||||
|
blake3_hasher_update(&hasher, &target_id, sizeof(target_id));
|
||||||
|
blake3_hasher_update(&hasher, &target_value, sizeof(target_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// finalize section hash
|
||||||
|
U128 hash;
|
||||||
|
blake3_hasher_finalize(&hasher, (U8*)&hash, sizeof(hash));
|
||||||
|
|
||||||
|
// update contribution hash and color
|
||||||
|
contribs[contrib_idx].key.hash = hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
barrier_wait(tp->barrier);
|
||||||
|
|
||||||
|
// group sections by (color, hash) and handle color splits
|
||||||
|
if (task_id == 0) {
|
||||||
|
Temp temp = temp_begin(scratch.arena);
|
||||||
|
|
||||||
|
HashMap new_color_hm = {0};
|
||||||
|
HashMap old_color_hm = {0};
|
||||||
|
U64 start_next_color = *next_color;
|
||||||
|
|
||||||
|
for EachIndex(contrib_idx, contrib_count) {
|
||||||
|
Contrib *contrib = &contribs[contrib_idx];
|
||||||
|
U64 *new_color_ptr = hash_map_search_string_u64(&new_color_hm, str8_struct(&contrib->key));
|
||||||
|
if (new_color_ptr == 0) {
|
||||||
|
U64 color;
|
||||||
|
if (hash_map_search_u64_u64(&old_color_hm, contrib->key.old_color) == 0) {
|
||||||
|
color = contrib->key.old_color;
|
||||||
|
hash_map_push_u64_u64(temp.arena, &old_color_hm, contrib->key.old_color, color);
|
||||||
|
} else {
|
||||||
|
// generate a new color for the hash
|
||||||
|
color = ++*next_color;
|
||||||
|
*is_part_stable = 0;
|
||||||
|
}
|
||||||
|
new_color_ptr = &hash_map_push_string_u64(temp.arena, &new_color_hm, str8_struct(&contrib->key), color)->v.value.value_u64;
|
||||||
|
}
|
||||||
|
color_map[contrib->obj_idx][contrib->sect_idx] = *new_color_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
U64 split_count = *next_color - start_next_color;
|
||||||
|
lnk_log(LNK_Log_Debug, " Pass %llu found %S splits", iter_count, str8_from_count(scratch.arena, split_count));
|
||||||
|
|
||||||
|
temp_end(temp);
|
||||||
|
}
|
||||||
|
barrier_wait(tp->barrier);
|
||||||
|
|
||||||
|
// stop iterating when partitions stabilize
|
||||||
|
if (*is_part_stable) { break; }
|
||||||
|
}
|
||||||
|
barrier_wait(tp->barrier);
|
||||||
|
|
||||||
|
//
|
||||||
|
// step 3: flag folded sections for removal
|
||||||
|
//
|
||||||
|
|
||||||
|
if (task_id == 0) {
|
||||||
|
HashMap leader_hm = { 0 };
|
||||||
|
for EachIndex(contrib_idx, contrib_count) {
|
||||||
|
Contrib *contrib = &contribs[contrib_idx];
|
||||||
|
U64 color = color_map[contrib->obj_idx][contrib->sect_idx];
|
||||||
|
if (hash_map_search_u64_raw(&leader_hm, color) == 0) {
|
||||||
|
hash_map_push_u64_raw(scratch.arena, &leader_hm, color, contrib);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct { U64 count; U64 size; } FoldStats;
|
||||||
|
FoldStats fold_stats[LNK_ICF_ColorSpace_COUNT] = {0};
|
||||||
|
|
||||||
|
for EachIndex(contrib_idx, contrib_count) {
|
||||||
|
Contrib *contrib = &contribs[contrib_idx];
|
||||||
|
Contrib *leader = hash_map_search_u64_raw(&leader_hm, color_map[contrib->obj_idx][contrib->sect_idx]);
|
||||||
|
if (leader == 0 || leader == contrib) { continue; }
|
||||||
|
|
||||||
|
LNK_Obj *contrib_obj = objs[contrib->obj_idx];
|
||||||
|
LNK_Obj *leader_obj = objs[leader->obj_idx];
|
||||||
|
|
||||||
|
// update fold stats
|
||||||
|
if (lnk_get_log_status(LNK_Log_Debug)) {
|
||||||
|
COFF_SectionHeader *section_header = lnk_coff_section_header_from_section_number(contrib_obj, contrib->sect_idx + 1);
|
||||||
|
LNK_ICF_ColorSpace color_space = lnk_icf_color_space_from_section(contrib_obj, contrib->sect_idx);
|
||||||
|
fold_stats[color_space].count += 1;
|
||||||
|
fold_stats[color_space].size += section_header->fsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update leader with largest alignment
|
||||||
|
U64 leader_align = coff_align_size_from_section_flags(leader_obj->section_flags[leader->sect_idx]);
|
||||||
|
U64 contrib_align = coff_align_size_from_section_flags(contrib_obj->section_flags[contrib->sect_idx]);
|
||||||
|
if (leader_align < contrib_align) {
|
||||||
|
leader_obj->section_flags[leader->sect_idx] &= ~(COFF_SectionFlag_AlignMask << COFF_SectionFlag_AlignShift);
|
||||||
|
leader_obj->section_flags[leader->sect_idx] |= coff_section_flag_from_align_size(contrib_align);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update COMDAT leader symbol link
|
||||||
|
Assert(leader_obj->comdats[leader->sect_idx] != max_U32);
|
||||||
|
LNK_ObjSymbolRef leader_symlink = { leader_obj, leader_obj->comdats[leader->sect_idx] };
|
||||||
|
contrib_obj->symlinks[contrib->sect_idx + 1] = leader_symlink;
|
||||||
|
|
||||||
|
// discard folded section
|
||||||
|
contrib_obj->section_flags[contrib->sect_idx] |= COFF_SectionFlag_LnkRemove;
|
||||||
|
|
||||||
|
#if LNK_PARANOID
|
||||||
|
String8 section_name = lnk_obj_section_name_from_section_number(contrib_obj, contrib->sect_idx+1);
|
||||||
|
String8 leader_name = lnk_obj_section_name_from_section_number(leader_obj, leader->sect_idx+1);
|
||||||
|
lnk_log(LNK_Log_Debug, "fold %.*s[SECT%X \"%.*s\"] ==> %.*s[SECT%X \"%.*s\"]", str8_varg(lnk_loc_from_obj(scratch.arena, contrib_obj)), contrib->sect_idx+1, str8_varg(section_name), str8_varg(lnk_loc_from_obj(scratch.arena, leader_obj)), leader->sect_idx+1, str8_varg(leader_name));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lnk_get_log_status(LNK_Log_Debug)) {
|
||||||
|
U64 total_count = 0, total_size = 0;
|
||||||
|
for (LNK_ICF_ColorSpace color_space = (LNK_ICF_ColorSpace)(LNK_ICF_ColorSpace_Null + 1); color_space < LNK_ICF_ColorSpace_COUNT; color_space += 1) {
|
||||||
|
lnk_log(LNK_Log_Debug, " %-8S: %M, %.*s sections", lnk_string_from_icf_color_space(color_space), fold_stats[color_space].size, str8_varg(str8_from_count(scratch.arena, fold_stats[color_space].count)));
|
||||||
|
total_count += fold_stats[color_space].count;
|
||||||
|
total_size += fold_stats[color_space].size;
|
||||||
|
}
|
||||||
|
lnk_log(LNK_Log_Debug, " %-8s: %M, %.*s sections", "Total", total_size, str8_varg(str8_from_count(scratch.arena, total_count)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
barrier_wait(tp->barrier);
|
||||||
|
|
||||||
|
//
|
||||||
|
// step 4: flatten COMDAT symlink chains so subsequent passes can assume symlinks are single hop
|
||||||
|
//
|
||||||
|
|
||||||
|
for EachIndex(i, task->obj_indices[task_id].count) {
|
||||||
|
U64 obj_idx = task->obj_indices[task_id].v[i];
|
||||||
|
LNK_Obj *obj = objs[obj_idx];
|
||||||
|
for EachIndex(sect_idx, obj->header.section_count_no_null) {
|
||||||
|
U64 section_number = sect_idx + 1;
|
||||||
|
|
||||||
|
LNK_ObjSymbolRef symlink_ref = {0};
|
||||||
|
if (!lnk_obj_get_comdat_symlink(obj, section_number, &symlink_ref)) { continue; }
|
||||||
|
|
||||||
|
{
|
||||||
|
Temp temp = temp_begin(scratch.arena);
|
||||||
|
|
||||||
|
HashMap seen_hm = {0};
|
||||||
|
U64 hop_count = 0;
|
||||||
|
U64 hop_cap = 1024;
|
||||||
|
for(; hop_count < hop_cap; hop_count += 1) {
|
||||||
|
COFF_ParsedSymbol symlink_parsed = lnk_parsed_symbol_from_coff_symbol_idx(symlink_ref.obj, symlink_ref.symbol_idx);
|
||||||
|
|
||||||
|
LNK_ObjSymbolRef next_symlink_ref = {0};
|
||||||
|
if (!lnk_obj_get_comdat_symlink(symlink_ref.obj, symlink_parsed.section_number, &next_symlink_ref)) { break; }
|
||||||
|
|
||||||
|
if (MemoryMatchStruct(&next_symlink_ref, &symlink_ref)) { break; }
|
||||||
|
|
||||||
|
if (hash_map_search_string_u64(&seen_hm, str8_struct(&next_symlink_ref)) != 0) {
|
||||||
|
lnk_error_obj(LNK_Error_IllData, obj, "recursive COMDAT symlink in SECT%X", section_number);
|
||||||
|
MemoryZeroStruct(&symlink_ref);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
symlink_ref = next_symlink_ref;
|
||||||
|
hash_map_push_string_u64(temp.arena, &seen_hm, str8_copy(temp.arena, str8_struct(&symlink_ref)), 1);
|
||||||
|
}
|
||||||
|
if (hop_count >= hop_cap) {
|
||||||
|
lnk_error_obj(LNK_Error_IllData, obj, "failed to flatten symlink for SECT%X; max number of hops reached", section_number);
|
||||||
|
MemoryZeroStruct(&symlink_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_end(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
obj->symlinks[section_number] = symlink_ref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
barrier_wait(tp->barrier);
|
||||||
|
|
||||||
|
scratch_end(scratch);
|
||||||
|
ProfEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
lnk_opt_icf(TP_Context *tp, LNK_SymbolTable *symtab, LNK_Config *config, LNK_Obj **objs, U64 objs_count)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(0,0);
|
||||||
|
U32Array *obj_indices = lnk_obj_indices_from_section_counts(scratch.arena, tp->worker_count, objs, objs_count);
|
||||||
|
|
||||||
|
ProfScope("/OPT:ICF")
|
||||||
|
{
|
||||||
|
LNK_OptTask task = { .symtab = symtab, .config = config, .objs = objs, .objs_count = objs_count, .obj_indices = obj_indices };
|
||||||
|
tp_for_parallel(tp, 0, tp->worker_count, lnk_opt_icf_task, &task);
|
||||||
|
}
|
||||||
|
|
||||||
|
scratch_end(scratch);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal
|
internal
|
||||||
@@ -2935,11 +3498,10 @@ THREAD_POOL_TASK_FUNC(lnk_set_comdat_leaders_contribs_task)
|
|||||||
|
|
||||||
if (~obj->section_flags[sect_idx] & COFF_SectionFlag_LnkCOMDAT) { continue; }
|
if (~obj->section_flags[sect_idx] & COFF_SectionFlag_LnkCOMDAT) { continue; }
|
||||||
|
|
||||||
LNK_Symbol *symlink = lnk_obj_get_comdat_symlink(obj, section_number);
|
LNK_ObjSymbolRef symlink_ref = {0};
|
||||||
if (symlink == 0) { continue; }
|
if ( ! lnk_obj_get_comdat_symlink(obj, section_number, &symlink_ref)) { continue; }
|
||||||
|
|
||||||
COFF_ParsedSymbol symlink_parsed = lnk_parsed_from_symbol(symlink);
|
COFF_ParsedSymbol symlink_parsed = lnk_parsed_symbol_from_coff_symbol_idx(symlink_ref.obj, symlink_ref.symbol_idx);
|
||||||
LNK_ObjSymbolRef symlink_ref = lnk_ref_from_symbol(symlink);
|
|
||||||
task->sect_map[obj_idx][sect_idx] = task->sect_map[symlink_ref.obj->input_idx][symlink_parsed.section_number - 1];
|
task->sect_map[obj_idx][sect_idx] = task->sect_map[symlink_ref.obj->input_idx][symlink_parsed.section_number - 1];
|
||||||
}
|
}
|
||||||
ProfEnd();
|
ProfEnd();
|
||||||
@@ -2981,24 +3543,21 @@ THREAD_POOL_TASK_FUNC(lnk_patch_comdat_leaders_task)
|
|||||||
symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx);
|
symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx);
|
||||||
|
|
||||||
COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class);
|
COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class);
|
||||||
if (interp == COFF_SymbolValueInterp_Regular) {
|
if (interp != COFF_SymbolValueInterp_Regular) { continue; }
|
||||||
LNK_Symbol *symlink = lnk_obj_get_comdat_symlink(obj, symbol.section_number);
|
|
||||||
if (symlink) {
|
LNK_ObjSymbolRef symlink_ref = {0};
|
||||||
LNK_ObjSymbolRef symlink_ref = lnk_ref_from_symbol(symlink);
|
if ( ! lnk_obj_get_comdat_symlink(obj, symbol.section_number, &symlink_ref)) { continue; }
|
||||||
if (symlink_ref.obj != obj) {
|
|
||||||
U32 section_number;
|
COFF_ParsedSymbol parsed_symlink = lnk_parsed_symbol_from_coff_symbol_idx(symlink_ref.obj, symlink_ref.symbol_idx);
|
||||||
U32 value;
|
if (symlink_ref.obj == obj && parsed_symlink.section_number == symbol.section_number) { continue; }
|
||||||
if (symbol.storage_class == COFF_SymStorageClass_External) {
|
|
||||||
|
B32 is_static_comdat_leader = symbol.storage_class == COFF_SymStorageClass_Static &&
|
||||||
|
obj->comdats[symbol.section_number-1] == symbol_idx;
|
||||||
|
|
||||||
|
if (symbol.storage_class == COFF_SymStorageClass_External || is_static_comdat_leader) {
|
||||||
// COMDAT leader may be at a different offset, so update this symbol with leader's offset
|
// COMDAT leader may be at a different offset, so update this symbol with leader's offset
|
||||||
COFF_ParsedSymbol parsed_symlink = lnk_parsed_from_symbol(symlink);
|
U32 section_number = symbol.section_number;
|
||||||
section_number = symbol.section_number;
|
U32 value = parsed_symlink.value;
|
||||||
value = parsed_symlink.value;
|
|
||||||
} else {
|
|
||||||
// COMDAT section may have static symbols which are now invalid to relocate against
|
|
||||||
section_number = lnk_obj_get_removed_section_number(obj);
|
|
||||||
value = max_U32;
|
|
||||||
task->u.patch_symtabs.was_symbol_patched[obj_idx][symbol_idx] = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj->header.is_big_obj) {
|
if (obj->header.is_big_obj) {
|
||||||
COFF_Symbol32 *symbol32 = symbol.raw_symbol;
|
COFF_Symbol32 *symbol32 = symbol.raw_symbol;
|
||||||
@@ -3011,8 +3570,6 @@ THREAD_POOL_TASK_FUNC(lnk_patch_comdat_leaders_task)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
ProfEnd();
|
ProfEnd();
|
||||||
|
|
||||||
ProfEnd();
|
ProfEnd();
|
||||||
|
|||||||
+7
-3
@@ -74,6 +74,7 @@ typedef struct LNK_Inputer
|
|||||||
#define LNK_NULL_SYMBOL "*** RAD_NULL_SYMBOL ***"
|
#define LNK_NULL_SYMBOL "*** RAD_NULL_SYMBOL ***"
|
||||||
|
|
||||||
#define LNK_SECTION_FLAG_DEBUG (1 << 0)
|
#define LNK_SECTION_FLAG_DEBUG (1 << 0)
|
||||||
|
#define LNK_SECTION_FLAG_NOICF (1 << 1)
|
||||||
|
|
||||||
typedef U8 LNK_LibMemberFlags;
|
typedef U8 LNK_LibMemberFlags;
|
||||||
enum
|
enum
|
||||||
@@ -217,8 +218,10 @@ typedef struct
|
|||||||
{
|
{
|
||||||
LNK_SymbolTable *symtab;
|
LNK_SymbolTable *symtab;
|
||||||
LNK_Config *config;
|
LNK_Config *config;
|
||||||
LNK_ObjList objs;
|
LNK_Obj **objs;
|
||||||
} LNK_OptRefTask;
|
U64 objs_count;
|
||||||
|
U32Array *obj_indices;
|
||||||
|
} LNK_OptTask;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@@ -390,7 +393,8 @@ internal LNK_LinkResult lnk_link_image (TP_Context *tp, TP_Arena *arena, LNK_Con
|
|||||||
|
|
||||||
// --- Optimizations -----------------------------------------------------------
|
// --- Optimizations -----------------------------------------------------------
|
||||||
|
|
||||||
internal void lnk_opt_ref(TP_Context *tp, LNK_SymbolTable *symtab, LNK_Config *config, LNK_ObjList objs);
|
internal void lnk_opt_ref(TP_Context *tp, LNK_SymbolTable *symtab, LNK_Config *config, LNK_Obj **objs, U64 objs_count);
|
||||||
|
internal void lnk_opt_icf(TP_Context *tp, LNK_SymbolTable *symtab, LNK_Config *config, LNK_Obj **objs, U64 objs_count);
|
||||||
|
|
||||||
// --- Win32 Image -------------------------------------------------------------
|
// --- Win32 Image -------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ global read_only LNK_CmdSwitch g_cmd_switch_map[] =
|
|||||||
{ LNK_CmdSwitch_FailIfMismatch, 1, "FAILIFMISMATCH", "{id=value}", "Fails to link if same ids have conflicting values." },
|
{ LNK_CmdSwitch_FailIfMismatch, 1, "FAILIFMISMATCH", "{id=value}", "Fails to link if same ids have conflicting values." },
|
||||||
{ LNK_CmdSwitch_FileAlign, 0, "FILEALIGN", ":#", "Set section alignment in the file." },
|
{ LNK_CmdSwitch_FileAlign, 0, "FILEALIGN", ":#", "Set section alignment in the file." },
|
||||||
{ LNK_CmdSwitch_Fixed, 0, "FIXED", "[:NO]", "Load the image at the default base address." },
|
{ LNK_CmdSwitch_Fixed, 0, "FIXED", "[:NO]", "Load the image at the default base address." },
|
||||||
|
{ LNK_CmdSwitch_Force, 0, "FORCE", "", "Force image output despite errors." },
|
||||||
{ LNK_CmdSwitch_FunctionPadMin, 0, "FUNCTIONPADMIN", ":#", "Minimum function byte size." },
|
{ LNK_CmdSwitch_FunctionPadMin, 0, "FUNCTIONPADMIN", ":#", "Minimum function byte size." },
|
||||||
{ LNK_CmdSwitch_Heap, 0, "HEAP", "RESERVE[,COMMIT]", "Set reserve and commit size for the heap." },
|
{ LNK_CmdSwitch_Heap, 0, "HEAP", "RESERVE[,COMMIT]", "Set reserve and commit size for the heap." },
|
||||||
{ LNK_CmdSwitch_HighEntropyVa, 0, "HIGHENTROPYVA", "[:NO]", "Indicate that image supports full 64-bit address space ASLR." },
|
{ LNK_CmdSwitch_HighEntropyVa, 0, "HIGHENTROPYVA", "[:NO]", "Indicate that image supports full 64-bit address space ASLR." },
|
||||||
@@ -106,6 +107,8 @@ global read_only LNK_CmdSwitch g_cmd_switch_map[] =
|
|||||||
|
|
||||||
{ LNK_CmdSwitch_RadTypeServer, 0, "RAD_TYPE_SERVER", ":FILENAME", "Merge types and store them in the specified file. The filename must have the .rrt extension." },
|
{ LNK_CmdSwitch_RadTypeServer, 0, "RAD_TYPE_SERVER", ":FILENAME", "Merge types and store them in the specified file. The filename must have the .rrt extension." },
|
||||||
|
|
||||||
|
{ LNK_CmdSwitch_LLVM_AddrSig, 0, "LLVM_ADDRSIG", "[:NO]", "Use .llvm_addrsig to guide ICF." },
|
||||||
|
|
||||||
{ LNK_CmdSwitch_Help, 0, "HELP", "", "" },
|
{ LNK_CmdSwitch_Help, 0, "HELP", "", "" },
|
||||||
{ LNK_CmdSwitch_Help, 0, "?", "", "" },
|
{ LNK_CmdSwitch_Help, 0, "?", "", "" },
|
||||||
};
|
};
|
||||||
@@ -2179,6 +2182,10 @@ lnk_apply_cmd_option_to_config(LNK_Config *config, String8 cmd_name, String8List
|
|||||||
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "missing type server file path");
|
lnk_error_cmd_switch(LNK_Error_Cmdl, obj, cmd_switch, "missing type server file path");
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case LNK_CmdSwitch_LLVM_AddrSig: {
|
||||||
|
lnk_cmd_switch_parse_flag(obj, cmd_switch, value_strings, &config->llvm_addrsig);
|
||||||
|
} break;
|
||||||
}
|
}
|
||||||
|
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
@@ -2208,6 +2215,7 @@ lnk_config_init(LNK_CmdLine cmd_line)
|
|||||||
config->arena = arena;
|
config->arena = arena;
|
||||||
config->raw_cmd_line = str8_list_copy(arena, &cmd_line.raw_cmd_line);
|
config->raw_cmd_line = str8_list_copy(arena, &cmd_line.raw_cmd_line);
|
||||||
config->work_dir = get_current_path(arena);
|
config->work_dir = get_current_path(arena);
|
||||||
|
config->force = lnk_cmd_line_has_switch(cmd_line, LNK_CmdSwitch_Force);
|
||||||
|
|
||||||
// apply command line switches
|
// apply command line switches
|
||||||
for EachNode(cmd, LNK_CmdOption, cmd_line.first_option) {
|
for EachNode(cmd, LNK_CmdOption, cmd_line.first_option) {
|
||||||
|
|||||||
@@ -143,6 +143,8 @@ typedef enum
|
|||||||
LNK_CmdSwitch_RadTypeServer,
|
LNK_CmdSwitch_RadTypeServer,
|
||||||
LNK_CmdSwitch_RadTypeServer_MatchObj,
|
LNK_CmdSwitch_RadTypeServer_MatchObj,
|
||||||
|
|
||||||
|
LNK_CmdSwitch_LLVM_AddrSig,
|
||||||
|
|
||||||
LNK_CmdSwitch_Help,
|
LNK_CmdSwitch_Help,
|
||||||
|
|
||||||
LNK_CmdSwitch_Count
|
LNK_CmdSwitch_Count
|
||||||
@@ -316,6 +318,7 @@ typedef struct LNK_Config
|
|||||||
U64 function_pad_min;
|
U64 function_pad_min;
|
||||||
U64 *manifest_resource_id;
|
U64 *manifest_resource_id;
|
||||||
B32 no_default_libs;
|
B32 no_default_libs;
|
||||||
|
B32 force;
|
||||||
LNK_SwitchState infer_asan_libs;
|
LNK_SwitchState infer_asan_libs;
|
||||||
Version link_ver;
|
Version link_ver;
|
||||||
Version os_ver;
|
Version os_ver;
|
||||||
@@ -387,6 +390,7 @@ typedef struct LNK_Config
|
|||||||
String8 type_server_name;
|
String8 type_server_name;
|
||||||
LNK_SwitchState type_server;
|
LNK_SwitchState type_server;
|
||||||
LNK_SwitchState sort_imports;
|
LNK_SwitchState sort_imports;
|
||||||
|
LNK_SwitchState llvm_addrsig;
|
||||||
} LNK_Config;
|
} LNK_Config;
|
||||||
|
|
||||||
// --- MSVC Error Codes --------------------------------------------------------
|
// --- MSVC Error Codes --------------------------------------------------------
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ typedef enum
|
|||||||
LNK_Log_Count
|
LNK_Log_Count
|
||||||
} LNK_LogType;
|
} LNK_LogType;
|
||||||
|
|
||||||
|
// TODO: factor into an xlist with explicitly defined error levels and warnings
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
LNK_Error_Null,
|
LNK_Error_Null,
|
||||||
|
|||||||
+38
-11
@@ -100,7 +100,7 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer)
|
|||||||
COFF_SectionFlags *section_flags = push_array_no_zero(arena, COFF_SectionFlags, header.section_count_no_null);
|
COFF_SectionFlags *section_flags = push_array_no_zero(arena, COFF_SectionFlags, header.section_count_no_null);
|
||||||
for (U64 sect_idx = 0; sect_idx < header.section_count_no_null; sect_idx += 1) {
|
for (U64 sect_idx = 0; sect_idx < header.section_count_no_null; sect_idx += 1) {
|
||||||
COFF_SectionHeader *coff_sect_header = &coff_section_table[sect_idx];
|
COFF_SectionHeader *coff_sect_header = &coff_section_table[sect_idx];
|
||||||
section_flags[sect_idx] = coff_sect_header->flags;
|
section_flags[sect_idx] = coff_sect_header->flags & ~3; // linker reserves low 2 bits for internal flags
|
||||||
String8 sect_name = coff_name_from_section_header(raw_coff_string_table, coff_sect_header);
|
String8 sect_name = coff_name_from_section_header(raw_coff_string_table, coff_sect_header);
|
||||||
if (~section_flags[sect_idx] & COFF_SectionFlag_CntUninitializedData) {
|
if (~section_flags[sect_idx] & COFF_SectionFlag_CntUninitializedData) {
|
||||||
if (coff_sect_header->fsize > 0) {
|
if (coff_sect_header->fsize > 0) {
|
||||||
@@ -344,6 +344,7 @@ THREAD_POOL_TASK_FUNC(lnk_obj_initer)
|
|||||||
obj->debug_t_sect_idx = ~0;
|
obj->debug_t_sect_idx = ~0;
|
||||||
obj->debug_p_sect_idx = ~0;
|
obj->debug_p_sect_idx = ~0;
|
||||||
obj->debug_h_sect_idx = ~0;
|
obj->debug_h_sect_idx = ~0;
|
||||||
|
obj->llvm_addrsig_sect_idx = ~0;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal
|
internal
|
||||||
@@ -362,6 +363,19 @@ THREAD_POOL_TASK_FUNC(lnk_obj_find_debug_t)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal
|
||||||
|
THREAD_POOL_TASK_FUNC(lnk_obj_find_llvm_addrsig)
|
||||||
|
{
|
||||||
|
LNK_Obj *obj = &((LNK_ObjNode *)raw_task)[task_id].data;
|
||||||
|
for EachIndex(sect_idx, obj->header.section_count_no_null) {
|
||||||
|
String8 section_name = lnk_obj_section_name_from_sect_idx(obj, sect_idx);
|
||||||
|
if (str8_match(section_name, str8_lit(".llvm_addrsig"), 0)) {
|
||||||
|
obj->llvm_addrsig_sect_idx = sect_idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal LNK_ObjNode *
|
internal LNK_ObjNode *
|
||||||
lnk_obj_from_input_many(TP_Context *tp, TP_Arena *arena, LNK_Config *config, U64 inputs_count, LNK_Input **inputs)
|
lnk_obj_from_input_many(TP_Context *tp, TP_Arena *arena, LNK_Config *config, U64 inputs_count, LNK_Input **inputs)
|
||||||
{
|
{
|
||||||
@@ -369,10 +383,12 @@ lnk_obj_from_input_many(TP_Context *tp, TP_Arena *arena, LNK_Config *config, U64
|
|||||||
if (inputs_count) {
|
if (inputs_count) {
|
||||||
objs = push_array(arena->v[0], LNK_ObjNode, inputs_count);
|
objs = push_array(arena->v[0], LNK_ObjNode, inputs_count);
|
||||||
tp_for_parallel(tp, arena, inputs_count, lnk_obj_initer, &(LNK_ObjIniter){ .inputs = inputs, .objs = objs, .machine = config->machine });
|
tp_for_parallel(tp, arena, inputs_count, lnk_obj_initer, &(LNK_ObjIniter){ .inputs = inputs, .objs = objs, .machine = config->machine });
|
||||||
|
|
||||||
if (lnk_do_debug_info(config)) {
|
if (lnk_do_debug_info(config)) {
|
||||||
tp_for_parallel(tp, arena, inputs_count, lnk_obj_find_debug_t, objs);
|
tp_for_parallel(tp, arena, inputs_count, lnk_obj_find_debug_t, objs);
|
||||||
}
|
}
|
||||||
|
if (config->opt_icf == LNK_SwitchState_Yes) {
|
||||||
|
tp_for_parallel(tp, arena, inputs_count, lnk_obj_find_llvm_addrsig, objs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return objs;
|
return objs;
|
||||||
}
|
}
|
||||||
@@ -451,19 +467,26 @@ THREAD_POOL_TASK_FUNC(lnk_input_coff_symbol_table)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal LNK_SymbolHashTrie **
|
internal LNK_ObjSymbolRef *
|
||||||
lnk_symlinks_from_obj(Arena *arena, LNK_SymbolTable *symtab, LNK_Obj *obj)
|
lnk_symlinks_from_obj(Arena *arena, LNK_SymbolTable *symtab, LNK_Obj *obj)
|
||||||
{
|
{
|
||||||
LNK_SymbolHashTrie **symlinks = push_array(arena, LNK_SymbolHashTrie *, obj->header.section_count_no_null+1);
|
LNK_ObjSymbolRef *symlinks = push_array(arena, LNK_ObjSymbolRef, obj->header.section_count_no_null+1);
|
||||||
COFF_ParsedSymbol symbol;
|
COFF_ParsedSymbol symbol;
|
||||||
for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) {
|
for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) {
|
||||||
symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx);
|
symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx);
|
||||||
COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class);
|
COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class);
|
||||||
if (interp == COFF_SymbolValueInterp_Regular && symbol.aux_symbol_count == 0 && symbol.storage_class == COFF_SymStorageClass_External) {
|
if (interp == COFF_SymbolValueInterp_Regular) {
|
||||||
LNK_ObjSection section = lnk_obj_section_from_section_number(obj, symbol.section_number);
|
LNK_ObjSection section = lnk_obj_section_from_section_number(obj, symbol.section_number);
|
||||||
if (*section.flags & COFF_SectionFlag_LnkCOMDAT) {
|
if (*section.flags & COFF_SectionFlag_LnkCOMDAT) {
|
||||||
if (symlinks[symbol.section_number] == 0 || symbol.value == 0) {
|
if (symbol.aux_symbol_count == 0 && symbol.storage_class == COFF_SymStorageClass_External) {
|
||||||
symlinks[symbol.section_number] = lnk_symbol_table_search_(symtab, symbol.name);
|
if (symlinks[symbol.section_number].obj == 0 || symbol.value == 0) {
|
||||||
|
LNK_SymbolHashTrie *link_symbol = lnk_symbol_table_search_(symtab, symbol.name);
|
||||||
|
if (link_symbol) {
|
||||||
|
symlinks[symbol.section_number] = lnk_ref_from_symbol(link_symbol->symbol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (symlinks[symbol.section_number].obj == 0 && symbol.storage_class == COFF_SymStorageClass_Static && symbol.aux_symbol_count > 0) {
|
||||||
|
symlinks[symbol.section_number] = (LNK_ObjSymbolRef){ obj, symbol_idx };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -543,11 +566,15 @@ lnk_obj_get_removed_section_number(LNK_Obj *obj)
|
|||||||
return obj->header.is_big_obj ? LNK_REMOVED_SECTION_NUMBER_32 : LNK_REMOVED_SECTION_NUMBER_16;
|
return obj->header.is_big_obj ? LNK_REMOVED_SECTION_NUMBER_32 : LNK_REMOVED_SECTION_NUMBER_16;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal LNK_Symbol *
|
internal B32
|
||||||
lnk_obj_get_comdat_symlink(LNK_Obj *obj, U64 section_number)
|
lnk_obj_get_comdat_symlink(LNK_Obj *obj, U64 section_number, LNK_ObjSymbolRef *symlink_out)
|
||||||
{
|
{
|
||||||
LNK_SymbolHashTrie *symlink = obj->symlinks[section_number];
|
LNK_ObjSymbolRef symlink = obj->symlinks[section_number];
|
||||||
return symlink ? symlink->symbol : 0;
|
B32 is_valid = symlink.obj != 0;
|
||||||
|
if (is_valid && symlink_out) {
|
||||||
|
*symlink_out = symlink;
|
||||||
|
}
|
||||||
|
return is_valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal COFF_SectionHeader *
|
internal COFF_SectionHeader *
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ typedef struct LNK_Obj
|
|||||||
// COMDAT
|
// COMDAT
|
||||||
U32 *comdats;
|
U32 *comdats;
|
||||||
U32Node **associated_sections;
|
U32Node **associated_sections;
|
||||||
LNK_SymbolHashTrie **symlinks;
|
LNK_ObjSymbolRef *symlinks;
|
||||||
|
|
||||||
// link
|
// link
|
||||||
struct LNK_LibMemberRef *link_member;
|
struct LNK_LibMemberRef *link_member;
|
||||||
@@ -33,6 +33,9 @@ typedef struct LNK_Obj
|
|||||||
U32 debug_p_sect_idx;
|
U32 debug_p_sect_idx;
|
||||||
U32 debug_h_sect_idx;
|
U32 debug_h_sect_idx;
|
||||||
|
|
||||||
|
// ICF
|
||||||
|
U32 llvm_addrsig_sect_idx;
|
||||||
|
|
||||||
// @type_server
|
// @type_server
|
||||||
Rng1U64 ti_range;
|
Rng1U64 ti_range;
|
||||||
CV_TypeIndex *ti_map;
|
CV_TypeIndex *ti_map;
|
||||||
@@ -139,7 +142,7 @@ internal U32 lnk_obj_get_vol_md(LNK_Obj *obj);
|
|||||||
internal struct LNK_Lib * lnk_obj_get_lib(LNK_Obj *obj);
|
internal struct LNK_Lib * lnk_obj_get_lib(LNK_Obj *obj);
|
||||||
internal String8 lnk_obj_get_lib_path(LNK_Obj *obj);
|
internal String8 lnk_obj_get_lib_path(LNK_Obj *obj);
|
||||||
internal U32 lnk_obj_get_removed_section_number(LNK_Obj *obj);
|
internal U32 lnk_obj_get_removed_section_number(LNK_Obj *obj);
|
||||||
internal LNK_Symbol * lnk_obj_get_comdat_symlink(LNK_Obj *obj, U64 section_number);
|
internal B32 lnk_obj_get_comdat_symlink(LNK_Obj *obj, U64 section_number, LNK_ObjSymbolRef *symlink_out);
|
||||||
|
|
||||||
// --- Symbol & Section Helpers ------------------------------------------------
|
// --- Symbol & Section Helpers ------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -717,8 +717,8 @@ lnk_resolve_symbol(LNK_SymbolTable *symtab, LNK_ObjSymbolRef symbol, LNK_ObjSymb
|
|||||||
COFF_SymbolValueInterpType symbol_interp = coff_interp_symbol(symbol_parsed.section_number, symbol_parsed.value, symbol_parsed.storage_class);
|
COFF_SymbolValueInterpType symbol_interp = coff_interp_symbol(symbol_parsed.section_number, symbol_parsed.value, symbol_parsed.storage_class);
|
||||||
switch (symbol_interp) {
|
switch (symbol_interp) {
|
||||||
case COFF_SymbolValueInterp_Regular: {
|
case COFF_SymbolValueInterp_Regular: {
|
||||||
LNK_Symbol *symlink = lnk_obj_get_comdat_symlink(symbol.obj, symbol_parsed.section_number);
|
LNK_ObjSymbolRef symlink = {0};
|
||||||
*symbol_out = symlink ? lnk_ref_from_symbol(symlink) : symbol;
|
*symbol_out = lnk_obj_get_comdat_symlink(symbol.obj, symbol_parsed.section_number, &symlink) ? symlink : symbol;
|
||||||
} break;
|
} break;
|
||||||
case COFF_SymbolValueInterp_Weak: {
|
case COFF_SymbolValueInterp_Weak: {
|
||||||
LNK_Symbol *defn = lnk_symbol_table_search(symtab, symbol_parsed.name);
|
LNK_Symbol *defn = lnk_symbol_table_search(symtab, symbol_parsed.name);
|
||||||
|
|||||||
+1135
-23
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
|||||||
internal void
|
internal void
|
||||||
tp_run_tasks(TP_Context *pool, TP_Worker *worker)
|
tp_run_tasks(TP_Context *pool, TP_Worker *worker)
|
||||||
{
|
{
|
||||||
barrier_wait(pool->barrier);
|
barrier_wait(pool->run_barrier);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
S64 task_left = ins_atomic_u64_dec_eval(&pool->task_left);
|
S64 task_left = ins_atomic_u64_dec_eval(&pool->task_left);
|
||||||
@@ -19,14 +19,11 @@ tp_run_tasks(TP_Context *pool, TP_Worker *worker)
|
|||||||
U64 task_id = pool->task_count - (task_left+1);
|
U64 task_id = pool->task_count - (task_left+1);
|
||||||
pool->task_func(arena, worker->id, task_id, pool->task_data, pool);
|
pool->task_func(arena, worker->id, task_id, pool->task_data, pool);
|
||||||
|
|
||||||
// cache task count so we dont touch pool memory after atomic inc
|
|
||||||
U64 task_count = pool->task_count;
|
|
||||||
|
|
||||||
// update task done count
|
// update task done count
|
||||||
ins_atomic_u64_inc_eval(&pool->task_done);
|
ins_atomic_u64_inc_eval(&pool->task_done);
|
||||||
}
|
}
|
||||||
|
|
||||||
barrier_wait(pool->barrier);
|
barrier_wait(pool->run_barrier);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void
|
internal void
|
||||||
@@ -74,6 +71,7 @@ tp_alloc(Arena *arena, U32 worker_count, U32 max_worker_count, String8 name)
|
|||||||
// init pool
|
// init pool
|
||||||
TP_Context *pool = push_array(arena, TP_Context, 1);
|
TP_Context *pool = push_array(arena, TP_Context, 1);
|
||||||
pool->exec_semaphore = exec_semaphore;
|
pool->exec_semaphore = exec_semaphore;
|
||||||
|
pool->run_barrier = barrier_alloc(worker_count);
|
||||||
pool->barrier = barrier_alloc(worker_count);
|
pool->barrier = barrier_alloc(worker_count);
|
||||||
pool->is_live = 1;
|
pool->is_live = 1;
|
||||||
pool->worker_count = worker_count;
|
pool->worker_count = worker_count;
|
||||||
@@ -113,6 +111,7 @@ tp_release(TP_Context *pool)
|
|||||||
if (is_shared) {
|
if (is_shared) {
|
||||||
semaphore_release(pool->exec_semaphore);
|
semaphore_release(pool->exec_semaphore);
|
||||||
}
|
}
|
||||||
|
barrier_release(pool->run_barrier);
|
||||||
barrier_release(pool->barrier);
|
barrier_release(pool->barrier);
|
||||||
|
|
||||||
MemoryZeroStruct(pool);
|
MemoryZeroStruct(pool);
|
||||||
@@ -194,13 +193,14 @@ tp_for_parallel(TP_Context *pool, TP_Arena *task_arena, U64 task_count, TP_TaskF
|
|||||||
|
|
||||||
// if we are in shared mode -> ping
|
// if we are in shared mode -> ping
|
||||||
if (*pool->exec_semaphore.u64) {
|
if (*pool->exec_semaphore.u64) {
|
||||||
U64 drop_count64 = Min(task_count, pool->worker_count);
|
U64 drop_count64 = pool->worker_count - 1;
|
||||||
U32 drop_count = safe_cast_u32(drop_count64);
|
U32 drop_count = safe_cast_u32(drop_count64);
|
||||||
semaphore_drop_count(pool->exec_semaphore, drop_count);
|
semaphore_drop_count(pool->exec_semaphore, drop_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
// run tasks on main worker
|
// run tasks on main worker
|
||||||
tp_run_tasks(pool, pool->worker_arr);
|
tp_run_tasks(pool, pool->worker_arr);
|
||||||
|
Assert(pool->task_done == task_count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ typedef struct TP_Context
|
|||||||
Semaphore exec_semaphore;
|
Semaphore exec_semaphore;
|
||||||
Semaphore task_semaphore;
|
Semaphore task_semaphore;
|
||||||
Semaphore main_semaphore;
|
Semaphore main_semaphore;
|
||||||
|
Barrier run_barrier;
|
||||||
Barrier barrier;
|
Barrier barrier;
|
||||||
void *broadcast;
|
void *broadcast;
|
||||||
U64 broadcast_size;
|
U64 broadcast_size;
|
||||||
@@ -58,4 +59,3 @@ internal void tp_temp_end(TP_Temp temp);
|
|||||||
internal void tp_for_parallel(TP_Context *pool, TP_Arena *arena, U64 task_count, TP_TaskFunc *task_func, void *task_data);
|
internal void tp_for_parallel(TP_Context *pool, TP_Arena *arena, U64 task_count, TP_TaskFunc *task_func, void *task_data);
|
||||||
internal Rng1U64 * tp_divide_work(Arena *arena, U64 item_count, U32 worker_count);
|
internal Rng1U64 * tp_divide_work(Arena *arena, U64 item_count, U32 worker_count);
|
||||||
#define tp_broadcast(p) tp_broadcast_(tp, task_id, p, sizeof(*p))
|
#define tp_broadcast(p) tp_broadcast_(tp, task_id, p, sizeof(*p))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user