cleaned up a bit symbol replacement logic

This commit is contained in:
Nikita Smith
2025-03-31 22:47:57 -07:00
parent bca5169447
commit 03a87fd4ee
3 changed files with 131 additions and 134 deletions
+1 -1
View File
@@ -31,10 +31,10 @@ typedef enum
LNK_Error_LoadRes, LNK_Error_LoadRes,
LNK_Error_IO, LNK_Error_IO,
LNK_Error_LargeAddrAwareRequired, LNK_Error_LargeAddrAwareRequired,
LNK_Error_InvalidPath,
LNK_Error_StopLast, LNK_Error_StopLast,
LNK_Error_First, LNK_Error_First,
LNK_Error_InvalidPath,
LNK_Error_AlreadyDefinedSymbol, LNK_Error_AlreadyDefinedSymbol,
LNK_Error_AlternateNameConflict, LNK_Error_AlternateNameConflict,
LNK_Error_CvPrecomp, LNK_Error_CvPrecomp,
+65 -68
View File
@@ -221,48 +221,55 @@ lnk_symbol_hash_trie_chunk_list_push(Arena *arena, LNK_SymbolHashTrieChunkList *
} }
internal B32 internal B32
lnk_can_replace_symbol(const LNK_Symbol *dst, const LNK_Symbol *src) lnk_can_replace_symbol(LNK_Symbol *dst, LNK_Symbol *src)
{ {
B32 can_replace = 0; Assert(src->type != LNK_Symbol_Undefined);
Assert(dst != src); Assert(dst != src);
Assert(str8_match(dst->name, src->name, 0)); Assert(str8_match(dst->name, src->name, 0));
Assert(src->type != LNK_Symbol_Undefined);
B32 can_replace = 0;
// lazy vs lazy
if (dst->type == LNK_Symbol_Lazy && src->type == LNK_Symbol_Lazy) { if (dst->type == LNK_Symbol_Lazy && src->type == LNK_Symbol_Lazy) {
// link.exe picks symbol from lib that is discovered first // link.exe picks symbol from lib that is discovered first
LNK_Lib *dst_lib = dst->u.lazy.lib; LNK_Lib *dst_lib = dst->u.lazy.lib;
LNK_Lib *src_lib = src->u.lazy.lib; LNK_Lib *src_lib = src->u.lazy.lib;
//if (dst_lib->input_idx == src_lib->input_idx) {
//Assert(!"TODO: report duplicate symbols in lib");
//}
can_replace = dst_lib->input_idx > src_lib->input_idx; can_replace = dst_lib->input_idx > src_lib->input_idx;
} else if (dst->type == LNK_Symbol_Lazy && (LNK_Symbol_IsDefined(src->type) || src->type == LNK_Symbol_Weak)) {
can_replace = 1;
} else if (dst->type == LNK_Symbol_Weak && LNK_Symbol_IsDefined(src->type)) {
// strong definition found, replace weak symbol
can_replace = 1;
} 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) {
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);
} }
// 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) { if (src->obj && !dst->obj) {
can_replace = 1; can_replace = 1;
} else if (src->obj && dst->obj) { } else if (src->obj && dst->obj) {
can_replace = src->obj->input_idx < dst->obj->input_idx; can_replace = src->obj->input_idx < dst->obj->input_idx;
} }
} else if (LNK_Symbol_IsDefined(dst->type) && LNK_Symbol_IsDefined(src->type)) { } else {
const LNK_DefinedSymbol *dst_defn = &dst->u.defined; lnk_error(LNK_Error_MultiplyDefinedSymbol, "multiply defined weak symbol %S, symbol defined in:", src->name);
const LNK_DefinedSymbol *src_defn = &src->u.defined; lnk_supplement_error("%S", dst->obj->path);
lnk_supplement_error("%S", src->obj->path);
}
}
// 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;
if (dst_defn->value_type == LNK_DefinedSymbolValue_Chunk &&
src_defn->value_type == LNK_DefinedSymbolValue_Chunk) {
Assert(dst_defn->u.chunk->is_discarded == 0); Assert(dst_defn->u.chunk->is_discarded == 0);
Assert(dst_defn->u.chunk->type == LNK_Chunk_Leaf); Assert(dst_defn->u.chunk->type == LNK_Chunk_Leaf);
Assert(src_defn->u.chunk->type == LNK_Chunk_Leaf); Assert(src_defn->u.chunk->type == LNK_Chunk_Leaf);
@@ -278,14 +285,14 @@ lnk_can_replace_symbol(const LNK_Symbol *dst, const LNK_Symbol *src)
} }
if (src_select == dst_select) { if (src_select == dst_select) {
switch (src_select) {
default: InvalidPath;
case COFF_ComdatSelect_Null:
case COFF_ComdatSelect_Any: {
LNK_Chunk *dst_chunk = dst_defn->u.chunk; LNK_Chunk *dst_chunk = dst_defn->u.chunk;
LNK_Chunk *src_chunk = src_defn->u.chunk; LNK_Chunk *src_chunk = src_defn->u.chunk;
U64 dst_chunk_size = lnk_chunk_get_size(dst_chunk); U64 dst_chunk_size = lnk_chunk_get_size(dst_chunk);
U64 src_chunk_size = lnk_chunk_get_size(src_chunk); U64 src_chunk_size = lnk_chunk_get_size(src_chunk);
switch (src_select) {
case COFF_ComdatSelect_Null:
case COFF_ComdatSelect_Any: {
if (src_chunk_size == dst_chunk_size) { if (src_chunk_size == dst_chunk_size) {
can_replace = src_chunk->input_idx < dst_chunk->input_idx; can_replace = src_chunk->input_idx < dst_chunk->input_idx;
} else { } else {
@@ -297,26 +304,16 @@ lnk_can_replace_symbol(const LNK_Symbol *dst, const LNK_Symbol *src)
lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, src->obj, "multiply defined symbol %S in %S.", dst->name, dst->obj->path); lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, src->obj, "multiply defined symbol %S in %S.", dst->name, dst->obj->path);
} break; } break;
case COFF_ComdatSelect_SameSize: { case COFF_ComdatSelect_SameSize: {
LNK_Chunk *dst_chunk = dst_defn->u.chunk; if (dst_chunk_size != src_chunk_size) {
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);
B32 is_same_size = (dst_chunk_size == src_chunk_size);
if (!is_same_size) {
lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, src->obj, "multiply defined symbol %S in %S.", dst->name, dst->obj->path); lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, src->obj, "multiply defined symbol %S in %S.", dst->name, dst->obj->path);
} }
} break; } break;
case COFF_ComdatSelect_ExactMatch: { case COFF_ComdatSelect_ExactMatch: {
B32 is_exact_match = (dst_defn->u.check_sum == src_defn->u.check_sum); if (dst_defn->u.check_sum != src_defn->u.check_sum) {
if (!is_exact_match) {
lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, src->obj, "multiply defined symbol %S in %S.", dst->name, dst->obj->path); lnk_error_obj(LNK_Error_MultiplyDefinedSymbol, src->obj, "multiply defined symbol %S in %S.", dst->name, dst->obj->path);
} }
} break; } break;
case COFF_ComdatSelect_Largest: { case COFF_ComdatSelect_Largest: {
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);
if (dst_chunk_size == src_chunk_size) { if (dst_chunk_size == src_chunk_size) {
if (dst_defn->u.chunk->u.leaf.str == 0 && src_defn->u.chunk->u.leaf.size > 0) { if (dst_defn->u.chunk->u.leaf.str == 0 && src_defn->u.chunk->u.leaf.size > 0) {
// handle communal variable // handle communal variable
@@ -333,6 +330,9 @@ lnk_can_replace_symbol(const LNK_Symbol *dst, const LNK_Symbol *src)
case COFF_ComdatSelect_Associative: { case COFF_ComdatSelect_Associative: {
// ignore // ignore
} break; } break;
default: {
lnk_error_obj(LNK_Error_InvalidPath, src->obj, "unknown COMDAT selection %#x", src->obj, src_select);
} break;
} }
} else { } else {
String8 src_select_str = coff_string_from_comdat_select_type(src_defn->u.selection); String8 src_select_str = coff_string_from_comdat_select_type(src_defn->u.selection);
@@ -341,13 +341,8 @@ lnk_can_replace_symbol(const LNK_Symbol *dst, const LNK_Symbol *src)
"%S: COMDAT selection conflict detected, current selection %S, leader selection %S from %S", "%S: COMDAT selection conflict detected, current selection %S, leader selection %S from %S",
src->name, src_select_str, dst_select_str, dst->obj->path); src->name, src_select_str, dst_select_str, dst->obj->path);
} }
} else if (dst_defn->value_type == LNK_DefinedSymbolValue_VA && src_defn->value_type == LNK_DefinedSymbolValue_Chunk) {
can_replace = 1;
} else { } else {
InvalidPath; lnk_error(LNK_Error_InvalidPath, "unable to find a suitable replacement logic for symbol combination");
}
} else {
InvalidPath;
} }
return can_replace; return can_replace;
@@ -388,19 +383,22 @@ lnk_on_symbol_replace(LNK_Symbol *dst, LNK_Symbol *src)
} }
internal void internal void
lnk_symbol_hash_trie_insert_or_replace(Arena *arena, LNK_SymbolHashTrieChunkList *chunk_list, LNK_SymbolHashTrie **trie, U64 hash, LNK_Symbol *new_symbol) lnk_symbol_hash_trie_insert_or_replace(Arena *arena,
LNK_SymbolHashTrieChunkList *chunks,
LNK_SymbolHashTrie **trie,
U64 hash,
LNK_Symbol *symbol)
{ {
LNK_SymbolHashTrie **curr_trie_ptr = trie; LNK_SymbolHashTrie **curr_trie_ptr = trie;
for (U64 h = hash; ; h <<= 2) { for (U64 h = hash; ; h <<= 2) {
// load current pointer // load current pointer
LNK_SymbolHashTrie *curr_trie = ins_atomic_ptr_eval(curr_trie_ptr); LNK_SymbolHashTrie *curr_trie = ins_atomic_ptr_eval(curr_trie_ptr);
if (curr_trie == 0) { if (curr_trie == 0) {
// init node // init node
LNK_SymbolHashTrie *new_trie = lnk_symbol_hash_trie_chunk_list_push(arena, chunk_list, 512); LNK_SymbolHashTrie *new_trie = lnk_symbol_hash_trie_chunk_list_push(arena, chunks, 512);
new_trie->name = &new_symbol->name; new_trie->name = &symbol->name;
new_trie->symbol = new_symbol; new_trie->symbol = symbol;
MemoryZeroArray(new_trie->child); MemoryZeroArray(new_trie->child);
// try to insert new node // try to insert new node
@@ -412,7 +410,7 @@ lnk_symbol_hash_trie_insert_or_replace(Arena *arena, LNK_SymbolHashTrieChunkList
} }
// rollback chunk list push // rollback chunk list push
chunk_list->last->count -= 1; --chunks->last->count;
// retry insert with trie node from another thread // retry insert with trie node from another thread
curr_trie = cmp; curr_trie = cmp;
@@ -421,39 +419,38 @@ lnk_symbol_hash_trie_insert_or_replace(Arena *arena, LNK_SymbolHashTrieChunkList
// load current symbol // load current symbol
String8 *curr_name = ins_atomic_ptr_eval(&curr_trie->name); String8 *curr_name = ins_atomic_ptr_eval(&curr_trie->name);
if (curr_name) { if (curr_name && str8_match(*curr_name, symbol->name, 0)) {
if (str8_match(*curr_name, new_symbol->name, 0)) { for (LNK_Symbol *src = symbol;;) {
for (LNK_Symbol *src = new_symbol, *dst;;) { // try replacing current symbol with zero, otherwise loop back and retry
LNK_Symbol *dst = ins_atomic_ptr_eval_assign(&curr_trie->symbol, 0); LNK_Symbol *dst = ins_atomic_ptr_eval_assign(&curr_trie->symbol, 0);
// apply replacement logic
LNK_Symbol *current_symbol = dst;
if (dst) { if (dst) {
if (lnk_can_replace_symbol(dst, src)) { if (lnk_can_replace_symbol(dst, src)) {
// HACK: patch dst because relocations might point to it // HACK: patch dst because relocations might point to it
lnk_on_symbol_replace(dst, src); lnk_on_symbol_replace(dst, src);
current_symbol = src;
// swap
dst = src;
} else { } else {
// discard source // discard source
lnk_on_symbol_replace(src, dst); lnk_on_symbol_replace(src, dst);
} }
} }
// try insert back symbol // try replacing symbol, if another thread has already taken the slot, rerun the whole loop
dst = ins_atomic_ptr_eval_cond_assign(&curr_trie->symbol, src, 0); dst = ins_atomic_ptr_eval_cond_assign(&curr_trie->symbol, current_symbol, 0);
// symbol replaced, exit
if (dst == 0) { if (dst == 0) {
break; goto exit;
}
} }
} }
break; // pick child and descend
}
}
// descend
curr_trie_ptr = curr_trie->child + (h >> 62); curr_trie_ptr = curr_trie->child + (h >> 62);
} }
exit:;
} }
internal LNK_SymbolHashTrie * internal LNK_SymbolHashTrie *
+1 -1
View File
@@ -201,7 +201,7 @@ internal LNK_SymbolArray lnk_symbol_array_from_list(Arena *arena, LNK_Symbol
//////////////////////////////// ////////////////////////////////
internal void lnk_symbol_hash_trie_insert_or_replace(Arena *arena, LNK_SymbolHashTrieChunkList *chunk_list, LNK_SymbolHashTrie **trie, U64 hash, LNK_Symbol *new_symbol); internal void lnk_symbol_hash_trie_insert_or_replace(Arena *arena, LNK_SymbolHashTrieChunkList *chunks, LNK_SymbolHashTrie **trie, U64 hash, LNK_Symbol *symbol);
internal LNK_SymbolHashTrie * lnk_symbol_hash_trie_search(LNK_SymbolHashTrie *trie, U64 hash, String8 name); internal LNK_SymbolHashTrie * lnk_symbol_hash_trie_search(LNK_SymbolHashTrie *trie, U64 hash, String8 name);
internal void lnk_symbol_hash_trie_remove(LNK_SymbolHashTrie *trie); internal void lnk_symbol_hash_trie_remove(LNK_SymbolHashTrie *trie);