[lld][COFF] Add support for overriding weak symbols in LLVM bitcode input
authorAlan Zhao <ayzhao@google.com>
Wed, 31 Aug 2022 20:45:40 +0000 (16:45 -0400)
committerAlan Zhao <ayzhao@google.com>
Thu, 8 Sep 2022 17:17:02 +0000 (13:17 -0400)
LLVM bitcode contains support for weak symbols, so we can add support
for overriding weak symbols in the output COFF even though COFF doesn't
have inherent support for weak symbols.

The motivation for this patch is that Chromium is trying to use libc++'s
assertion handler mechanism, which relies on weak symbols [0], but we're
unable to perform a ThinLTO build on Windows due to this problem [1].

[0]: https://reviews.llvm.org/D121478
[1]: https://crrev.com/c/3863576

Reviewed By: rnk

Differential Revision: https://reviews.llvm.org/D133165

lld/COFF/InputFiles.cpp
lld/COFF/SymbolTable.cpp
lld/COFF/SymbolTable.h
lld/COFF/Symbols.h
lld/test/COFF/Inputs/strong-def.ll [new file with mode: 0644]
lld/test/COFF/Inputs/weak-def.ll [new file with mode: 0644]
lld/test/COFF/weak-override.ll [new file with mode: 0644]

index 0f3f5e0..9c1b269 100644 (file)
@@ -1070,7 +1070,8 @@ void BitcodeFile::parse() {
         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())
index 9e5edb6..95f7b4e 100644 (file)
@@ -698,12 +698,12 @@ Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) {
 
 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<DefinedRegular>(s))
+  if (wasInserted || !isa<DefinedRegular>(s) || s->isWeak)
     replaceSymbol<DefinedRegular>(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;
 }
index 167377e..15cb533 100644 (file)
@@ -95,7 +95,8 @@ public:
   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<DefinedRegular *, bool>
   addComdat(InputFile *f, StringRef n,
             const llvm::object::coff_symbol_generic *s = nullptr);
index a4c6f89..7e99b02 100644 (file)
@@ -106,7 +106,8 @@ protected:
       : 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 @@ public:
   // 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 @@ public:
   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 (file)
index 0000000..ea5d78c
--- /dev/null
@@ -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 (file)
index 0000000..9e1315a
--- /dev/null
@@ -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 (file)
index 0000000..93f2d8e
--- /dev/null
@@ -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