From d32667546dbb0a0401c2fe605475cd53ebd7f941 Mon Sep 17 00:00:00 2001 From: Nikita Smith Date: Tue, 15 Jul 2025 14:31:49 -0700 Subject: [PATCH] factor out weak symbol resolution logic and apply it to the symbol table when building link context --- src/coff/coff_parse.c | 6 + src/coff/coff_parse.h | 1 + src/linker/lnk.c | 709 +++++++--------------------------- src/linker/lnk.h | 29 +- src/linker/lnk_obj.c | 21 +- src/linker/lnk_obj.h | 1 + src/linker/lnk_symbol_table.c | 239 ++++++++---- src/linker/lnk_symbol_table.h | 10 + src/torture/torture.c | 227 ++++++++--- 9 files changed, 516 insertions(+), 727 deletions(-) diff --git a/src/coff/coff_parse.c b/src/coff/coff_parse.c index 2f356a90..9d734c48 100644 --- a/src/coff/coff_parse.c +++ b/src/coff/coff_parse.c @@ -279,6 +279,12 @@ coff_interp_symbol(U32 section_number, U32 value, COFF_SymStorageClass storage_c return COFF_SymbolValueInterp_Regular; } +internal COFF_SymbolValueInterpType +coff_interp_from_parsed_symbol(COFF_ParsedSymbol symbol) +{ + return coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); +} + internal void coff_parse_secdef(COFF_ParsedSymbol symbol, B32 is_big_obj, COFF_ComdatSelectType *selection_out, U32 *number_out, U32 *length_out, U32 *check_sum_out) { diff --git a/src/coff/coff_parse.h b/src/coff/coff_parse.h index fb230ae7..92106867 100644 --- a/src/coff/coff_parse.h +++ b/src/coff/coff_parse.h @@ -267,6 +267,7 @@ internal COFF_Symbol32Array coff_symbol_array_from_data (Arena *arena, String8 internal COFF_Symbol16Node *coff_symbol16_list_push(Arena *arena, COFF_Symbol16List *list, COFF_Symbol16 symbol); internal COFF_SymbolValueInterpType coff_interp_symbol(U32 section_number, U32 value, COFF_SymStorageClass storage_class); +internal COFF_SymbolValueInterpType coff_interp_from_parsed_symbol(COFF_ParsedSymbol symbol); internal void coff_parse_secdef(COFF_ParsedSymbol symbol, B32 is_big_obj, COFF_ComdatSelectType *selection_out, U32 *number_out, U32 *length_out, U32 *check_sum_out); internal COFF_SymbolWeakExt * coff_parse_weak_tag(COFF_ParsedSymbol symbol, B32 is_big_obj); diff --git a/src/linker/lnk.c b/src/linker/lnk.c index b66ed10c..a62701c4 100644 --- a/src/linker/lnk.c +++ b/src/linker/lnk.c @@ -2189,6 +2189,9 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) break; } + // pass over symbol table and replace weak symbols without a strong definition with fallback definitions + lnk_finalize_weak_symbols(tp, symtab); + // log { if (lnk_get_log_status(LNK_Log_InputObj)) { @@ -2207,7 +2210,6 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) } exit:; - LNK_LinkContext link_ctx = {0}; link_ctx.symtab = symtab; link_ctx.objs_count = obj_list.count; @@ -2223,13 +2225,6 @@ lnk_build_link_context(TP_Context *tp, TP_Arena *tp_arena, LNK_Config *config) #undef state_list_pop } -internal LNK_SymbolTableFixup * -lnk_bsearch_symbol_table_fixup_array(LNK_SymbolTableFixupArray array, U64 symbol_idx) -{ - NotImplemented; - return 0; -} - internal THREAD_POOL_TASK_FUNC(lnk_remove_associative_sections_task) { @@ -2426,214 +2421,51 @@ THREAD_POOL_TASK_FUNC(lnk_set_comdat_leaders_contribs_task) scratch_end(scratch); } -internal LNK_SymbolTableFixup * -lnk_symbol_table_fixup_list_push(Arena *arena, LNK_SymbolTableFixupList *list) +internal B32 +lnk_resolve_symbol(LNK_SymbolTable *symtab, LNK_SymbolDefined symbol, LNK_SymbolDefined *symbol_out) { - LNK_SymbolTableFixupNode *node = push_array(arena, LNK_SymbolTableFixupNode, 1); - SLLQueuePush(list->first, list->last, node); - list->count += 1; - return &node->data; -} - -internal LNK_SymbolTableFixupArray -lnk_array_from_symbol_table_fixup_list(Arena *arena, LNK_SymbolTableFixupList list) -{ - LNK_SymbolTableFixupArray result = {0}; - result.v = push_array(arena, LNK_SymbolTableFixup, list.count); - for (LNK_SymbolTableFixupNode *n = list.first; n != 0; n = n->next) { - result.v[result.count++] = n->data; - } - return result; -} - -internal -THREAD_POOL_TASK_FUNC(lnk_gather_symtab_fixups_task) -{ - Temp scratch = scratch_begin(&arena, 1); - - LNK_BuildImageTask *task = raw_task; - U64 obj_idx = task_id; - LNK_Obj *obj = task->objs[task_id]; - - ProfBeginV("%S", obj->path); - - LNK_SymbolTableFixupList fixups = {0}; - - HashTable *visited_symbols_ht = hash_table_init(scratch.arena, 32); - struct LookupLocation { struct LookupLocation *next; LNK_Obj *obj; U64 symbol_idx; }; - struct LookupLocation *lookup_first = 0, *lookup_last = 0, *lookup_free_list = 0; - - COFF_ParsedSymbol symbol; - 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); - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - - COFF_ParsedSymbol fixup_symbol = symbol; - COFF_SymbolValueInterpType fixup_interp = interp; - - for (String8 lookup_name = symbol.name; fixup_interp == COFF_SymbolValueInterp_Weak;) { - // search external symbol table for definition - LNK_Symbol *defn_symbol = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, lookup_name); - COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_defined(defn_symbol); - COFF_SymbolValueInterpType defn_interp = coff_interp_symbol(defn_parsed.section_number, defn_parsed.value, defn_parsed.storage_class); - - // was weak symbol replaced by strong symbol? - if (defn_interp != COFF_SymbolValueInterp_Weak) { - fixup_interp = defn_interp; - fixup_symbol = defn_parsed; - break; - } - - // strong symbol not found, fallback to tag symbol - COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(defn_parsed, defn_symbol->u.defined.obj->header.is_big_obj); - COFF_ParsedSymbol tag_parsed = lnk_parsed_symbol_from_coff_symbol_idx(defn_symbol->u.defined.obj, weak_ext->tag_index); - COFF_SymbolValueInterpType tag_interp = coff_interp_symbol(tag_parsed.section_number, tag_parsed.value, tag_parsed.storage_class); - - // validate tag symbol - if (tag_interp == COFF_SymbolValueInterp_Weak) { - Assert(!"TODO: report that tag symbol must not be weak"); - break; - } - - // tag is a resolved symbol - if (tag_interp != COFF_SymbolValueInterp_Undefined) { - fixup_interp = tag_interp; - fixup_symbol = tag_parsed; - break; - } - - // guard against self-referencing weak symbols - struct LookupLocation *was_visited = 0; - hash_table_search_string_raw(visited_symbols_ht, tag_parsed.name, &was_visited); - if (was_visited) { - Temp temp = temp_begin(scratch.arena); - - String8List list = {0}; - for (struct LookupLocation *l = lookup_first; l != 0; l = l->next) { - COFF_ParsedSymbol loc_symbol = lnk_parsed_symbol_from_coff_symbol_idx(l->obj, l->symbol_idx); - str8_list_pushf(temp.arena, &list, "\t%S Symbol %S (No.%#llx) =>", l->obj->path, loc_symbol.name, l->symbol_idx); - } - COFF_ParsedSymbol loc_symbol = lnk_parsed_symbol_from_coff_symbol_idx(was_visited->obj, was_visited->symbol_idx); - str8_list_pushf(temp.arena, &list, "\t%S Symbol %S (No.%#llx)", was_visited->obj->path, loc_symbol.name, was_visited->symbol_idx); - - String8 loc_string = str8_list_join(temp.arena, &list, &(StringJoin){.sep = str8_lit("\n") }); - lnk_error_obj(LNK_Error_WeakCycle, obj, "unable to resolve cyclic symbol %S; ref chain:\n%S", symbol.name, loc_string); - - temp_end(temp); - break; - } - - // alloc lookup location - struct LookupLocation *loc = lookup_free_list; - if (lookup_free_list) { SLLStackPop(lookup_free_list); } - else { loc = push_array(scratch.arena, struct LookupLocation, 1); } - - // fill out lookup location - loc->obj = defn_symbol->u.defined.obj; - loc->symbol_idx = symbol_idx; - SLLQueuePush(lookup_first, lookup_last, loc); - hash_table_push_string_raw(scratch.arena, visited_symbols_ht, lookup_name, loc); - - // follow undefined tag symbol - lookup_name = tag_parsed.name; - } - - if (fixup_interp == COFF_SymbolValueInterp_Common) { - LNK_Symbol *defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, fixup_symbol.name); - if (defn) { - LNK_SymbolTableFixup *fixup = lnk_symbol_table_fixup_list_push(scratch.arena, &fixups); - fixup->idx = symbol_idx; - fixup->obj_idx = defn->u.defined.obj->input_idx; - fixup->obj_symbol_idx = defn->u.defined.symbol_idx; - } - } else if (fixup_interp == COFF_SymbolValueInterp_Abs) { - if (fixup_symbol.storage_class == COFF_SymStorageClass_External) { - LNK_Symbol *defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, fixup_symbol.name); - - if (defn == 0) { continue; } - if (defn->u.defined.obj == obj && defn->u.defined.symbol_idx == symbol_idx) { continue; } - - LNK_SymbolTableFixup *fixup = lnk_symbol_table_fixup_list_push(scratch.arena, &fixups); - fixup->idx = symbol_idx; - fixup->obj_idx = defn->u.defined.obj->input_idx; - fixup->obj_symbol_idx = defn->u.defined.symbol_idx; - } - } else if (fixup_interp == COFF_SymbolValueInterp_Undefined) { - if (symbol.storage_class == COFF_SymStorageClass_External) { - LNK_Symbol *defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol.name); - if (defn) { - LNK_SymbolTableFixup *fixup = lnk_symbol_table_fixup_list_push(scratch.arena, &fixups); - fixup->idx = symbol_idx; - fixup->obj_idx = defn->u.defined.obj->input_idx; - fixup->obj_symbol_idx = defn->u.defined.symbol_idx; - } - } - } - } - - task->obj_symtab_fixups[obj_idx] = lnk_array_from_symbol_table_fixup_list(arena, fixups); - - ProfEnd(); - scratch_end(scratch); -} - -internal -THREAD_POOL_TASK_FUNC(lnk_apply_symtab_fixups_task) -{ - LNK_BuildImageTask *task = raw_task; - U64 obj_idx = task_id; - LNK_Obj *obj = task->objs[obj_idx]; - LNK_SymbolTableFixupArray fixups = task->obj_symtab_fixups[obj_idx]; - - ProfBeginV("%S\n", obj->path); - for EachIndex(fixup_idx, fixups.count) { - LNK_SymbolTableFixup *fixup = &fixups.v[fixup_idx]; - - COFF_ParsedSymbol fixup_dst = lnk_parsed_symbol_from_coff_symbol_idx(obj, fixup->idx); - COFF_SymbolValueInterpType fixup_dst_type = coff_interp_symbol(fixup_dst.section_number, fixup_dst.value, fixup_dst.storage_class); - if (task->u.patch_symtabs.fixup_type != fixup_dst_type) { continue; } - - LNK_Obj *fixup_obj_src = task->objs[fixup->obj_idx]; - COFF_ParsedSymbol fixup_src = lnk_parsed_symbol_from_coff_symbol_idx(fixup_obj_src, fixup->obj_symbol_idx); - COFF_SymbolValueInterpType fixup_type = coff_interp_symbol(fixup_src.section_number, fixup_src.value, fixup_src.storage_class); - - if (fixup_type == COFF_SymbolValueInterp_Regular) { - if (obj->header.is_big_obj) { - COFF_Symbol32 *symbol32 = fixup_dst.raw_symbol; - symbol32->section_number = fixup_src.section_number; - symbol32->value = safe_cast_u32(fixup_src.value); - symbol32->type = fixup_src.type; - symbol32->storage_class = COFF_SymStorageClass_Static; - } else { - COFF_Symbol16 *symbol16 = fixup_dst.raw_symbol; - symbol16->section_number = safe_cast_u16(fixup_src.section_number); - symbol16->value = safe_cast_u32(fixup_src.value); - symbol16->type = fixup_src.type; - symbol16->storage_class = COFF_SymStorageClass_Static; - } - } else if (fixup_type == COFF_SymbolValueInterp_Abs) { - if (obj->header.is_big_obj) { - COFF_Symbol32 *symbol32 = fixup_dst.raw_symbol; - symbol32->section_number = COFF_Symbol_AbsSection32; - symbol32->value = safe_cast_u32(fixup_src.value); - symbol32->type = fixup_src.type; - symbol32->storage_class = COFF_SymStorageClass_Static; - } else { - COFF_Symbol16 *symbol16 = fixup_dst.raw_symbol; - symbol16->section_number = COFF_Symbol_AbsSection16; - symbol16->value = safe_cast_u32(fixup_src.value); - symbol16->type = fixup_src.type; - symbol16->storage_class = COFF_SymStorageClass_Static; - } + B32 is_resolved = 1; + COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_coff_symbol_idx(symbol.obj, symbol.symbol_idx); + COFF_SymbolValueInterpType symbol_interp = coff_interp_symbol(symbol_parsed.section_number, symbol_parsed.value, symbol_parsed.storage_class); + switch (symbol_interp) { + case COFF_SymbolValueInterp_Regular: { *symbol_out = symbol; } break; + case COFF_SymbolValueInterp_Weak: { + LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, symbol_parsed.name); + COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); + COFF_SymbolValueInterpType defn_interp = coff_interp_symbol(defn_parsed.section_number, defn_parsed.value, defn_parsed.storage_class); + if (defn_interp != COFF_SymbolValueInterp_Undefined) { + *symbol_out = defn->u.defined; } else { - InvalidPath; + is_resolved = 0; } + } break; + case COFF_SymbolValueInterp_Undefined: { + LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, symbol_parsed.name); + if (defn) { + *symbol_out = defn->u.defined; + } else { + is_resolved = 0; + } + } break; + case COFF_SymbolValueInterp_Common: { + LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, symbol_parsed.name); + *symbol_out = defn->u.defined; + } break; + case COFF_SymbolValueInterp_Abs: { + if (symbol_parsed.storage_class == COFF_SymStorageClass_External) { + LNK_Symbol *defn = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, symbol_parsed.name); + *symbol_out = defn->u.defined; + } else { + *symbol_out = symbol; + } + } break; + case COFF_SymbolValueInterp_Debug: { *symbol_out = symbol; } break; } - ProfEnd(); + return is_resolved; } internal void -lnk_gc_sections(U64 objs_count, LNK_Obj **objs, LNK_SectionContrib ***sect_map, LNK_SymbolTableFixupArray *obj_symtab_fixups, U64 roots_count, LNK_Symbol **roots) +lnk_gc_sections(LNK_SymbolTable *symtab, U64 objs_count, LNK_Obj **objs, LNK_SectionContrib ***sect_map, U64 roots_count, LNK_Symbol **roots) { Temp scratch = scratch_begin(0,0); @@ -2726,10 +2558,13 @@ lnk_gc_sections(U64 objs_count, LNK_Obj **objs, LNK_SectionContrib ***sect_map, for EachIndex(reloc_idx, t->relocs.count) { COFF_Reloc *reloc = &t->relocs.v[reloc_idx]; - LNK_SymbolTableFixup *fixup = lnk_bsearch_symbol_table_fixup_array(obj_symtab_fixups[t->obj->input_idx], reloc->isymbol); - LNK_Obj *reloc_obj; U32 reloc_symbol_idx; - if (fixup == 0) { reloc_obj = t->obj, reloc_symbol_idx = reloc->isymbol; } - else { reloc_obj = objs[fixup->obj_idx]; reloc_symbol_idx = fixup->obj_symbol_idx; } + LNK_SymbolDefined symbol_to_resolve = { .obj = t->obj, .symbol_idx = reloc->isymbol }; + LNK_SymbolDefined resolved_symbol = {0}; + B32 is_symbol_resolved = lnk_resolve_symbol(symtab, symbol_to_resolve, &resolved_symbol); + if ( ! is_symbol_resolved) { continue; } + + LNK_Obj *reloc_obj = resolved_symbol.obj; + U32 reloc_symbol_idx = resolved_symbol.symbol_idx; COFF_ParsedSymbol reloc_symbol = lnk_parsed_symbol_from_coff_symbol_idx(reloc_obj, reloc_symbol_idx); COFF_SymbolValueInterpType reloc_symbol_interp = coff_interp_symbol(reloc_symbol.section_number, reloc_symbol.value, reloc_symbol.storage_class); @@ -2905,7 +2740,7 @@ THREAD_POOL_TASK_FUNC(lnk_patch_common_block_leaders_task) ProfBeginFunction(); LNK_BuildImageTask *task = raw_task; - Rng1U64 contrib_range = task->u.patch_symtabs.ranges[task_id]; + Rng1U64 contrib_range = task->u.patch_symtabs.common_block_ranges[task_id]; for (U64 contrib_idx = contrib_range.min; contrib_idx < contrib_range.max; contrib_idx += 1) { LNK_CommonBlockContrib *contrib = &task->u.patch_symtabs.common_block_contribs[contrib_idx]; @@ -3010,275 +2845,76 @@ THREAD_POOL_TASK_FUNC(lnk_patch_regular_symbols_task) ProfEnd(); } -internal -THREAD_POOL_TASK_FUNC(lnk_patch_abs_symbols_task) +internal void +lnk_patch_obj_symtab(LNK_SymbolTable *symtab, LNK_Obj *obj, B8 *was_symbol_patched, COFF_SymbolValueInterpType fixup_type) { - LNK_BuildImageTask *task = raw_task; - U64 obj_idx = task_id; - LNK_Obj *obj = task->objs[obj_idx]; + ProfBeginV("%S\n", obj->path); - ProfBeginV("Patch Absolute Symbols [%S]", obj->path); - COFF_ParsedSymbol symbol; - 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); - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); + COFF_ParsedSymbol fixup_dst; + for (U64 symbol_idx = 0; symbol_idx < obj->header.symbol_count; symbol_idx += (1 + fixup_dst.aux_symbol_count)) { + fixup_dst = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); + if (was_symbol_patched[symbol_idx]) { continue; } - if (interp == COFF_SymbolValueInterp_Abs && symbol.storage_class == COFF_SymStorageClass_External) { - LNK_Symbol *defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol.name); + COFF_SymbolValueInterpType fixup_dst_type = coff_interp_symbol(fixup_dst.section_number, fixup_dst.value, fixup_dst.storage_class); + if (fixup_type != fixup_dst_type) { continue; } - if (defn == 0) { - continue; - } - if (defn->u.defined.obj == obj && defn->u.defined.symbol_idx == symbol_idx) { - continue; - } + LNK_SymbolDefined symbol_to_resolve = { .obj = obj, .symbol_idx = symbol_idx }; + LNK_SymbolDefined fixup_symbol = {0}; + B32 is_resolved = lnk_resolve_symbol(symtab, symbol_to_resolve, &fixup_symbol); + if (is_resolved) { + COFF_ParsedSymbol fixup_src = lnk_parsed_symbol_from_coff_symbol_idx(fixup_symbol.obj, fixup_symbol.symbol_idx); + COFF_SymbolValueInterpType fixup_type = coff_interp_symbol(fixup_src.section_number, fixup_src.value, fixup_src.storage_class); + AssertAlways(fixup_type == COFF_SymbolValueInterp_Regular || + fixup_type == COFF_SymbolValueInterp_Abs || + fixup_type == COFF_SymbolValueInterp_Common); - COFF_ParsedSymbol defn_symbol = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); - COFF_SymbolValueInterpType defn_interp = coff_interp_symbol(defn_symbol.section_number, defn_symbol.value, defn_symbol.storage_class); - if (defn_interp == COFF_SymbolValueInterp_Regular) { - if (defn->u.defined.obj->header.is_big_obj) { - COFF_Symbol32 *symbol32 = symbol.raw_symbol; - symbol32->section_number = defn_symbol.section_number; - symbol32->value = defn_symbol.value; - symbol32->type = defn_symbol.type; - symbol32->storage_class = COFF_SymStorageClass_Static; - } else { - COFF_Symbol16 *symbol16 = symbol.raw_symbol; - symbol16->section_number = defn_symbol.section_number; - symbol16->value = defn_symbol.value; - symbol16->type = defn_symbol.type; - symbol16->storage_class = COFF_SymStorageClass_Static; - } + if (obj->header.is_big_obj) { + COFF_Symbol32 *symbol32 = fixup_dst.raw_symbol; + symbol32->section_number = fixup_src.section_number; + symbol32->value = fixup_src.value; + symbol32->type = fixup_src.type; + symbol32->storage_class = COFF_SymStorageClass_Static; } else { - InvalidPath; + COFF_Symbol16 *symbol16 = fixup_dst.raw_symbol; + symbol16->section_number = (U16)fixup_src.section_number; + symbol16->value = fixup_src.value; + symbol16->type = fixup_src.type; + symbol16->storage_class = COFF_SymStorageClass_Static; } + + was_symbol_patched[symbol_idx] = 1; } } + ProfEnd(); } -internal void -lnk_patch_weak_external_symbol(B32 is_big_obj, void *symbol, COFF_ParsedSymbol parsed_symbol) +internal +THREAD_POOL_TASK_FUNC(lnk_patch_common_symbols_task) { - COFF_SymbolValueInterpType parsed_symbol_interp = coff_interp_symbol(parsed_symbol.section_number, parsed_symbol.value, parsed_symbol.storage_class); - switch (parsed_symbol_interp) { - case COFF_SymbolValueInterp_Regular: { - if (is_big_obj) { - COFF_Symbol32 *symbol32 = symbol; - symbol32->section_number = parsed_symbol.section_number; - symbol32->value = parsed_symbol.value; - symbol32->type = parsed_symbol.type; - symbol32->storage_class = COFF_SymStorageClass_Static; - } else { - COFF_Symbol16 *symbol16 = symbol; - symbol16->section_number = safe_cast_u16(parsed_symbol.section_number); - symbol16->value = parsed_symbol.value; - symbol16->type = parsed_symbol.type; - symbol16->storage_class = COFF_SymStorageClass_Static; - } - } break; - case COFF_SymbolValueInterp_Common: { - InvalidPath; - } break; - case COFF_SymbolValueInterp_Abs: { - if (is_big_obj) { - COFF_Symbol32 *symbol32 = symbol; - symbol32->section_number = COFF_Symbol_AbsSection32; - symbol32->value = parsed_symbol.value; - symbol32->type = parsed_symbol.type; - symbol32->storage_class = COFF_SymStorageClass_Static; - } else { - COFF_Symbol16 *symbol16 = symbol; - symbol16->section_number = COFF_Symbol_AbsSection16; - symbol16->value = parsed_symbol.value; - symbol16->type = parsed_symbol.type; - symbol16->storage_class = COFF_SymStorageClass_Static; - } - } break; - case COFF_SymbolValueInterp_Weak: - case COFF_SymbolValueInterp_Debug: - case COFF_SymbolValueInterp_Undefined: { - InvalidPath; - } break; - default: { NotImplemented; } break; - } + LNK_BuildImageTask *task = raw_task; + lnk_patch_obj_symtab(task->symtab, task->objs[task_id], task->u.patch_symtabs.was_symbol_patched[task_id], COFF_SymbolValueInterp_Common); +} + +internal +THREAD_POOL_TASK_FUNC(lnk_patch_abs_symbols_task) +{ + LNK_BuildImageTask *task = raw_task; + lnk_patch_obj_symtab(task->symtab, task->objs[task_id], task->u.patch_symtabs.was_symbol_patched[task_id], COFF_SymbolValueInterp_Abs); } internal THREAD_POOL_TASK_FUNC(lnk_patch_undefined_symbols_task) { - LNK_BuildImageTask *task = raw_task; - U64 obj_idx = task_id; - LNK_Obj *obj = task->objs[obj_idx]; - - ProfBeginV("Patch Undefined Symbols [%S]", obj->path); - COFF_ParsedSymbol symbol; - 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); - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - if (interp == COFF_SymbolValueInterp_Undefined) { - if (symbol.storage_class == COFF_SymStorageClass_External) { - LNK_Symbol *defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol.name); - if (defn) { - COFF_ParsedSymbol defn_symbol = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); - - if (defn_symbol.storage_class == COFF_SymStorageClass_WeakExternal) { - continue; - } - - lnk_patch_weak_external_symbol(obj->header.is_big_obj, symbol.raw_symbol, defn_symbol); - } else { - // TODO: collect unresolved undefined - } - } - } - } - ProfEnd(); + LNK_BuildImageTask *task = raw_task; + lnk_patch_obj_symtab(task->symtab, task->objs[task_id], task->u.patch_symtabs.was_symbol_patched[task_id], COFF_SymbolValueInterp_Undefined); } internal -THREAD_POOL_TASK_FUNC(lnk_patch_weak_symbols_with_strong_definition_task) +THREAD_POOL_TASK_FUNC(lnk_patch_weak_symbols_task) { - LNK_BuildImageTask *task = raw_task; - U64 obj_idx = task_id; - LNK_Obj *obj = task->objs[obj_idx]; - - ProfBeginV("Patch Weak Symbols [%S]", obj->path); - COFF_ParsedSymbol symbol; - 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); - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - if (interp == COFF_SymbolValueInterp_Weak) { - LNK_Symbol *defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, symbol.name); - if (defn) { - COFF_ParsedSymbol defn_symbol = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); - COFF_SymbolValueInterpType defn_interp = coff_interp_symbol(defn_symbol.section_number, defn_symbol.value, defn_symbol.storage_class); - if (defn_interp != COFF_SymbolValueInterp_Weak) { - lnk_patch_weak_external_symbol(obj->header.is_big_obj, symbol.raw_symbol, defn_symbol); - } - } - } - } - ProfEnd(); -} - -internal -THREAD_POOL_TASK_FUNC(lnk_patch_weak_symbols_with_fallback_definition_task) -{ - Temp scratch = scratch_begin(&arena, 1); - - LNK_BuildImageTask *task = raw_task; - U64 obj_idx = task_id; - LNK_Obj *obj = task->objs[obj_idx]; - - HashTable *visited_symbols_ht = hash_table_init(scratch.arena, 32); - struct LookupLocation { - struct LookupLocation *next; - LNK_Obj *obj; - U64 symbol_idx; - }; - struct LookupLocation *lookup_first = 0; - struct LookupLocation *lookup_last = 0; - struct LookupLocation *lookup_free_list = 0; - - ProfBegin("Patch Weak Symbols [%S]", obj->path); - COFF_ParsedSymbol symbol; - 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); - - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - if (interp == COFF_SymbolValueInterp_Undefined && symbol.storage_class == COFF_SymStorageClass_External) { - String8 lookup_name = symbol.name; - LNK_Obj *lookup_obj = obj; - U64 lookup_symbol_idx = symbol_idx; - for (;;) { - // lookup definition - LNK_Symbol *defn = lnk_symbol_table_search(task->symtab, LNK_SymbolScope_Defined, lookup_name); - if (defn == 0) { - break; - } - - // not external symbol? patch and move to next symbol - COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, defn->u.defined.symbol_idx); - if (defn_parsed.storage_class != COFF_SymStorageClass_WeakExternal) { - lnk_patch_weak_external_symbol(obj->header.is_big_obj, symbol.raw_symbol, defn_parsed); - break; - } - - // check against cyclic refs - struct LookupLocation *was_visited = 0; - hash_table_search_string_raw(visited_symbols_ht, lookup_name, &was_visited); - if (was_visited != 0) { - Temp temp = temp_begin(scratch.arena); - - String8List list = {0}; - for (struct LookupLocation *l = lookup_first; l != 0; l = l->next) { - COFF_ParsedSymbol loc_symbol = lnk_parsed_symbol_from_coff_symbol_idx(l->obj, l->symbol_idx); - str8_list_pushf(temp.arena, &list, "\t%S Symbol %S (No.%#llx) =>", l->obj->path, loc_symbol.name, l->symbol_idx); - } - { - COFF_ParsedSymbol loc_symbol = lnk_parsed_symbol_from_coff_symbol_idx(was_visited->obj, was_visited->symbol_idx); - str8_list_pushf(temp.arena, &list, "\t%S Symbol %S (No.%#llx)", was_visited->obj->path, loc_symbol.name, was_visited->symbol_idx); - } - - String8 loc_string = str8_list_join(temp.arena, &list, &(StringJoin){.sep = str8_lit("\n") }); - lnk_error_obj(LNK_Error_WeakCycle, obj, "unable to resolve cyclic symbol %S; ref chain:\n%S", symbol.name, loc_string); - - temp_end(temp); - break; - } - struct LookupLocation *loc = lookup_free_list; - if (lookup_free_list) { - SLLStackPop(lookup_free_list); - } else { - loc = push_array(scratch.arena, struct LookupLocation, 1); - } - loc->obj = lookup_obj; - loc->symbol_idx = symbol_idx; - SLLQueuePush(lookup_first, lookup_last, loc); - hash_table_push_string_raw(scratch.arena, visited_symbols_ht, lookup_name, loc); - - // fallback to weak tag for definition - COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(defn_parsed, defn->u.defined.obj->header.is_big_obj); - COFF_ParsedSymbol parsed_tag = lnk_parsed_symbol_from_coff_symbol_idx(defn->u.defined.obj, weak_ext->tag_index); - lookup_name = parsed_tag.name; - lookup_obj = defn->u.defined.obj; - lookup_symbol_idx = weak_ext->tag_index; - } - - hash_table_purge(visited_symbols_ht); - lookup_free_list = lookup_first; - lookup_first = 0; - lookup_last = 0; - } - } - ProfEnd(); - - scratch_end(scratch); -} - -internal -THREAD_POOL_TASK_FUNC(lnk_patch_weak_symbols_with_defined_tags_task) -{ - LNK_BuildImageTask *task = raw_task; - U64 obj_idx = task_id; - LNK_Obj *obj = task->objs[obj_idx]; - - ProfBeginV("Patch Weak Symbols [%S]", obj->path); - COFF_ParsedSymbol symbol; - 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); - COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class); - if (interp == COFF_SymbolValueInterp_Weak) { - COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(symbol, obj->header.is_big_obj); - COFF_ParsedSymbol defn_symbol = lnk_parsed_symbol_from_coff_symbol_idx(obj, weak_ext->tag_index); - COFF_SymbolValueInterpType defn_interp = coff_interp_symbol(defn_symbol.section_number, defn_symbol.value, defn_symbol.storage_class); - if (defn_interp != COFF_SymbolValueInterp_Undefined) { - lnk_patch_weak_external_symbol(obj->header.is_big_obj, symbol.raw_symbol, defn_symbol); - } - } - } - ProfEnd(); + LNK_BuildImageTask *task = raw_task; + lnk_patch_obj_symtab(task->symtab, task->objs[task_id], task->u.patch_symtabs.was_symbol_patched[task_id], COFF_SymbolValueInterp_Weak); } internal U64 @@ -4407,14 +4043,15 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT LNK_Section *common_block_sect = lnk_section_table_search(sectab, str8_lit(".bss"), PE_BSS_SECTION_FLAGS); - LNK_BuildImageTask task = {0}; - task.symtab = symtab; - task.sectab = sectab; - task.objs_count = objs_count; - task.objs = objs; - task.function_pad_min = config->function_pad_min; - task.default_align = coff_default_align_from_machine(config->machine); - task.null_sc = push_array(arena->v[0], LNK_SectionContrib, 1); + LNK_BuildImageTask task = { + .symtab = symtab, + .sectab = sectab, + .objs_count = objs_count, + .objs = objs, + .function_pad_min = config->function_pad_min, + .default_align = coff_default_align_from_machine(config->machine), + .null_sc = push_array(arena->v[0], LNK_SectionContrib, 1), + }; tp_for_parallel(tp, 0, objs_count, lnk_remove_associative_sections_task, &task); @@ -4427,9 +4064,7 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT for EachIndex(worker_id, tp->worker_count) { task.u.gather_sects.defns[worker_id] = hash_table_init(arena->v[0], 128); } ProfEnd(); - ProfBegin("Gather Section Definitions"); - tp_for_parallel(tp, arena, objs_count, lnk_gather_section_definitions_task, &task); - ProfEnd(); + tp_for_parallel_prof(tp, arena, objs_count, lnk_gather_section_definitions_task, &task, "Gather Section Definitions"); ProfBegin("Merge Section Definitions Hash Tables"); for (U64 worker_idx = 1; worker_idx < tp->worker_count; worker_idx += 1) { @@ -4519,45 +4154,40 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT for EachIndex(obj_idx, objs_count) { task.sect_map[obj_idx] = push_array(scratch.arena, LNK_SectionContrib *, objs[obj_idx]->header.section_count_no_null); } ProfEnd(); - ProfBegin("Gather Section Contribs"); - tp_for_parallel(tp, 0, objs_count, lnk_gather_section_contribs_task, &task); - ProfEnd(); + tp_for_parallel_prof(tp, 0, objs_count, lnk_gather_section_contribs_task, &task, "Gather Section Contribs"); // ensure determinism by sorting section contribs in chunks by input index { ProfBegin("Sort Section Contribs"); - U64 total_chunk_count = 0; - LNK_SectionContribChunk **chunks = 0; + + U64 total_chunk_count = 0; { for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { total_chunk_count += sect_n->data.contribs.chunk_count; } + } + + { U64 cursor = 0; - chunks = push_array(scratch.arena, LNK_SectionContribChunk *, total_chunk_count); + task.u.sort_contribs.chunks = push_array(scratch.arena, LNK_SectionContribChunk *, total_chunk_count); for (LNK_SectionNode *sect_n = sectab->list.first; sect_n != 0; sect_n = sect_n->next) { for (LNK_SectionContribChunk *chunk_n = sect_n->data.contribs.first; chunk_n != 0; chunk_n = chunk_n->next) { - chunks[cursor++] = chunk_n; + task.u.sort_contribs.chunks[cursor++] = chunk_n; } } Assert(cursor == total_chunk_count); } - task.u.sort_contribs.chunks = chunks; + tp_for_parallel(tp, 0, total_chunk_count, lnk_sort_contribs_task, &task); + ProfEnd(); } - ProfBegin("Update Section Map With COMDAT Leader Contribs"); - tp_for_parallel(tp, 0, objs_count, lnk_set_comdat_leaders_contribs_task, &task); - ProfEnd(); - - ProfBegin("Gather Obj Symbol Tables Fixups"); - task.obj_symtab_fixups = push_array(scratch.arena, LNK_SymbolTableFixupArray, objs_count); - tp_for_parallel(tp, arena, objs_count, lnk_gather_symtab_fixups_task, &task); - ProfEnd(); + tp_for_parallel_prof(tp, 0, objs_count, lnk_set_comdat_leaders_contribs_task, &task, "Update Section Map With COMDAT Leader Contribs"); if (config->opt_ref == LNK_SwitchState_Yes) { //LNK_Symbol *entry_point_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, config->entry_point_name); - //lnk_gc_sections(objs_count, objs, task.sect_map, task.obj_symtab_fixups, 1, &entry_point_symbol); + //lnk_gc_sections(symtab, objs_count, objs, task.sect_map, task.obj_symtab_fixups, 1, &entry_point_symbol); } // build common block @@ -4568,10 +4198,8 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT { ProfBegin("Build Common Block"); - ProfBegin("Count Contribs"); task.u.common_block.counts = push_array(scratch.arena, U64, tp->worker_count); - tp_for_parallel(tp, 0, tp->worker_count, lnk_count_common_block_contribs_task, &task); - ProfEnd(); + tp_for_parallel_prof(tp, 0, tp->worker_count, lnk_count_common_block_contribs_task, &task, "Count Contribs"); ProfBegin("Push Contribs"); common_block_contribs_count = sum_array_u64(tp->worker_count, task.u.common_block.counts); @@ -4595,7 +4223,7 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT U64 common_block_cursor = common_block_sect->vsize; // compute and assign offsets into the common block - for (U64 contrib_idx = 0; contrib_idx < common_block_contribs_count; contrib_idx += 1) { + for EachIndex(contrib_idx, common_block_contribs_count) { LNK_CommonBlockContrib *contrib = &common_block_contribs[contrib_idx]; U32 size = contrib->u.size; U32 align = Min(32, u64_up_to_pow2(size)); // link.exe caps align at 32 bytes @@ -4620,7 +4248,6 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT ProfEnd(); } - // finalize sections layouts { ProfBegin("Finalize Sections Layout"); @@ -4639,9 +4266,7 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT } if (config->do_function_pad_min == LNK_SwitchState_Yes) { - ProfBegin("Flag Hotpatch Section Contribs"); - tp_for_parallel(tp, arena, objs_count, lnk_flag_hotpatch_contribs_task, &task); - ProfEnd(); + tp_for_parallel_prof(tp, arena, objs_count, lnk_flag_hotpatch_contribs_task, &task, "Flag Hotpatch Section Contribs"); } // assign contribs offsets, sizes, and section indices @@ -4684,39 +4309,29 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT ProfEnd(); } - // patch symbol tables { ProfBegin("Patch Symbol Tables"); Temp temp = temp_begin(scratch.arena); - B8 **was_symbol_patched = push_array(temp.arena, B8 *, objs_count); - for EachIndex(obj_idx, objs_count) { was_symbol_patched[obj_idx] = push_array(temp.arena, B8, objs[obj_idx]->header.symbol_count); } - - task.u.patch_symtabs.was_symbol_patched = was_symbol_patched; + // set up context for patch tasks task.u.patch_symtabs.common_block_sect = common_block_sect; - task.u.patch_symtabs.ranges = tp_divide_work(temp.arena, common_block_contribs_count, tp->worker_count); + task.u.patch_symtabs.common_block_ranges = tp_divide_work(temp.arena, common_block_contribs_count, tp->worker_count); task.u.patch_symtabs.common_block_contribs = common_block_contribs; + task.u.patch_symtabs.was_symbol_patched = push_array(temp.arena, B8 *, objs_count); + for EachIndex(obj_idx, objs_count) { task.u.patch_symtabs.was_symbol_patched[obj_idx] = push_array(temp.arena, B8, objs[obj_idx]->header.symbol_count); } // flag debug symbols to prevent them from being patched in subsequent passes - tp_for_parallel_prof(tp, 0, objs_count, lnk_flag_debug_symbols_task, &task, "Flag Debug Symbols"); - tp_for_parallel_prof(tp, 0, objs_count, lnk_patch_comdat_leaders_task, &task, "Patch COMDAT Leaders"); - tp_for_parallel_prof(tp, 0, tp->worker_count, lnk_patch_common_block_leaders_task, &task, "Patch Common Block Leaders"); - tp_for_parallel_prof(tp, 0, objs_count, lnk_patch_regular_symbols_task, &task, "Patch Regular Symbols"); + tp_for_parallel_prof(tp, 0, objs_count, lnk_flag_debug_symbols_task, &task, "Flag Debug Symbols"); - task.u.patch_symtabs.fixup_type = COFF_SymbolValueInterp_Common; - tp_for_parallel_prof(tp, 0, objs_count, lnk_apply_symtab_fixups_task, &task, "Fixup Common Symbols"); - - task.u.patch_symtabs.fixup_type = COFF_SymbolValueInterp_Abs; - tp_for_parallel_prof(tp, 0, objs_count, lnk_apply_symtab_fixups_task, &task, "Fixup Absolute Symbols"); - - task.u.patch_symtabs.fixup_type = COFF_SymbolValueInterp_Undefined; - tp_for_parallel_prof(tp, 0, objs_count, lnk_apply_symtab_fixups_task, &task, "Fixup Undefined Symbols"); - - task.u.patch_symtabs.fixup_type = COFF_SymbolValueInterp_Weak; - tp_for_parallel_prof(tp, 0, objs_count, lnk_apply_symtab_fixups_task, &task, "Fixup Weak Symbols"); - - task.u.patch_symtabs.fixup_type = COFF_SymbolValueInterp_Undefined; - tp_for_parallel_prof(tp, 0, objs_count, lnk_apply_symtab_fixups_task, &task, "Fixup Undefined Symbols"); + // patch symbols + tp_for_parallel_prof(tp, 0, objs_count, lnk_patch_comdat_leaders_task, &task, "COMDAT Leaders" ); + tp_for_parallel_prof(tp, 0, tp->worker_count, lnk_patch_common_block_leaders_task, &task, "Common Block Leaders"); + tp_for_parallel_prof(tp, 0, objs_count, lnk_patch_regular_symbols_task, &task, "Regular Symbols" ); + tp_for_parallel_prof(tp, 0, objs_count, lnk_patch_common_symbols_task, &task, "Common Symbols" ); + tp_for_parallel_prof(tp, 0, objs_count, lnk_patch_abs_symbols_task, &task, "Absolute Symbols" ); + tp_for_parallel_prof(tp, 0, objs_count, lnk_patch_undefined_symbols_task, &task, "Undefined Symbols" ); + tp_for_parallel_prof(tp, 0, objs_count, lnk_patch_weak_symbols_task, &task, "Weak Symbols" ); + tp_for_parallel_prof(tp, 0, objs_count, lnk_patch_undefined_symbols_task, &task, "Undefined Symbols" ); temp_end(temp); ProfEnd(); @@ -4725,17 +4340,11 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT // section list -> array task.image_sects = lnk_section_array_from_list(scratch.arena, sectab->list); + // assign virtual offsets to sections expected_image_header_size = lnk_compute_win32_image_header_size(config, task.image_sects.count); - - // assign virtual space U64 voff_cursor = AlignPow2(expected_image_header_size + sizeof(COFF_SectionHeader), config->sect_align); - for (U64 i = 0; i < task.image_sects.count; i += 1) { - lnk_assign_section_virtual_space(task.image_sects.v[i], config->sect_align, &voff_cursor); - } - - ProfBegin("Patch Virtual Offsets and SIzes in Obj Section Headers"); - tp_for_parallel(tp, 0, task.objs_count, lnk_patch_virtual_offsets_and_sizes_in_obj_section_headers_task, &task); - ProfEnd(); + for EachIndex(sect_idx, task.image_sects.count) { lnk_assign_section_virtual_space(task.image_sects.v[sect_idx], config->sect_align, &voff_cursor); } + tp_for_parallel_prof(tp, 0, task.objs_count, lnk_patch_virtual_offsets_and_sizes_in_obj_section_headers_task, &task, "Patch Virtual Offsets and Sizes in Obj Section Headers"); // build base relocs if (~config->flags & LNK_ConfigFlag_Fixed) { @@ -4758,35 +4367,25 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT } } - // assign file space + // assign file offsets to sections U64 foff_cursor = AlignPow2(expected_image_header_size, config->file_align); - for (U64 i = 0; i < task.image_sects.count; i += 1) { - lnk_assign_section_file_space(task.image_sects.v[i], &foff_cursor); - } - - ProfBegin("Patch File Offsets And Sizes In Section Headers"); - tp_for_parallel(tp, 0, task.objs_count, lnk_patch_file_offsets_and_sizes_in_obj_section_headers_task, &task); - ProfEnd(); + for EachIndex(sect_idx, task.image_sects.count) { lnk_assign_section_file_space(task.image_sects.v[sect_idx], &foff_cursor); } + tp_for_parallel_prof(tp, 0, task.objs_count, lnk_patch_file_offsets_and_sizes_in_obj_section_headers_task, &task, "Patch File Offsets And Sizes In Section Headers"); } // build win32 image header { - String8List image_header_data = lnk_build_win32_header(sectab->arena, symtab, config, task.image_sects, AlignPow2(expected_image_header_size, config->file_align)); - + String8List image_header_data = lnk_build_win32_header(sectab->arena, symtab, config, task.image_sects, AlignPow2(expected_image_header_size, config->file_align)); LNK_Section *image_header_sect = lnk_section_table_push(sectab, str8_lit(".rad_linker_image_header_section"), 0); LNK_SectionContribChunk *image_header_sc_chunk = lnk_section_contrib_chunk_list_push_chunk(sectab->arena, &image_header_sect->contribs, 1, str8_zero()); LNK_SectionContrib *image_header_sc = lnk_section_contrib_chunk_push(image_header_sc_chunk, 1); - image_header_sc->align = config->file_align; image_header_sc->first_data_node = *image_header_data.first; image_header_sc->last_data_node = image_header_data.last; - lnk_finalize_section_layout(image_header_sect, config->file_align, config->function_pad_min); } - ProfBegin("Patch Section Symbols"); - tp_for_parallel(tp, 0, task.objs_count, lnk_patch_section_symbols_task, &task); - ProfEnd(); + tp_for_parallel_prof(tp, 0, task.objs_count, lnk_patch_section_symbols_task, &task, "Patch Section Symbols"); String8 image_data = {0}; { @@ -4795,14 +4394,12 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT LNK_SectionArray sects = lnk_section_array_from_list(scratch.arena, sectab->list); U64 image_size = 0; - for (U64 sect_idx = 0; sect_idx < sects.count; sect_idx += 1) { - image_size += sects.v[sect_idx]->fsize; - } + for EachIndex(sect_idx, sects.count) { image_size += sects.v[sect_idx]->fsize; } image_data.size = image_size; image_data.str = push_array_no_zero(arena->v[0], U8, image_size); - for (U64 sect_idx = 0; sect_idx < sects.count; sect_idx += 1) { + for EachIndex(sect_idx, sects.count) { LNK_Section *sect = sects.v[sect_idx]; if (~sect->flags & COFF_SectionFlag_CntUninitializedData) { @@ -4815,7 +4412,7 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT // copy section contribution U64 prev_sc_opl = 0; for (LNK_SectionContribChunk *sc_chunk = sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { - for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) { + for EachIndex(sc_idx, sc_chunk->count) { LNK_SectionContrib *sc = sc_chunk->v[sc_idx]; // fill align bytes @@ -4855,14 +4452,8 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT // patch relocs { - ProfBegin("Patch Relocs"); - LNK_ObjRelocPatcher task = {0}; - task.image_data = image_data; - task.objs = objs; - task.image_base = pe.image_base; - task.image_section_table = image_section_table; - tp_for_parallel(tp, 0, objs_count, lnk_obj_reloc_patcher, &task); - ProfEnd(); + LNK_ObjRelocPatcher task = { .image_data = image_data, .objs = objs, .image_base = pe.image_base, .image_section_table = image_section_table }; + tp_for_parallel_prof(tp, 0, objs_count, lnk_obj_reloc_patcher, &task, "Patch Relocs"); } // patch load config @@ -4964,7 +4555,7 @@ lnk_build_image(TP_Arena *arena, TP_Context *tp, LNK_Config *config, LNK_SymbolT U64 tls_align = 0; LNK_Section *tls_sect = lnk_section_table_search(sectab, str8_lit(".tls"), PE_TLS_SECTION_FLAGS); for (LNK_SectionContribChunk *sc_chunk = tls_sect->contribs.first; sc_chunk != 0; sc_chunk = sc_chunk->next) { - for (U64 sc_idx = 0; sc_idx < sc_chunk->count; sc_idx += 1) { + for EachIndex (sc_idx, sc_chunk->count) { Assert(IsPow2(sc_chunk->v[sc_idx]->align)); tls_align = Max(tls_align, sc_chunk->v[sc_idx]->align); } diff --git a/src/linker/lnk.h b/src/linker/lnk.h index 2502a31a..206847e5 100644 --- a/src/linker/lnk.h +++ b/src/linker/lnk.h @@ -25,32 +25,6 @@ typedef struct LNK_ImageContext LNK_SectionTable *sectab; } LNK_ImageContext; -typedef struct LNK_SymbolTableFixup -{ - U32 idx; - U32 obj_idx; - U32 obj_symbol_idx; -} LNK_SymbolTableFixup; - -typedef struct LNK_SymbolTableFixupNode -{ - LNK_SymbolTableFixup data; - struct LNK_SymbolTableFixupNode *next; -} LNK_SymbolTableFixupNode; - -typedef struct LNK_SymbolTableFixupList -{ - U64 count; - LNK_SymbolTableFixupNode *first; - LNK_SymbolTableFixupNode *last; -} LNK_SymbolTableFixupList; - -typedef struct LNK_SymbolTableFixupArray -{ - U64 count; - LNK_SymbolTableFixup *v; -} LNK_SymbolTableFixupArray; - typedef struct LNK_SectionDefinition { String8 name; @@ -108,7 +82,6 @@ typedef struct U64 function_pad_min; U64 default_align; LNK_SectionContrib *null_sc; - LNK_SymbolTableFixupArray *obj_symtab_fixups; LNK_SectionContrib ***sect_map; HashTable *contribs_ht; LNK_SectionArray image_sects; @@ -127,7 +100,7 @@ typedef struct struct { B8 **was_symbol_patched; LNK_Section *common_block_sect; - Rng1U64 *ranges; + Rng1U64 *common_block_ranges; LNK_CommonBlockContrib *common_block_contribs; COFF_SymbolValueInterpType fixup_type; } patch_symtabs; diff --git a/src/linker/lnk_obj.c b/src/linker/lnk_obj.c index 3dd6d0e5..0d161af7 100644 --- a/src/linker/lnk_obj.c +++ b/src/linker/lnk_obj.c @@ -352,7 +352,6 @@ THREAD_POOL_TASK_FUNC(lnk_input_coff_symbol_table) case COFF_SymbolValueInterp_Weak: { LNK_Symbol *defn = lnk_make_defined_symbol(arena, symbol.name, obj, symbol_idx); lnk_symbol_table_push_(task->symtab, arena, worker_id, LNK_SymbolScope_Defined, defn); - lnk_symbol_list_push(arena, &task->weak_lists[worker_id], defn); } break; case COFF_SymbolValueInterp_Common: { @@ -466,6 +465,19 @@ lnk_coff_section_header_from_section_number(LNK_Obj *obj, U64 section_number) return section_header; } +internal B32 +lnk_try_comdat_props_from_section_number(LNK_Obj *obj, U32 section_number, COFF_ComdatSelectType *select_out, U32 *section_length_out, U32 *check_sum_out) +{ + Assert(section_number > 0); + U32 symbol_idx = obj->comdats[section_number-1]; + if (symbol_idx != max_U32) { + COFF_ParsedSymbol secdef = lnk_parsed_symbol_from_coff_symbol_idx(obj, symbol_idx); + coff_parse_secdef(secdef, obj->header.is_big_obj, select_out, 0, section_length_out, check_sum_out); + return 1; + } + return 0; +} + internal B32 lnk_is_coff_section_debug(LNK_Obj *obj, U64 sect_idx) { @@ -533,6 +545,13 @@ lnk_collect_obj_sections(TP_Context *tp, TP_Arena *arena, U64 objs_count, LNK_Ob return task.out_lists; } +internal B32 +lnk_obj_is_before(void *raw_a, void *raw_b) +{ + LNK_Obj *a = raw_a, *b = raw_b; + return a->input_idx < b->input_idx; +} + internal void lnk_parse_msvc_linker_directive(Arena *arena, LNK_Obj *obj, LNK_DirectiveInfo *directive_info, String8 buffer) { diff --git a/src/linker/lnk_obj.h b/src/linker/lnk_obj.h index 3f79365c..5a844569 100644 --- a/src/linker/lnk_obj.h +++ b/src/linker/lnk_obj.h @@ -109,6 +109,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_try_comdat_props_from_section_number(LNK_Obj *obj, U32 section_number, COFF_ComdatSelectType *select_out, U32 *section_length_out, U32 *check_sum_out); internal B32 lnk_is_coff_section_debug(LNK_Obj *obj, U64 sect_idx); // --- Helpers ----------------------------------------------------------------- diff --git a/src/linker/lnk_symbol_table.c b/src/linker/lnk_symbol_table.c index c2313d23..9c732876 100644 --- a/src/linker/lnk_symbol_table.c +++ b/src/linker/lnk_symbol_table.c @@ -39,6 +39,20 @@ lnk_make_undefined_symbol(Arena *arena, String8 name, struct LNK_Obj *obj) return symbol; } +internal B32 +lnk_symbol_defined_is_before(void *raw_a, void *raw_b) +{ + LNK_Symbol *a = raw_a, *b = raw_b; + return a->u.defined.obj->input_idx < b->u.defined.obj->input_idx; +} + +internal B32 +lnk_symbol_lib_is_before(void *raw_a, void *raw_b) +{ + LNK_Symbol *a = raw_a, *b = raw_b; + return a->u.lib.lib->input_idx < b->u.lib.lib->input_idx; +} + internal void lnk_symbol_list_push_node(LNK_SymbolList *list, LNK_SymbolNode *node) { @@ -123,41 +137,27 @@ lnk_error_multiply_defined_symbol(LNK_Symbol *dst, LNK_Symbol *src) internal B32 lnk_can_replace_symbol(LNK_SymbolScope scope, LNK_Symbol *dst, LNK_Symbol *src) { - //Assert(src->type != LNK_Symbol_Undefined); - Assert(dst != src); - Assert(str8_match(dst->name, src->name, 0)); - B32 can_replace = 0; - switch (scope) { - case LNK_SymbolScope_Lib: { - // link.exe picks symbol from lib that is discovered first - can_replace = src->u.lib.lib->input_idx < dst->u.lib.lib->input_idx; - } break; - case LNK_SymbolScope_Import: { - can_replace = 1; - } break; case LNK_SymbolScope_Defined: { - LNK_Obj *dst_obj = dst->u.defined.obj; - LNK_Obj *src_obj = src->u.defined.obj; - - COFF_ParsedSymbol dst_parsed = lnk_parsed_symbol_from_coff_symbol_idx(dst->u.defined.obj, dst->u.defined.symbol_idx); - COFF_ParsedSymbol src_parsed = lnk_parsed_symbol_from_coff_symbol_idx(src->u.defined.obj, src->u.defined.symbol_idx); - - COFF_SymbolValueInterpType dst_interp = coff_interp_symbol(dst_parsed.section_number, dst_parsed.value, dst_parsed.storage_class); - COFF_SymbolValueInterpType src_interp = coff_interp_symbol(src_parsed.section_number, src_parsed.value, src_parsed.storage_class); + LNK_Obj *dst_obj = dst->u.defined.obj; + LNK_Obj *src_obj = src->u.defined.obj; + COFF_ParsedSymbol dst_parsed = lnk_parsed_symbol_from_coff_symbol_idx(dst->u.defined.obj, dst->u.defined.symbol_idx); + COFF_ParsedSymbol src_parsed = lnk_parsed_symbol_from_coff_symbol_idx(src->u.defined.obj, src->u.defined.symbol_idx); + COFF_SymbolValueInterpType dst_interp = coff_interp_from_parsed_symbol(dst_parsed); + COFF_SymbolValueInterpType src_interp = coff_interp_from_parsed_symbol(src_parsed); + // regular vs abs if (dst_interp == COFF_SymbolValueInterp_Regular && src_interp == COFF_SymbolValueInterp_Abs) { lnk_error_multiply_defined_symbol(dst, src); } // abs vs regular - else if ((dst_interp == COFF_SymbolValueInterp_Abs && src_interp == COFF_SymbolValueInterp_Regular) || - (dst_interp == COFF_SymbolValueInterp_Regular && src_interp == COFF_SymbolValueInterp_Abs)) { + else if (dst_interp == COFF_SymbolValueInterp_Abs && src_interp == COFF_SymbolValueInterp_Regular) { lnk_error_multiply_defined_symbol(dst, src); } // abs vs common else if (dst_interp == COFF_SymbolValueInterp_Abs && src_interp == COFF_SymbolValueInterp_Common) { - if (dst->u.defined.obj->input_idx < src->u.defined.obj->input_idx) { + if (lnk_symbol_defined_is_before(dst, src)) { can_replace = 1; } else { lnk_error_multiply_defined_symbol(dst, src); @@ -165,43 +165,35 @@ lnk_can_replace_symbol(LNK_SymbolScope scope, LNK_Symbol *dst, LNK_Symbol *src) } // common vs abs else if (dst_interp == COFF_SymbolValueInterp_Common && src_interp == COFF_SymbolValueInterp_Abs) { - if (dst->u.defined.obj->input_idx < src->u.defined.obj->input_idx) { + if (lnk_symbol_defined_is_before(dst, src)) { lnk_error_multiply_defined_symbol(dst, src); } } + // abs vs abs + else if (dst_interp == COFF_SymbolValueInterp_Abs && src_interp == COFF_SymbolValueInterp_Abs) { + lnk_error_multiply_defined_symbol(dst, src); + } // weak vs weak else if (dst_interp == COFF_SymbolValueInterp_Weak && src_interp == COFF_SymbolValueInterp_Weak) { - can_replace = src->u.defined.obj->input_idx < dst->u.defined.obj->input_idx; + can_replace = lnk_symbol_defined_is_before(src, dst); } - // weak vs abs - else if (dst_interp == COFF_SymbolValueInterp_Weak && src_interp == COFF_SymbolValueInterp_Abs) { + // weak vs regular/abs/common + else if (dst_interp == COFF_SymbolValueInterp_Weak && (src_interp == COFF_SymbolValueInterp_Regular || src_interp == COFF_SymbolValueInterp_Abs || src_interp == COFF_SymbolValueInterp_Common)) { can_replace = 1; } - // abs vs weak - else if (dst_interp == COFF_SymbolValueInterp_Abs && src_interp == COFF_SymbolValueInterp_Weak) { + // regular/abs/common vs weak + else if ((dst_interp == COFF_SymbolValueInterp_Regular || dst_interp == COFF_SymbolValueInterp_Abs || dst_interp == COFF_SymbolValueInterp_Common) && src_interp == COFF_SymbolValueInterp_Weak) { can_replace = 0; } - // weak vs (regular, common, abs) - else if (dst_interp == COFF_SymbolValueInterp_Weak && (src_interp == COFF_SymbolValueInterp_Regular || src_interp == COFF_SymbolValueInterp_Common || src_interp == COFF_SymbolValueInterp_Abs)) { - can_replace = 1; - } - // regular vs weak - else if (dst_interp == COFF_SymbolValueInterp_Regular && src_interp == COFF_SymbolValueInterp_Weak) { - can_replace = 0; - } - // (regular, common) vs (regular, common) + // regular/common vs regular/common else if ((dst_interp == COFF_SymbolValueInterp_Regular || dst_interp == COFF_SymbolValueInterp_Common) && (src_interp == COFF_SymbolValueInterp_Regular || src_interp == COFF_SymbolValueInterp_Common)) { + // parse dst symbol properties B32 dst_is_comdat = 0; COFF_ComdatSelectType dst_select; U32 dst_section_length; U32 dst_check_sum; if (dst_interp == COFF_SymbolValueInterp_Regular) { - U32 dst_comdat_symbol_idx = dst_obj->comdats[dst_parsed.section_number-1]; - if (dst_comdat_symbol_idx != max_U32) { - COFF_ParsedSymbol secdef = lnk_parsed_symbol_from_coff_symbol_idx(dst_obj, dst_comdat_symbol_idx); - coff_parse_secdef(secdef, dst_obj->header.is_big_obj, &dst_select, 0, &dst_section_length, &dst_check_sum); - dst_is_comdat = 1; - } + dst_is_comdat = lnk_try_comdat_props_from_section_number(dst->u.defined.obj, dst_parsed.section_number, &dst_select, &dst_section_length, &dst_check_sum); } else if (dst_interp == COFF_SymbolValueInterp_Common) { dst_select = COFF_ComdatSelect_Largest; dst_section_length = dst_parsed.value; @@ -209,17 +201,13 @@ lnk_can_replace_symbol(LNK_SymbolScope scope, LNK_Symbol *dst, LNK_Symbol *src) dst_is_comdat = 1; } + // parse src symbol properties B32 src_is_comdat = 0; COFF_ComdatSelectType src_select; - U32 src_section_length; + U32 src_section_length, src_checks; U32 src_check_sum; if (src_interp == COFF_SymbolValueInterp_Regular) { - U32 src_comdat_symbol_idx = src_obj->comdats[src_parsed.section_number-1]; - if (src_comdat_symbol_idx != max_U32) { - COFF_ParsedSymbol secdef = lnk_parsed_symbol_from_coff_symbol_idx(src_obj, src_comdat_symbol_idx); - coff_parse_secdef(secdef, src_obj->header.is_big_obj, &src_select, 0, &src_section_length, &src_check_sum); - src_is_comdat = 1; - } + src_is_comdat = lnk_try_comdat_props_from_section_number(src->u.defined.obj, src_parsed.section_number, &src_select, &src_section_length, &src_check_sum); } else if (src_interp == COFF_SymbolValueInterp_Common) { src_select = COFF_ComdatSelect_Largest; src_section_length = src_parsed.value; @@ -228,18 +216,15 @@ lnk_can_replace_symbol(LNK_SymbolScope scope, LNK_Symbol *dst, LNK_Symbol *src) } // regular non-comdat vs communal - if (dst_interp == COFF_SymbolValueInterp_Regular && !dst_is_comdat && - src_interp == COFF_SymbolValueInterp_Common) { + if (dst_interp == COFF_SymbolValueInterp_Regular && !dst_is_comdat && src_interp == COFF_SymbolValueInterp_Common) { can_replace = 0; } // communal vs regular non-comdat - else if (dst_interp == COFF_SymbolValueInterp_Common && - src_interp == COFF_SymbolValueInterp_Regular && !src_is_comdat) { + else if (dst_interp == COFF_SymbolValueInterp_Common && src_interp == COFF_SymbolValueInterp_Regular && !src_is_comdat) { can_replace = 1; } // handle COMDATs else if (dst_is_comdat && src_is_comdat) { - // handle objs compiled with /GR- and /GR if ((src_select == COFF_ComdatSelect_Any && dst_select == COFF_ComdatSelect_Largest)) { src_select = COFF_ComdatSelect_Largest; } @@ -252,7 +237,7 @@ lnk_can_replace_symbol(LNK_SymbolScope scope, LNK_Symbol *dst, LNK_Symbol *src) case COFF_ComdatSelect_Null: case COFF_ComdatSelect_Any: { if (src_section_length == dst_section_length) { - can_replace = src_obj->input_idx < dst_obj->input_idx; + can_replace = lnk_obj_is_before(src_obj, dst_obj); } else { // both COMDATs are valid but to get smaller exe pick smallest can_replace = src_section_length < dst_section_length; @@ -263,19 +248,17 @@ lnk_can_replace_symbol(LNK_SymbolScope scope, LNK_Symbol *dst, LNK_Symbol *src) } break; case COFF_ComdatSelect_SameSize: { if (dst_section_length == src_section_length) { - can_replace = src_obj->input_idx < dst_obj->input_idx; + can_replace = lnk_obj_is_before(src_obj, dst_obj); } else { lnk_error_multiply_defined_symbol(dst, src); } } break; case COFF_ComdatSelect_ExactMatch: { - COFF_SectionHeader *dst_section_table = (COFF_SectionHeader *)str8_substr(dst_obj->data, dst_obj->header.section_table_range).str; - COFF_SectionHeader *src_section_table = (COFF_SectionHeader *)str8_substr(src_obj->data, src_obj->header.section_table_range).str; - COFF_SectionHeader *dst_sect_header = &dst_section_table[dst_parsed.section_number-1]; - COFF_SectionHeader *src_sect_header = &src_section_table[src_parsed.section_number-1]; - String8 dst_data = str8_substr(dst_obj->data, rng_1u64(dst_sect_header->foff, dst_sect_header->foff + dst_sect_header->fsize)); - String8 src_data = str8_substr(src_obj->data, rng_1u64(src_sect_header->foff, src_sect_header->foff + src_sect_header->fsize)); - B32 is_exact_match = 0; + COFF_SectionHeader *dst_sect_header = lnk_coff_section_header_from_section_number(dst_obj, dst_parsed.section_number); + COFF_SectionHeader *src_sect_header = lnk_coff_section_header_from_section_number(src_obj, src_parsed.section_number); + String8 dst_data = str8_substr(dst_obj->data, rng_1u64(dst_sect_header->foff, dst_sect_header->foff + dst_sect_header->fsize)); + String8 src_data = str8_substr(src_obj->data, rng_1u64(src_sect_header->foff, src_sect_header->foff + src_sect_header->fsize)); + B32 is_exact_match = 0; if (dst_check_sum != 0 && src_check_sum != 0) { is_exact_match = dst_check_sum == src_check_sum && str8_match(dst_data, src_data, 0); } else { @@ -283,29 +266,25 @@ lnk_can_replace_symbol(LNK_SymbolScope scope, LNK_Symbol *dst, LNK_Symbol *src) } if (is_exact_match) { - can_replace = src_obj->input_idx < dst_obj->input_idx; + can_replace = lnk_obj_is_before(src_obj, dst_obj); } else { lnk_error_multiply_defined_symbol(dst, src); } } break; case COFF_ComdatSelect_Largest: { if (dst_section_length == src_section_length) { - can_replace = src_obj->input_idx < dst_obj->input_idx; + can_replace = lnk_obj_is_before(src_obj, dst_obj); } else { can_replace = dst_section_length < src_section_length; } } break; - case COFF_ComdatSelect_Associative: { - // ignore - } break; - default: { InvalidPath; } + case COFF_ComdatSelect_Associative: { /* ignore */ } break; + default: { InvalidPath; } break; } } else { - String8 src_select_str = coff_string_from_comdat_select_type(src_select); - String8 dst_select_str = coff_string_from_comdat_select_type(dst_select); lnk_error_obj(LNK_Warning_UnresolvedComdat, src_obj, "%S: COMDAT selection conflict detected, current selection %S, leader selection %S from %S", - src->name, src_select_str, dst_select_str, dst_obj); + src->name, coff_string_from_comdat_select_type(src_select), coff_string_from_comdat_select_type(dst_select), dst_obj); } } else { lnk_error_multiply_defined_symbol(dst, src); @@ -314,9 +293,15 @@ lnk_can_replace_symbol(LNK_SymbolScope scope, LNK_Symbol *dst, LNK_Symbol *src) lnk_error(LNK_Error_InvalidPath, "unable to find a suitable replacement logic for symbol combination"); } } break; + case LNK_SymbolScope_Import: { + lnk_error_multiply_defined_symbol(dst, src); + } break; + case LNK_SymbolScope_Lib: { + // link.exe picks symbol from lib that is discovered first + can_replace = lnk_symbol_lib_is_before(src, dst); + } break; default: { InvalidPath; } } - return can_replace; } @@ -521,6 +506,110 @@ lnk_symbol_table_push_alt_name(LNK_SymbolTable *symtab, LNK_Obj *obj, String8 fr } } +internal +THREAD_POOL_TASK_FUNC(lnk_finalize_weak_symbols_task) +{ + Temp scratch = scratch_begin(&arena,1); + + LNK_FinalizeWeakSymbolsTask *task = raw_task; + LNK_SymbolTable *symtab = task->symtab; + LNK_SymbolHashTrieChunk *chunk = task->chunks[task_id]; + + for EachIndex(i, chunk->count) { + LNK_Symbol *symbol = chunk->v[i].symbol; + COFF_ParsedSymbol symbol_parsed = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); + COFF_SymbolValueInterpType symbol_interp = coff_interp_symbol(symbol_parsed.section_number, symbol_parsed.value, symbol_parsed.storage_class); + if (symbol_interp == COFF_SymbolValueInterp_Weak) { + struct LookupLocation { struct LookupLocation *next; LNK_SymbolDefined symbol; }; + struct LookupLocation *lookup_first = 0, *lookup_last = 0; + + LNK_SymbolDefined current_symbol = symbol->u.defined; + for (;;) { + // guard against self-referencing weak symbols + struct LookupLocation *was_visited = 0; + for (struct LookupLocation *l = lookup_first; l != 0; l = l->next) { + if (MemoryCompare(&l->symbol, ¤t_symbol, sizeof(LNK_SymbolDefined)) == 0) { was_visited = l; break; } + } + if (was_visited) { + Temp temp = temp_begin(scratch.arena); + + String8List ref_list = {0}; + for (struct LookupLocation *l = lookup_first; l != 0; l = l->next) { + COFF_ParsedSymbol loc_symbol = lnk_parsed_symbol_from_coff_symbol_idx(l->symbol.obj, l->symbol.symbol_idx); + str8_list_pushf(temp.arena, &ref_list, "\t%S Symbol %S (No. %#x) =>", l->symbol.obj->path, loc_symbol.name, l->symbol.symbol_idx); + } + COFF_ParsedSymbol loc_symbol = lnk_parsed_symbol_from_coff_symbol_idx(lookup_first->symbol.obj, lookup_first->symbol.symbol_idx); + str8_list_pushf(temp.arena, &ref_list, "\t%S Symbol %S (No. %#x)", lookup_first->symbol.obj->path, loc_symbol.name, lookup_first->symbol.symbol_idx); + + COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx); + String8 loc_string = str8_list_join(temp.arena, &ref_list, &(StringJoin){ .sep = str8_lit("\n") }); + lnk_error_obj(LNK_Error_WeakCycle, symbol->u.defined.obj, "unable to resolve cyclic symbol %S; ref chain:\n%S", parsed_symbol.name, loc_string); + + MemoryZeroStruct(¤t_symbol); + + temp_end(temp); + break; + } + + // record visited symbol + struct LookupLocation *loc = push_array(scratch.arena, struct LookupLocation, 1); + loc->symbol = current_symbol; + SLLQueuePush(lookup_first, lookup_last, loc); + + COFF_ParsedSymbol current_parsed = lnk_parsed_symbol_from_coff_symbol_idx(current_symbol.obj, current_symbol.symbol_idx); + COFF_SymbolValueInterpType current_interp = coff_interp_symbol(current_parsed.section_number, current_parsed.value, current_parsed.storage_class); + if (current_interp != COFF_SymbolValueInterp_Weak && current_interp != COFF_SymbolValueInterp_Undefined) { + break; + } + + // does weak symbol have the strong definition? + LNK_Symbol *defn_symbol = lnk_symbol_table_search(symtab, LNK_SymbolScope_Defined, current_parsed.name); + COFF_ParsedSymbol defn_parsed = lnk_parsed_symbol_from_coff_symbol_idx(defn_symbol->u.defined.obj, defn_symbol->u.defined.symbol_idx); + COFF_SymbolValueInterpType defn_interp = coff_interp_symbol(defn_parsed.section_number, defn_parsed.value, defn_parsed.storage_class); + if (defn_interp != COFF_SymbolValueInterp_Weak) { + current_symbol = defn_symbol->u.defined; + break; + } + + // no strong definition fallback to the tag + COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(current_parsed, current_symbol.obj->header.is_big_obj); + COFF_ParsedSymbol tag_parsed = lnk_parsed_symbol_from_coff_symbol_idx(current_symbol.obj, weak_ext->tag_index); + COFF_SymbolValueInterpType tag_interp = coff_interp_symbol(tag_parsed.section_number, tag_parsed.value, tag_parsed.storage_class); + current_symbol = (LNK_SymbolDefined){ .obj = current_symbol.obj, .symbol_idx = weak_ext->tag_index }; + } + + // replace weak symbol with it's tag + symbol->u.defined = current_symbol; + } + } + + scratch_end(scratch); +} + +internal void +lnk_finalize_weak_symbols(TP_Context *tp, LNK_SymbolTable *symtab) +{ + ProfBeginFunction(); + Temp scratch = scratch_begin(0,0); + + U64 chunks_count = 0; + for EachIndex(worker_id, tp->worker_count) { chunks_count += symtab->chunk_lists[LNK_SymbolScope_Defined][worker_id].count; } + + LNK_SymbolHashTrieChunk **chunks = push_array(scratch.arena, LNK_SymbolHashTrieChunk *, chunks_count); + U64 chunks_cursor = 0; + for EachIndex(worker_id, tp->worker_count) { + for (LNK_SymbolHashTrieChunk *chunk = symtab->chunk_lists[LNK_SymbolScope_Defined][worker_id].first; chunk != 0; chunk = chunk->next) { + chunks[chunks_cursor++] = chunk; + } + } + + LNK_FinalizeWeakSymbolsTask task = { .symtab = symtab, .chunks = chunks }; + tp_for_parallel(tp, 0, chunks_count, lnk_finalize_weak_symbols_task, &task); + + scratch_end(scratch); + ProfEnd(); +} + internal COFF_ParsedSymbol lnk_parsed_symbol_from_defined(LNK_Symbol *symbol) { diff --git a/src/linker/lnk_symbol_table.h b/src/linker/lnk_symbol_table.h index eec6c878..e85490c5 100644 --- a/src/linker/lnk_symbol_table.h +++ b/src/linker/lnk_symbol_table.h @@ -107,6 +107,14 @@ typedef struct LNK_SymbolTable HashTable *alt_names; } LNK_SymbolTable; +// --- Workers Contensts ------------------------------------------------------- + +typedef struct +{ + LNK_SymbolTable *symtab; + LNK_SymbolHashTrieChunk **chunks; +} LNK_FinalizeWeakSymbolsTask; + // --- Symbol Make ------------------------------------------------------------- internal LNK_Symbol * lnk_make_defined_symbol(Arena *arena, String8 name, struct LNK_Obj *obj, U32 symbol_idx); @@ -140,6 +148,8 @@ internal LNK_Symbol * lnk_symbol_table_searchf(LNK_SymbolTable *symtab, LNK internal void lnk_symbol_table_push_alt_name(LNK_SymbolTable *symtab, struct LNK_Obj *obj, String8 from, String8 to); +internal void lnk_finalize_weak_symbols(TP_Context *tp, LNK_SymbolTable *symtab); + // --- Symbol Contrib Helpers -------------------------------------------------- internal ISectOff lnk_sc_from_symbol(LNK_Symbol *symbol); diff --git a/src/torture/torture.c b/src/torture/torture.c index 94dab1a2..6e7641af 100644 --- a/src/torture/torture.c +++ b/src/torture/torture.c @@ -60,13 +60,13 @@ t_string_from_result(T_Result v) return 0; } -global String8 g_stdout_file_name = str8_lit_comp("torture"); +global String8 g_stdout_file_name = str8_lit_comp("torture.out"); global U64 g_linker_time_out; global String8 g_linker; global String8 g_wdir; global String8 g_out = str8_lit_comp("torture"); global B32 g_verbose; -global B32 g_redirect_stdout; +global B32 g_redirect_stdout = 1; #define T_LINKER_TIME_OUT_EXIT_CODE 999999 @@ -743,6 +743,128 @@ exit:; return result; } +internal T_Result +t_weak_vs_weak(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 a_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS, str8_lit("a")); + COFF_ObjSymbol *sect_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("_a"), 0, sect); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, sect_symbol); + a_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 b_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".b"), PE_DATA_SECTION_FLAGS, str8_lit("b")); + COFF_ObjSymbol *sect_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("_b"), 0, sect); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, sect_symbol); + b_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("w")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + if (!t_write_file(str8_lit("a.obj"), a_obj)) { goto exit; } + if (!t_write_file(str8_lit("b.obj"), b_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } + + int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj b.obj entry.obj"); + if (linker_exit_code != 0) { goto exit; } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + +internal T_Result +t_weak_vs_common(void) +{ + Temp scratch = scratch_begin(0,0); + T_Result result = T_Result_Fail; + + String8 weak_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".a"), PE_DATA_SECTION_FLAGS, str8_lit("a")); + COFF_ObjSymbol *sect_symbol = coff_obj_writer_push_symbol_static(obj_writer, str8_lit("_a"), 0, sect); + coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("w"), COFF_WeakExt_SearchLibrary, sect_symbol); + weak_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 common_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + coff_obj_writer_push_symbol_common(obj_writer, str8_lit("w"), 2); + common_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + String8 entry_obj; + { + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + U8 text[] = { + 0x48, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov rax, $imm + 0xC3 // ret + }; + COFF_ObjSection *sect = coff_obj_writer_push_section(obj_writer, str8_lit(".text"), PE_TEXT_SECTION_FLAGS, str8_array_fixed(text)); + coff_obj_writer_push_symbol_extern(obj_writer, str8_lit("entry"), 0, sect); + COFF_ObjSymbol *symbol = coff_obj_writer_push_symbol_undef(obj_writer, str8_lit("w")); + coff_obj_writer_section_push_reloc_voff(obj_writer, sect, 0, symbol); + entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); + coff_obj_writer_release(&obj_writer); + } + + if (!t_write_file(str8_lit("weak.obj"), weak_obj)) { goto exit; } + if (!t_write_file(str8_lit("common.obj"), common_obj)) { goto exit; } + if (!t_write_file(str8_lit("entry.obj"), entry_obj)) { goto exit; } + + int linker_exit_code; + + linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe common.obj weak.obj entry.obj"); + if (linker_exit_code != 0) { goto exit; } + + linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe weak.obj common.obj entry.obj"); + if (linker_exit_code != 0) { goto exit; } + + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; + String8 string_table = str8_substr(exe, pe.string_table_range); + + COFF_SectionHeader *bss = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".bss")); + if (!bss) { goto exit; } + if (bss->fsize != 0) { goto exit; } + if (bss->vsize != 2) { goto exit; } + + result = T_Result_Pass; +exit:; + scratch_end(scratch); + return result; +} + internal T_Result t_abs_vs_weak(void) { @@ -750,8 +872,8 @@ t_abs_vs_weak(void) T_Result result = T_Result_Fail; - U32 abs_value = 0x123; - U8 text_code[] = { 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3 }; + U32 abs_value = 0x123; + U8 text_code[] = { 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3 }; String8 abs_obj; { @@ -777,27 +899,14 @@ t_abs_vs_weak(void) coff_obj_writer_release(&obj_writer); } - { - B32 was_file_written = 0; - was_file_written = t_write_file(str8_lit("abs.obj"), abs_obj); - if (!was_file_written) { - goto exit; - } - was_file_written = t_write_file(str8_lit("text.obj"), text_obj); - if (!was_file_written) { - goto exit; - } - } + if (!t_write_file(str8_lit("abs.obj"), abs_obj)) { goto exit; } + if (!t_write_file(str8_lit("text.obj"), text_obj)) { goto exit; } int abs_vs_weak_exit_code = t_invoke_linker(str8_lit("/subsystem:console /entry:my_entry /out:a.exe abs.obj text.obj")); - if (abs_vs_weak_exit_code != 0) { - goto exit; - } + if (abs_vs_weak_exit_code != 0) { goto exit; } int weak_vs_abs_exit_code = t_invoke_linker(str8_lit("/subsystem:console /entry:my_entry /out:a.exe text.obj abs.obj")); - if (weak_vs_abs_exit_code != 0) { - goto exit; - } + if (weak_vs_abs_exit_code != 0) { goto exit; } String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); @@ -805,18 +914,17 @@ t_abs_vs_weak(void) String8 string_table = str8_substr(exe, pe.string_table_range); COFF_SectionHeader *text_section = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".text")); - if (text_section) { - String8 text_data = str8_substr(exe, rng_1u64(text_section->foff, text_section->foff + text_section->fsize)); - String8 inst = str8_prefix(text_data, 2); - if (str8_match(inst, str8_array(text_code, 2), 0)) { - String8 imm = str8_prefix(str8_skip(text_data, 2), 8); - U64 expected_imm = abs_value; - if (str8_match(imm, str8_struct(&expected_imm), 0)) { - result = T_Result_Pass; - } - } - } - + if (text_section == 0) { goto exit; } + + String8 text_data = str8_substr(exe, rng_1u64(text_section->foff, text_section->foff + text_section->fsize)); + String8 inst = str8_prefix(text_data, 2); + if (!str8_match(inst, str8_array(text_code, 2), 0)) { goto exit; } + + String8 imm = str8_prefix(str8_skip(text_data, 2), 8); + U64 expected_imm = abs_value; + if (!str8_match(imm, str8_struct(&expected_imm), 0)) { goto exit; } + + result = T_Result_Pass; exit:; scratch_end(scratch); return result; @@ -1100,30 +1208,27 @@ internal T_Result t_weak_tag(void) { Temp scratch = scratch_begin(0,0); - T_Result result = T_Result_Fail; - U32 weak_tag_expected_value = 0x12345678; - String8 weak_tag_obj_name = str8_lit("weak_tag.obj"); + U32 weak_tag_expected_value = 0x12345678; + String8 weak_tag_obj_name = str8_lit("weak_tag.obj"); { - COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); + COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); COFF_ObjSymbol *tag_symbol = coff_obj_writer_push_symbol_abs(obj_writer, str8_lit("abs"), weak_tag_expected_value, COFF_SymStorageClass_Static); COFF_ObjSymbol *weak_first = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("strong_first"), COFF_WeakExt_SearchAlias, tag_symbol); COFF_ObjSymbol *weak_second = coff_obj_writer_push_symbol_weak(obj_writer, str8_lit("strong_second"), COFF_WeakExt_SearchAlias, weak_first); - U8 sect_data[] = { 0, 0, 0, 0 }; + U8 sect_data[4] = {0}; COFF_ObjSection *sect = t_push_data_section(obj_writer, str8_array_fixed(sect_data)); coff_obj_writer_section_push_reloc(obj_writer, sect, 0, weak_second, COFF_Reloc_X64_Addr32); String8 weak_tag_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); coff_obj_writer_release(&obj_writer); - if (!t_write_file(weak_tag_obj_name, weak_tag_obj)) { - goto exit; - } + if (!t_write_file(weak_tag_obj_name, weak_tag_obj)) { goto exit; } } - String8 entry_name = str8_lit("my_entry"); - U8 entry_text[] = { 0xC3 }; + String8 entry_name = str8_lit("my_entry"); + U8 entry_text[] = { 0xC3 }; String8 entry_obj_name = str8_lit("entry.obj"); { COFF_ObjWriter *obj_writer = coff_obj_writer_alloc(0, COFF_MachineType_X64); @@ -1131,31 +1236,23 @@ t_weak_tag(void) coff_obj_writer_push_symbol_extern(obj_writer, entry_name, 0, text_sect); String8 entry_obj = coff_obj_writer_serialize(scratch.arena, obj_writer); coff_obj_writer_release(&obj_writer); - if (!t_write_file(entry_obj_name, entry_obj)) { - goto exit; - } + if (!t_write_file(entry_obj_name, entry_obj)) { goto exit; } } int linker_exit_code = t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe %S %S", weak_tag_obj_name, entry_obj_name); - if (linker_exit_code == 0) { - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); - COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; - String8 string_table = str8_substr(exe, pe.string_table_range); + if (linker_exit_code != 0) { goto exit; } - COFF_SectionHeader *data_section = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); - if (!data_section) { - goto exit; - } - if (data_section->vsize != 4) { - goto exit; - } - String8 data = str8_substr(exe, rng_1u64(data_section->foff, data_section->foff + data_section->vsize)); - if (str8_match(data, str8_struct(&weak_tag_expected_value), 0)) { - result = T_Result_Pass; - } - } + String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); + PE_BinInfo pe = pe_bin_info_from_data(scratch.arena, exe); + COFF_SectionHeader *section_table = (COFF_SectionHeader *)str8_substr(exe, pe.section_table_range).str; + String8 string_table = str8_substr(exe, pe.string_table_range); + COFF_SectionHeader *data_section = t_coff_section_header_from_name(string_table, section_table, pe.section_count, str8_lit(".data")); + String8 data = str8_substr(exe, rng_1u64(data_section->foff, data_section->foff + data_section->vsize)); + if (!data_section) { goto exit; } + if (data_section->vsize != 4) { goto exit; } + if (!str8_match(data, str8_struct(&weak_tag_expected_value), 0)) { goto exit; } + result = T_Result_Pass; exit:; scratch_end(scratch); return result; @@ -3670,7 +3767,7 @@ entry_point(CmdLine *cmdline) Temp scratch = scratch_begin(0,0); // - // Targets + // Test Targets // static struct { char *label; T_Result (*r)(void); @@ -3681,6 +3778,8 @@ entry_point(CmdLine *cmdline) { "merge", t_merge }, { "undef_section", t_undef_section }, { "undef_reloc_section", t_undef_reloc_section }, + { "weak_vs_weak", t_weak_vs_weak }, + { "weak_vs_common", t_weak_vs_common }, { "abs_vs_weak", t_abs_vs_weak }, { "abs_vs_regular", t_abs_vs_regular }, { "abs_vs_common", t_abs_vs_common },