linker checkpoint

This commit is contained in:
Nikita Smith
2025-06-25 10:53:22 -07:00
committed by Ryan Fleury
parent a60216fa9f
commit 7c071e7238
74 changed files with 3940 additions and 6441 deletions
+207 -430
View File
@@ -1,128 +1,40 @@
// Copyright (c) 2024 Epic Games Tools
// Copyright (c) 2025 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal void
lnk_init_symbol(LNK_Symbol *symbol, String8 name, LNK_SymbolType type)
{
symbol->name = name;
symbol->type = type;
}
internal void
lnk_init_defined_symbol(LNK_Symbol *symbol, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags)
{
switch (visibility) {
case LNK_DefinedSymbolVisibility_Static: lnk_init_symbol(symbol, name, LNK_Symbol_DefinedStatic); break;
case LNK_DefinedSymbolVisibility_Extern: lnk_init_symbol(symbol, name, LNK_Symbol_DefinedExtern); break;
case LNK_DefinedSymbolVisibility_Internal: lnk_init_symbol(symbol, name, LNK_Symbol_DefinedInternal); break;
}
LNK_DefinedSymbol *def = &symbol->u.defined;
def->flags = flags;
def->value_type = LNK_DefinedSymbolValue_Null;
}
internal void
lnk_init_defined_symbol_chunk(LNK_Symbol *symbol, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, LNK_Chunk *chunk, U64 offset, COFF_ComdatSelectType selection, U32 check_sum)
{
lnk_init_defined_symbol(symbol, name, visibility, flags);
LNK_DefinedSymbol *def = &symbol->u.defined;
def->value_type = LNK_DefinedSymbolValue_Chunk;
def->u.chunk = chunk;
def->u.chunk_offset = offset;
def->u.check_sum = check_sum;
def->u.selection = selection;
}
internal void
lnk_init_defined_symbol_va(LNK_Symbol *symbol, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, U64 va)
{
lnk_init_defined_symbol(symbol, name, visibility, flags);
LNK_DefinedSymbol *def = &symbol->u.defined;
def->value_type = LNK_DefinedSymbolValue_VA;
def->u.va = va;
}
internal void
lnk_init_undefined_symbol(LNK_Symbol *symbol, String8 name, LNK_SymbolScopeFlags scope_flags)
{
lnk_init_symbol(symbol, name, LNK_Symbol_Undefined);
symbol->u.undefined.scope_flags = scope_flags;
}
internal void
lnk_init_weak_symbol(LNK_Symbol *symbol, String8 name, COFF_WeakExtType lookup, LNK_Symbol *fallback)
{
lnk_init_symbol(symbol, name, LNK_Symbol_Weak);
symbol->u.weak.scope_flags = LNK_SymbolScopeFlag_Defined;
symbol->u.weak.lookup_type = lookup;
symbol->u.weak.fallback_symbol = fallback;
}
internal void
lnk_init_lazy_symbol(LNK_Symbol *symbol, String8 name, LNK_Lib *lib, U64 member_offset)
{
lnk_init_symbol(symbol, name, LNK_Symbol_Lazy);
symbol->u.lazy.lib = lib;
symbol->u.lazy.member_offset = member_offset;
}
////////////////////////////////
internal LNK_Symbol *
lnk_make_defined_symbol(Arena *arena, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags)
lnk_make_defined_symbol(Arena *arena, String8 name, struct LNK_Obj *obj, U32 symbol_idx)
{
LNK_Symbol *symbol = push_array_no_zero(arena, LNK_Symbol, 1);
lnk_init_defined_symbol(symbol, name, visibility, flags);
return symbol;
}
internal LNK_Symbol *
lnk_make_defined_symbol_chunk(Arena *arena, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, LNK_Chunk *chunk, U64 offset, COFF_ComdatSelectType selection, U32 check_sum)
{
LNK_Symbol *symbol = push_array_no_zero(arena, LNK_Symbol, 1);
lnk_init_defined_symbol_chunk(symbol, name, visibility, flags, chunk, offset, selection, check_sum);
LNK_Symbol *symbol = push_array(arena, LNK_Symbol, 1);
symbol->name = name;
symbol->type = LNK_Symbol_Defined;
symbol->u.defined.obj = obj;
symbol->u.defined.symbol_idx = symbol_idx;
return symbol;
}
internal LNK_Symbol *
lnk_make_defined_symbol_va(Arena *arena, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, U64 va)
lnk_make_lib_symbol(Arena *arena, String8 name, struct LNK_Lib *lib, U64 member_offset)
{
LNK_Symbol *symbol = push_array_no_zero(arena, LNK_Symbol, 1);
lnk_init_defined_symbol_va(symbol, name, visibility, flags, va);
return symbol;
}
internal LNK_Symbol *
lnk_make_undefined_symbol(Arena *arena, String8 name, LNK_SymbolScopeFlags flags)
{
LNK_Symbol *symbol = push_array_no_zero(arena, LNK_Symbol, 1);
lnk_init_undefined_symbol(symbol, name, flags);
LNK_Symbol *symbol = push_array(arena, LNK_Symbol, 1);
symbol->name = name;
symbol->type = LNK_Symbol_Lib;
symbol->u.lib.lib = lib;
symbol->u.lib.member_offset = member_offset;
return symbol;
}
internal LNK_Symbol *
lnk_make_weak_symbol(Arena *arena, String8 name, COFF_WeakExtType lookup, LNK_Symbol *fallback)
lnk_make_undefined_symbol(Arena *arena, String8 name, struct LNK_Obj *obj)
{
LNK_Symbol *symbol = push_array_no_zero(arena, LNK_Symbol, 1);
lnk_init_weak_symbol(symbol, name, lookup, fallback);
LNK_Symbol *symbol = push_array(arena, LNK_Symbol, 1);
symbol->name = name;
symbol->type = LNK_Symbol_Undefined;
symbol->u.undef.obj = obj;
return symbol;
}
internal LNK_Symbol *
lnk_make_lazy_symbol(Arena *arena, String8 name, LNK_Lib *lib, U64 member_offset)
{
LNK_Symbol *symbol = push_array_no_zero(arena, LNK_Symbol, 1);
lnk_init_lazy_symbol(symbol, name, lib, member_offset);
return symbol;
}
internal LNK_Chunk *
lnk_chunk_from_symbol(LNK_Symbol *symbol)
{
if (LNK_Symbol_IsDefined(symbol->type) && symbol->u.defined.value_type == LNK_DefinedSymbolValue_Chunk) {
return symbol->u.defined.u.chunk;
}
return 0;
}
////////////////////////////////
internal void
@@ -147,24 +59,6 @@ lnk_symbol_list_concat_in_place(LNK_SymbolList *list, LNK_SymbolList *to_concat)
SLLConcatInPlace(list, to_concat);
}
internal LNK_SymbolNode *
lnk_symbol_list_search_node(LNK_SymbolList list, String8 name, StringMatchFlags flags)
{
for (LNK_SymbolNode *node = list.first; node != 0; node = node->next) {
if (str8_match(node->data->name, name, flags)) {
return node;
}
}
return 0;
}
internal LNK_Symbol *
lnk_symbol_list_search(LNK_SymbolList list, String8 name, StringMatchFlags flags)
{
LNK_SymbolNode *node = lnk_symbol_list_search_node(list, name, flags);
return node ? node->data : 0;
}
internal LNK_SymbolList
lnk_symbol_list_from_array(Arena *arena, LNK_SymbolArray arr)
{
@@ -205,6 +99,48 @@ lnk_symbol_array_from_list(Arena *arena, LNK_SymbolList list)
////////////////////////////////
internal ISectOff
lnk_sc_from_symbol(LNK_Symbol *symbol)
{
COFF_ParsedSymbol parsed_symbol = lnk_parsed_symbol_from_coff_symbol_idx(symbol->u.defined.obj, symbol->u.defined.symbol_idx);
ISectOff sc = {0};
sc.isect = parsed_symbol.section_number;
sc.off = parsed_symbol.value;
return sc;
}
internal U64
lnk_isect_from_symbol(LNK_Symbol *symbol)
{
return lnk_sc_from_symbol(symbol).isect;
}
internal U64
lnk_sect_off_from_symbol(LNK_Symbol *symbol)
{
return lnk_sc_from_symbol(symbol).off;
}
internal U64
lnk_virt_off_from_symbol(COFF_SectionHeader **section_table, LNK_Symbol *symbol)
{
ISectOff sc = lnk_sc_from_symbol(symbol);
U64 voff = section_table[sc.isect]->voff + sc.off;
return voff;
}
internal U64
lnk_file_off_from_symbol(COFF_SectionHeader **section_table, LNK_Symbol *symbol)
{
ISectOff sc = lnk_sc_from_symbol(symbol);
U64 foff = section_table[sc.isect]->foff + sc.off;
return foff;
}
////////////////////////////////
internal LNK_SymbolHashTrie *
lnk_symbol_hash_trie_chunk_list_push(Arena *arena, LNK_SymbolHashTrieChunkList *list, U64 cap)
{
@@ -229,120 +165,148 @@ lnk_can_replace_symbol(LNK_Symbol *dst, LNK_Symbol *src)
B32 can_replace = 0;
// lazy vs lazy
if (dst->type == LNK_Symbol_Lazy && src->type == LNK_Symbol_Lazy) {
// lib vs lib
if (dst->type == LNK_Symbol_Lib && src->type == LNK_Symbol_Lib) {
// link.exe picks symbol from lib that is discovered first
LNK_Lib *dst_lib = dst->u.lazy.lib;
LNK_Lib *src_lib = src->u.lazy.lib;
can_replace = dst_lib->input_idx > src_lib->input_idx;
}
// lazy vs weak
else if (dst->type == LNK_Symbol_Lazy && (LNK_Symbol_IsDefined(src->type) || src->type == LNK_Symbol_Weak)) {
can_replace = 1;
}
// weak vs strong
else if (dst->type == LNK_Symbol_Weak && LNK_Symbol_IsDefined(src->type)) {
can_replace = 1;
}
// weak vs weak
else if (dst->type == LNK_Symbol_Weak && src->type == LNK_Symbol_Weak) {
B32 is_fallback_same = str8_match(dst->u.weak.fallback_symbol->name, src->u.weak.fallback_symbol->name, 0);
if (is_fallback_same) {
if (src->obj && !dst->obj) {
can_replace = 1;
} else if (src->obj && dst->obj) {
can_replace = src->obj->input_idx < dst->obj->input_idx;
}
} else {
lnk_error(LNK_Error_MultiplyDefinedSymbol, "multiply defined weak symbol %S, symbol defined in:", src->name);
lnk_supplement_error("%S", dst->obj->path);
lnk_supplement_error("%S", src->obj->path);
}
can_replace = src->u.lib.lib->input_idx < dst->u.lib.lib->input_idx;
}
else if (dst->type == LNK_Symbol_Import) {
AssertAlways(src->type != LNK_Symbol_Import);
can_replace = 1;
}
// defined VA vs defined chunk
else if (LNK_Symbol_IsDefined(dst->type) && dst->u.defined.value_type == LNK_DefinedSymbolValue_VA &&
LNK_Symbol_IsDefined(src->type)) {
can_replace = 1;
}
// defined chunk vs defined chunk
else if (LNK_Symbol_IsDefined(dst->type) && dst->u.defined.value_type == LNK_DefinedSymbolValue_Chunk &&
LNK_Symbol_IsDefined(src->type) && src->u.defined.value_type == LNK_DefinedSymbolValue_Chunk) {
LNK_DefinedSymbol *dst_defn = &dst->u.defined;
LNK_DefinedSymbol *src_defn = &src->u.defined;
// defined vs defined
else if (dst->type == LNK_Symbol_Defined && src->type == LNK_Symbol_Defined) {
LNK_Obj *dst_obj = dst->u.defined.obj;
LNK_Obj *src_obj = src->u.defined.obj;
Assert(dst_defn->u.chunk->is_discarded == 0);
Assert(dst_defn->u.chunk->type == LNK_Chunk_Leaf);
Assert(src_defn->u.chunk->type == LNK_Chunk_Leaf);
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_ComdatSelectType dst_select = dst_defn->u.selection;
COFF_ComdatSelectType src_select = src_defn->u.selection;
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);
// handle objs compiled with /GR- and /GR
if ((src_select == COFF_ComdatSelect_Any && dst_select == COFF_ComdatSelect_Largest) ||
(src_select == COFF_ComdatSelect_Largest && dst_select == COFF_ComdatSelect_Any)) {
dst_select = COFF_ComdatSelect_Largest;
src_select = COFF_ComdatSelect_Largest;
if (dst_interp == COFF_SymbolValueInterp_Regular && src_interp == COFF_SymbolValueInterp_Abs) {
lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, dst->u.defined.obj, "symbol \"%S\" (No. %#x) is multiply defined in %S (No. %#x)", dst->name, dst->u.defined.symbol_idx, src->u.defined.obj->path, src->u.defined.symbol_idx);
}
// 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)) {
lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, dst->u.defined.obj, "symbol \"%S\" (No. %#x) is multiply defined in %S (No. %#x)", dst->name, dst->u.defined.symbol_idx, src->u.defined.obj->path, src->u.defined.symbol_idx);
}
// 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) {
can_replace = 1;
} else {
lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, dst->u.defined.obj, "symbol \"%S\" (No. %#x) is multiply defined in %S (No. %#x)", dst->name, dst->u.defined.symbol_idx, src->u.defined.obj->path, src->u.defined.symbol_idx);
}
}
// 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) {
lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, dst->u.defined.obj, "symbol \"%S\" (No. %#x) is multiply defined in %S (No. %#x)", dst->name, dst->u.defined.symbol_idx, src->u.defined.obj->path, src->u.defined.symbol_idx);
}
}
// weak vs weak
else if (dst_interp == COFF_SymbolValueInterp_Weak && src_interp == COFF_SymbolValueInterp_Weak) {
}
// weak vs abs
else if (dst_interp == COFF_SymbolValueInterp_Weak && src_interp == COFF_SymbolValueInterp_Abs) {
can_replace = 1;
}
// abs vs weak
else if (dst_interp == COFF_SymbolValueInterp_Abs && 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,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)) {
COFF_ComdatSelectType dst_select;
U32 dst_section_length;
U32 dst_check_sum;
if (dst_interp == COFF_SymbolValueInterp_Regular) {
coff_parse_secdef(dst_parsed, dst_obj->header.is_big_obj, &dst_select, 0, &dst_section_length, &dst_check_sum);
} else {
dst_select = COFF_ComdatSelect_Largest;
dst_section_length = dst_parsed.value;
dst_check_sum = 0;
}
if (src_select == dst_select) {
LNK_Chunk *dst_chunk = dst_defn->u.chunk;
LNK_Chunk *src_chunk = src_defn->u.chunk;
U64 dst_chunk_size = lnk_chunk_get_size(dst_chunk);
U64 src_chunk_size = lnk_chunk_get_size(src_chunk);
COFF_ComdatSelectType src_select;
U32 src_section_length;
U32 src_check_sum;
if (src_interp == COFF_SymbolValueInterp_Regular) {
coff_parse_secdef(src_parsed, src_obj->header.is_big_obj, &src_select, 0, &src_section_length, &src_check_sum);
} else {
src_select = COFF_ComdatSelect_Largest;
src_section_length = src_parsed.value;
src_check_sum = 0;
}
switch (src_select) {
case COFF_ComdatSelect_Null:
case COFF_ComdatSelect_Any: {
if (src_chunk_size == dst_chunk_size) {
can_replace = src_chunk->input_idx < dst_chunk->input_idx;
} else {
// both COMDATs are valid but to get smaller exe pick smallest
can_replace = src_chunk_size < dst_chunk_size;
}
} break;
case COFF_ComdatSelect_NoDuplicates: {
lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, src->obj, "multiply defined symbol %S in %S.", dst->name, dst->obj->path);
} break;
case COFF_ComdatSelect_SameSize: {
if (dst_chunk_size != src_chunk_size) {
lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, src->obj, "multiply defined symbol %S in %S.", dst->name, dst->obj->path);
}
} break;
case COFF_ComdatSelect_ExactMatch: {
if (dst_defn->u.check_sum != src_defn->u.check_sum) {
lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, src->obj, "multiply defined symbol %S in %S.", dst->name, dst->obj->path);
}
} break;
case COFF_ComdatSelect_Largest: {
if (dst_chunk_size == src_chunk_size) {
if (dst_defn->u.chunk->u.leaf.str == 0 && src_defn->u.chunk->u.leaf.size > 0) {
// handle communal variable
//
// MSVC CRT relies on this behaviour (e.g. __scrt_ucrt_dll_is_in_use in ucrt_detection.c)
can_replace = 1;
// handle objs compiled with /GR- and /GR
if ((src_select == COFF_ComdatSelect_Any && dst_select == COFF_ComdatSelect_Largest) ||
(src_select == COFF_ComdatSelect_Largest && dst_select == COFF_ComdatSelect_Any)) {
dst_select = COFF_ComdatSelect_Largest;
src_select = COFF_ComdatSelect_Largest;
}
if (src_select == dst_select) {
switch (src_select) {
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;
} else {
can_replace = src_chunk->input_idx < dst_chunk->input_idx;
// both COMDATs are valid but to get smaller exe pick smallest
can_replace = src_section_length < dst_section_length;
}
} else {
can_replace = dst_chunk_size < src_chunk_size;
} break;
case COFF_ComdatSelect_NoDuplicates: {
lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, src_obj, "multiply defined symbol %S in %S.", dst->name, dst_obj->path);
} break;
case COFF_ComdatSelect_SameSize: {
if (dst_section_length != src_section_length) {
lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, src_obj, "multiply defined symbol %S in %S.", dst->name, dst_obj->path);
}
} break;
case COFF_ComdatSelect_ExactMatch: {
if (dst_check_sum != src_check_sum) {
lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, src_obj, "multiply defined symbol %S in %S.", dst->name, dst_obj->path);
}
} break;
case COFF_ComdatSelect_Largest: {
if (dst_section_length == src_section_length) {
if (dst_interp == COFF_SymbolValueInterp_Common) {
// handle communal variable
//
// MSVC CRT relies on this behaviour (e.g. __scrt_ucrt_dll_is_in_use in ucrt_detection.c)
can_replace = 1;
} else {
can_replace = src_obj->input_idx < dst_obj->input_idx;
}
} else {
can_replace = dst_section_length < src_section_length;
}
} break;
case COFF_ComdatSelect_Associative: {
// ignore
} break;
default: { InvalidPath; }
}
} break;
case COFF_ComdatSelect_Associative: {
// ignore
} break;
default: {
lnk_error_obj(LNK_Error_InvalidPath, src->obj, "unknown COMDAT selection %#x", src->obj, src_select);
} 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);
}
} else {
String8 src_select_str = coff_string_from_comdat_select_type(src_defn->u.selection);
String8 dst_select_str = coff_string_from_comdat_select_type(dst_defn->u.selection);
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->path);
lnk_error(LNK_Error_InvalidPath, "unable to find a suitable replacement logic for symbol combination");
}
} else {
lnk_error(LNK_Error_InvalidPath, "unable to find a suitable replacement logic for symbol combination");
@@ -355,33 +319,12 @@ internal void
lnk_on_symbol_replace(LNK_Symbol *dst, LNK_Symbol *src)
{
Assert(dst != src);
if (dst->type == LNK_Symbol_Lazy && src->type == LNK_Symbol_Lazy) {
dst->u.lazy = src->u.lazy;
} else if (LNK_Symbol_IsDefined(dst->type)) {
LNK_DefinedSymbol *dst_defined = &dst->u.defined;
if (dst_defined->value_type == LNK_DefinedSymbolValue_Chunk) {
// discard chunk from output
dst_defined->u.chunk->is_discarded = 1;
if (LNK_Symbol_IsDefined(src->type)) {
LNK_DefinedSymbol *src_defined = &src->u.defined;
if (src_defined->value_type == LNK_DefinedSymbolValue_Chunk) {
// static symbols that are not part of obj's symbol table might point to discarded chunk
dst_defined->u.chunk->ref = src_defined->u.chunk->ref;
// copy offset because after folding COMDATS we might end
// up with larger sized chunk and, for instance, a vftable
// might have a function pointer preceeding lead symbol
dst_defined->u.chunk = src_defined->u.chunk;
dst_defined->u.chunk_offset = src_defined->u.chunk_offset;
}
} else {
InvalidPath;
}
}
if (dst->type == LNK_Symbol_Lib && src->type == LNK_Symbol_Lib) {
dst->u.lib = src->u.lib;
} else if (dst->type == LNK_Symbol_Defined && src->type == LNK_Symbol_Defined) {
dst->u.defined = src->u.defined;
} else {
InvalidPath;
}
}
@@ -501,38 +444,28 @@ lnk_symbol_table_init(TP_Arena *arena)
{
LNK_SymbolTable *symtab = push_array(arena->v[0], LNK_SymbolTable, 1);
symtab->arena = arena;
for (U64 i = 0; i < LNK_SymbolScopeIndex_Count; ++i) {
for (U64 i = 0; i < LNK_SymbolScope_Count; ++i) {
symtab->chunk_lists[i] = push_array(arena->v[0], LNK_SymbolHashTrieChunkList, arena->count);
}
return symtab;
}
internal LNK_Symbol *
lnk_symbol_table_search_hash(LNK_SymbolTable *symtab, LNK_SymbolScopeFlags scope_flags, U64 hash, String8 name)
lnk_symbol_table_search_hash(LNK_SymbolTable *symtab, LNK_SymbolScope scope, U64 hash, String8 name)
{
LNK_Symbol *result = 0;
while (scope_flags) {
LNK_SymbolScopeIndex scope_idx = ctz64(scope_flags);
scope_flags &= scope_flags - 1;
LNK_SymbolHashTrie *match = lnk_symbol_hash_trie_search(symtab->scopes[scope_idx], hash, name);
if (match) {
result = match->symbol;
break;
}
}
return result;
LNK_SymbolHashTrie *trie = lnk_symbol_hash_trie_search(symtab->scopes[scope], hash, name);
return trie ? trie->symbol : 0;
}
internal LNK_Symbol *
lnk_symbol_table_search(LNK_SymbolTable *symtab, LNK_SymbolScopeFlags scope, String8 name)
lnk_symbol_table_search(LNK_SymbolTable *symtab, LNK_SymbolScope scope, String8 name)
{
U64 hash = lnk_symbol_hash(name);
return lnk_symbol_table_search_hash(symtab, scope, hash, name);
}
internal LNK_Symbol *
lnk_symbol_table_searchf(LNK_SymbolTable *symtab, LNK_SymbolScopeFlags scope_flags, char *fmt, ...)
lnk_symbol_table_searchf(LNK_SymbolTable *symtab, LNK_SymbolScope scope, char *fmt, ...)
{
Temp scratch = scratch_begin(0, 0);
@@ -541,16 +474,16 @@ lnk_symbol_table_searchf(LNK_SymbolTable *symtab, LNK_SymbolScopeFlags scope_fla
String8 name = push_str8fv(scratch.arena, fmt, args);
va_end(args);
LNK_Symbol *symbol = lnk_symbol_table_search(symtab, scope_flags, name);
LNK_Symbol *symbol = lnk_symbol_table_search(symtab, scope, name);
scratch_end(scratch);
return symbol;
}
internal void
lnk_symbol_table_push_(LNK_SymbolTable *symtab, Arena *arena, LNK_SymbolHashTrieChunkList *chunk_list, LNK_SymbolScopeIndex scope_idx, U64 hash, LNK_Symbol *symbol)
lnk_symbol_table_push_(LNK_SymbolTable *symtab, Arena *arena, U64 worker_id, LNK_SymbolScope scope, U64 hash, LNK_Symbol *symbol)
{
lnk_symbol_hash_trie_insert_or_replace(arena, chunk_list, &symtab->scopes[scope_idx], hash, symbol);
lnk_symbol_hash_trie_insert_or_replace(arena, &symtab->chunk_lists[scope][worker_id], &symtab->scopes[scope], hash, symbol);
}
internal void
@@ -558,29 +491,10 @@ lnk_symbol_table_push_hash(LNK_SymbolTable *symtab, U64 hash, LNK_Symbol *symbol
{
switch (symbol->type) {
case LNK_Symbol_Null: break;
case LNK_Symbol_Import:
case LNK_Symbol_DefinedExtern: {
lnk_symbol_table_push_(symtab, symtab->arena->v[0], &symtab->chunk_lists[LNK_SymbolScopeIndex_Defined][0], LNK_SymbolScopeIndex_Defined, hash, symbol);
} break;
case LNK_Symbol_DefinedInternal: {
lnk_symbol_table_push_(symtab, symtab->arena->v[0], &symtab->chunk_lists[LNK_SymbolScopeIndex_Internal][0], LNK_SymbolScopeIndex_Internal, hash, symbol);
} break;
case LNK_Symbol_Weak: {
lnk_symbol_table_push_(symtab, symtab->arena->v[0], &symtab->chunk_lists[LNK_SymbolScopeIndex_Weak][0], LNK_SymbolScopeIndex_Weak, hash, symbol);
} break;
case LNK_Symbol_Lazy: {
lnk_symbol_table_push_(symtab, symtab->arena->v[0], &symtab->chunk_lists[LNK_SymbolScopeIndex_Lib][0], LNK_SymbolScopeIndex_Lib, hash, symbol);
} break;
// symbols not supported
case LNK_Symbol_Undefined:
case LNK_Symbol_DefinedStatic: {
InvalidPath;
} break;
case LNK_Symbol_Defined:
case LNK_Symbol_Import: { lnk_symbol_table_push_(symtab, symtab->arena->v[0], 0, LNK_SymbolScope_Defined, hash, symbol); } break;
case LNK_Symbol_Lib: { lnk_symbol_table_push_(symtab, symtab->arena->v[0], 0, LNK_SymbolScope_Lib, hash, symbol); } break;
default: { InvalidPath; } break;
}
}
@@ -592,7 +506,7 @@ lnk_symbol_table_push(LNK_SymbolTable *symtab, LNK_Symbol *symbol)
}
internal void
lnk_symbol_table_remove(LNK_SymbolTable *symtab, LNK_SymbolScopeIndex scope, String8 name)
lnk_symbol_table_remove(LNK_SymbolTable *symtab, LNK_SymbolScope scope, String8 name)
{
U64 hash = lnk_symbol_hash(name);
LNK_SymbolHashTrie *trie = lnk_symbol_hash_trie_search(symtab->scopes[scope], hash, name);
@@ -601,141 +515,4 @@ lnk_symbol_table_remove(LNK_SymbolTable *symtab, LNK_SymbolScopeIndex scope, Str
}
}
internal LNK_Symbol *
lnk_symbol_table_push_defined_chunk(LNK_SymbolTable *symtab, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, LNK_Chunk *chunk, U64 offset, COFF_ComdatSelectType selection, U32 check_sum)
{
LNK_Symbol *symbol = lnk_make_defined_symbol_chunk(symtab->arena->v[0], name, visibility, flags, chunk, offset, selection, check_sum);
lnk_symbol_table_push(symtab, symbol);
return symbol;
}
internal LNK_Symbol *
lnk_symbol_table_push_defined(LNK_SymbolTable *symtab, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags)
{
LNK_Symbol *symbol = lnk_make_defined_symbol(symtab->arena->v[0], name, visibility, flags);
lnk_symbol_table_push(symtab, symbol);
return symbol;
}
internal LNK_Symbol *
lnk_symbol_table_push_defined_va(LNK_SymbolTable *symtab, String8 name, LNK_DefinedSymbolVisibility visibility, LNK_DefinedSymbolFlags flags, U64 va)
{
LNK_Symbol *symbol = lnk_make_defined_symbol_va(symtab->arena->v[0], name, visibility, flags, va);
lnk_symbol_table_push(symtab, symbol);
return symbol;
}
internal LNK_Symbol *
lnk_symbol_table_push_weak(LNK_SymbolTable *symtab, String8 weak_name, COFF_WeakExtType lookup, String8 strong_name)
{
weak_name = push_str8_copy(symtab->arena->v[0], weak_name);
strong_name = push_str8_copy(symtab->arena->v[0], strong_name);
LNK_Symbol *strong_symbol = lnk_make_undefined_symbol(symtab->arena->v[0], strong_name, LNK_SymbolScopeFlag_Main);
LNK_Symbol *weak_symbol = lnk_make_weak_symbol(symtab->arena->v[0], weak_name, COFF_WeakExt_SearchAlias, strong_symbol);
lnk_symbol_table_push(symtab, weak_symbol);
return weak_symbol;
}
internal LNK_Symbol *
lnk_resolve_symbol(LNK_SymbolTable *symtab, LNK_Symbol *resolve_symbol)
{
LNK_Symbol *symbol = resolve_symbol;
B32 run_resolver;
do {
run_resolver = 0;
switch (symbol->type) {
case LNK_Symbol_Null: break;
case LNK_Symbol_Undefined: {
LNK_UndefinedSymbol *undef_symbol = &symbol->u.undefined;
LNK_Symbol *def = lnk_symbol_table_search(symtab, undef_symbol->scope_flags, symbol->name);
if (def) {
symbol = def;
run_resolver = 1;
}
} break;
case LNK_Symbol_Weak: {
LNK_WeakSymbol *weak = &symbol->u.weak;
LNK_Symbol *def = lnk_symbol_table_search(symtab, weak->scope_flags, symbol->name);
if (def) {
Assert(LNK_Symbol_IsDefined(def->type));
symbol = def;
} else {
symbol = symbol->u.weak.fallback_symbol;
}
run_resolver = 1;
} break;
case LNK_Symbol_DefinedExtern: {
// search for defined symbol because we don't update symbol pointers in relocations
// whenver we replace them in the symbol table
symbol = lnk_symbol_table_search(symtab, LNK_SymbolScopeFlag_Defined, symbol->name);
Assert(symbol);
} break;
case LNK_Symbol_DefinedStatic:
case LNK_Symbol_DefinedInternal: {
// symbol resolved
} break;
default: NotImplemented;
}
} while (run_resolver);
return symbol;
}
#if 0
internal void
lnk_symbol_hash_trie_debug(LNK_SymbolHashTrie *root)
{
Temp scratch = scratch_begin(0,0);
struct Stack {
struct Stack *next;
U64 i;
LNK_SymbolHashTrie *trie;
};
struct Stack *stack = push_array(scratch.arena, struct Stack, 1);
stack->i = 0;
stack->trie = root;
U64 cur_depth = 1;
U64 max_depth = 0;
char *dashes = "--------------------------------";
FILE *f = fopen("trie.txt", "w");
while (stack) {
for (; stack->i < ArrayCount(stack->trie->child); ++stack->i) {
if (stack->i == 0 && stack->trie->symbol) {
fprintf(f, "%.*s%.*s\n", (int)cur_depth, dashes, str8_varg(stack->trie->symbol->name));
}
if (stack->trie->child[stack->i] != 0) {
struct Stack *frame = push_array(scratch.arena, struct Stack, 1);
frame->i = 0;
frame->trie = stack->trie->child[stack->i];
stack->i += 1;
SLLStackPush(stack, frame);
cur_depth += 1;
max_depth = Max(cur_depth, max_depth);
break;
}
}
if (stack->i >= ArrayCount(stack->trie->child)) {
cur_depth -= 1;
SLLStackPop(stack);
}
}
fprintf(f, "Max Depth: %llu\n", max_depth);
fclose(f);
scratch_end(scratch);
}
#endif