diff --git a/src/linker/lnk_symbol_table.c b/src/linker/lnk_symbol_table.c index e27d4bf3..7d04d353 100644 --- a/src/linker/lnk_symbol_table.c +++ b/src/linker/lnk_symbol_table.c @@ -145,9 +145,30 @@ lnk_can_replace_symbol(LNK_SymbolScope scope, LNK_Symbol *dst, LNK_Symbol *src) if (dst_interp == COFF_SymbolValueInterp_Undefined && src_interp == COFF_SymbolValueInterp_Regular) { can_replace = 1; } - // undefined vs weak - else if (dst_interp == COFF_SymbolValueInterp_Undefined && src_interp == COFF_SymbolValueInterp_Weak) { - can_replace = 1; + // (weak vs undefined) or (undefined vs weak) + else if ((dst_interp == COFF_SymbolValueInterp_Weak && src_interp == COFF_SymbolValueInterp_Undefined) || (dst_interp == COFF_SymbolValueInterp_Undefined && src_interp == COFF_SymbolValueInterp_Weak)) { + LNK_Symbol *weak, *undef; + COFF_ParsedSymbol weak_parsed; + if (dst_interp == COFF_SymbolValueInterp_Weak) { + weak = dst; + undef = src; + weak_parsed = dst_parsed; + } else { + weak = src; + undef = dst; + weak_parsed = src_parsed; + } + + COFF_SymbolWeakExt *weak_ext = coff_parse_weak_tag(weak_parsed, weak->u.defined.obj->header.is_big_obj); + if (weak_ext->characteristics == COFF_WeakExt_SearchLibrary) { + can_replace = lnk_symbol_defined_is_before(weak, undef); + } else if (weak_ext->characteristics == COFF_WeakExt_NoLibrary) { + can_replace = dst_interp == COFF_SymbolValueInterp_Weak; + } else if (weak_ext->characteristics == COFF_WeakExt_SearchAlias) { + can_replace = dst_interp == COFF_SymbolValueInterp_Undefined; + } else { + can_replace = lnk_symbol_defined_is_before(src, dst); + } } // undefined vs undefined else if (dst_interp == COFF_SymbolValueInterp_Undefined && src_interp == COFF_SymbolValueInterp_Undefined) { @@ -165,7 +186,7 @@ lnk_can_replace_symbol(LNK_SymbolScope scope, LNK_Symbol *dst, LNK_Symbol *src) else if (dst_interp == COFF_SymbolValueInterp_Undefined && src_interp == COFF_SymbolValueInterp_Debug) { can_replace = 1; } - // regular/weak/common/abs/debug vs undefined + // regular/common/abs/debug vs undefined else if (dst_interp != COFF_SymbolValueInterp_Undefined && src_interp == COFF_SymbolValueInterp_Undefined) { can_replace = 0; } @@ -197,7 +218,25 @@ lnk_can_replace_symbol(LNK_SymbolScope scope, LNK_Symbol *dst, LNK_Symbol *src) } // weak vs weak else if (dst_interp == COFF_SymbolValueInterp_Weak && src_interp == COFF_SymbolValueInterp_Weak) { - can_replace = lnk_symbol_defined_is_before(src, dst); + COFF_ParsedSymbol dst_parsed = lnk_parsed_symbol_from_defined(dst); + COFF_ParsedSymbol src_parsed = lnk_parsed_symbol_from_defined(src); + COFF_SymbolWeakExt *dst_ext = coff_parse_weak_tag(dst_parsed, dst->u.defined.obj->header.is_big_obj); + COFF_SymbolWeakExt *src_ext = coff_parse_weak_tag(src_parsed, src->u.defined.obj->header.is_big_obj); + if (dst_ext->characteristics == COFF_WeakExt_SearchAlias && src_ext->characteristics == COFF_WeakExt_SearchAlias) { + // TODO: test + lnk_error_multiply_defined_symbol(dst, src); + } else if (dst_ext->characteristics == COFF_WeakExt_SearchAlias && src_ext->characteristics != COFF_WeakExt_SearchAlias) { + // TODO: test + can_replace = 0; + } else if (dst_ext->characteristics != COFF_WeakExt_SearchAlias && src_ext->characteristics == COFF_WeakExt_SearchAlias) { + // TODO: test + can_replace = 1; + } else if (dst_ext->characteristics == COFF_WeakExt_NoLibrary && src_ext->characteristics == COFF_WeakExt_AntiDependency) { + can_replace = 1; + can_replace = lnk_symbol_defined_is_before(src, dst); + } else { + can_replace = lnk_symbol_defined_is_before(src, dst); + } } // 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)) {