diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp --- a/lld/COFF/InputFiles.cpp +++ b/lld/COFF/InputFiles.cpp @@ -1070,7 +1070,8 @@ sym = ctx.symtab.addUndefined(symName, this, false); } } else { - sym = ctx.symtab.addRegular(this, symName, nullptr, fakeSC); + sym = ctx.symtab.addRegular(this, symName, nullptr, fakeSC, 0, + objSym.isWeak()); } symbols.push_back(sym); if (objSym.isUsed()) diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h --- a/lld/COFF/SymbolTable.h +++ b/lld/COFF/SymbolTable.h @@ -95,7 +95,8 @@ Symbol *addAbsolute(StringRef n, COFFSymbolRef s); Symbol *addRegular(InputFile *f, StringRef n, const llvm::object::coff_symbol_generic *s = nullptr, - SectionChunk *c = nullptr, uint32_t sectionOffset = 0); + SectionChunk *c = nullptr, uint32_t sectionOffset = 0, + bool isWeak = false); std::pair addComdat(InputFile *f, StringRef n, const llvm::object::coff_symbol_generic *s = nullptr); diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp --- a/lld/COFF/SymbolTable.cpp +++ b/lld/COFF/SymbolTable.cpp @@ -698,12 +698,12 @@ Symbol *SymbolTable::addRegular(InputFile *f, StringRef n, const coff_symbol_generic *sym, SectionChunk *c, - uint32_t sectionOffset) { + uint32_t sectionOffset, bool isWeak) { auto [s, wasInserted] = insert(n, f); - if (wasInserted || !isa(s)) + if (wasInserted || !isa(s) || s->isWeak) replaceSymbol(s, f, n, /*IsCOMDAT*/ false, - /*IsExternal*/ true, sym, c); - else + /*IsExternal*/ true, sym, c, isWeak); + else if (!isWeak) reportDuplicate(s, f, c, sectionOffset); return s; } diff --git a/lld/COFF/Symbols.h b/lld/COFF/Symbols.h --- a/lld/COFF/Symbols.h +++ b/lld/COFF/Symbols.h @@ -106,7 +106,8 @@ : symbolKind(k), isExternal(true), isCOMDAT(false), writtenToSymtab(false), pendingArchiveLoad(false), isGCRoot(false), isRuntimePseudoReloc(false), deferUndefined(false), canInline(true), - nameSize(n.size()), nameData(n.empty() ? nullptr : n.data()) { + isWeak(false), nameSize(n.size()), + nameData(n.empty() ? nullptr : n.data()) { assert((!n.empty() || k <= LastDefinedCOFFKind) && "If the name is empty, the Symbol must be a DefinedCOFF."); } @@ -145,6 +146,11 @@ // doesn't know the final contents of the symbol. unsigned canInline : 1; + // True if the symbol is weak. This is only tracked for bitcode/LTO symbols. + // This information isn't written to the output; rather, it's used for + // managing weak symbol overrides. + unsigned isWeak : 1; + protected: // Symbol name length. Assume symbol lengths fit in a 32-bit integer. uint32_t nameSize; @@ -200,10 +206,11 @@ DefinedRegular(InputFile *f, StringRef n, bool isCOMDAT, bool isExternal = false, const coff_symbol_generic *s = nullptr, - SectionChunk *c = nullptr) + SectionChunk *c = nullptr, bool isWeak = false) : DefinedCOFF(DefinedRegularKind, f, n, s), data(c ? &c->repl : nullptr) { this->isExternal = isExternal; this->isCOMDAT = isCOMDAT; + this->isWeak = isWeak; } static bool classof(const Symbol *s) { diff --git a/lld/test/COFF/Inputs/strong-def.ll b/lld/test/COFF/Inputs/strong-def.ll new file mode 100644 --- /dev/null +++ b/lld/test/COFF/Inputs/strong-def.ll @@ -0,0 +1,6 @@ +target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +define dso_local noundef i32 @foo() local_unnamed_addr { + ret i32 5678 +} diff --git a/lld/test/COFF/Inputs/weak-def.ll b/lld/test/COFF/Inputs/weak-def.ll new file mode 100644 --- /dev/null +++ b/lld/test/COFF/Inputs/weak-def.ll @@ -0,0 +1,6 @@ +target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +define weak dso_local noundef i32 @foo() local_unnamed_addr { + ret i32 1234 +} diff --git a/lld/test/COFF/weak-override.ll b/lld/test/COFF/weak-override.ll new file mode 100644 --- /dev/null +++ b/lld/test/COFF/weak-override.ll @@ -0,0 +1,30 @@ +; REQUIRES: x86 +; RUN: llvm-as -o %t-weak.obj %S/Inputs/weak-def.ll +; RUN: llvm-as -o %t-strong.obj %S/Inputs/strong-def.ll +; RUN: llvm-as -o %t.obj %s +; RUN: lld-link /dll /out:%t-weak-first.dll %t.obj %t-weak.obj %t-strong.obj +; RUN: lld-link /dll /out:%t-strong-first.dll %t.obj %t-strong.obj %t-weak.obj +; RUN: lld-link /dll /out:%t-weak-only.dll %t.obj %t-weak.obj +; RUN: llvm-objdump -d %t-weak-first.dll | FileCheck --check-prefix=CHECK-STRONG %s +; RUN: llvm-objdump -d %t-strong-first.dll | FileCheck --check-prefix=CHECK-STRONG %s +; RUN: llvm-objdump -d %t-weak-only.dll | FileCheck --check-prefix=CHECK-WEAK %s + +target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +declare noundef i32 @foo() local_unnamed_addr + +define dllexport i32 @bar() local_unnamed_addr { + %1 = tail call noundef i32 @foo() + ret i32 %1 +} + +define void @_DllMainCRTStartup() { +entry: + ret void +} + +; CHECK-STRONG: movl $5678, %eax +; CHECK-STRONG-NOT: movl $1234, %eax +; CHECK-WEAK: movl $1234, %eax +; CHECK-WEAK-NOT: movl $5678, %eax