factor out weak symbol resolution logic and apply it to the symbol table

when building link context
This commit is contained in:
Nikita Smith
2025-07-15 14:31:49 -07:00
committed by Ryan Fleury
parent 7cd5a670e3
commit d32667546d
9 changed files with 516 additions and 727 deletions
+6
View File
@@ -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)
{
+1
View File
@@ -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);
+150 -559
View File
@@ -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);
}
+1 -28
View File
@@ -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;
+20 -1
View File
@@ -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)
{
+1
View File
@@ -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 -----------------------------------------------------------------
+164 -75
View File
@@ -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, &current_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(&current_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)
{
+10
View File
@@ -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);
+163 -64
View File
@@ -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 },