ELF: Only add libcall symbols to the link if defined in bitcode.
authorPeter Collingbourne <peter@pcc.me.uk>
Wed, 8 Aug 2018 23:48:12 +0000 (23:48 +0000)
committerPeter Collingbourne <peter@pcc.me.uk>
Wed, 8 Aug 2018 23:48:12 +0000 (23:48 +0000)
Adding all libcall symbols to the link can have undesired consequences.
For example, the libgcc implementation of __sync_val_compare_and_swap_8
on 32-bit ARM pulls in an .init_array entry that aborts the program if
the Linux kernel does not support 64-bit atomics, which would prevent
the program from running even if it does not use 64-bit atomics.

This change makes it so that we only add libcall symbols to the
link before LTO if we have to, i.e. if the symbol's definition is in
bitcode. Any other required libcall symbols will be added to the link
after LTO when we add the LTO object file to the link.

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

llvm-svn: 339301

lld/ELF/Driver.cpp
lld/ELF/Symbols.cpp
lld/ELF/Symbols.h
lld/test/ELF/lto/Inputs/libcall-archive.s [new file with mode: 0644]
lld/test/ELF/lto/libcall-archive.ll

index 814c7fe..1ed5291 100644 (file)
@@ -1212,6 +1212,21 @@ template <class ELFT> static void handleUndefined(StringRef Name) {
     Symtab->fetchLazy<ELFT>(Sym);
 }
 
+template <class ELFT> static void handleLibcall(StringRef Name) {
+  Symbol *Sym = Symtab->find(Name);
+  if (!Sym || !Sym->isLazy())
+    return;
+
+  MemoryBufferRef MB;
+  if (auto *LO = dyn_cast<LazyObject>(Sym))
+    MB = LO->File->MB;
+  else
+    MB = cast<LazyArchive>(Sym)->getMemberBuffer();
+
+  if (isBitcode(MB))
+    Symtab->fetchLazy<ELFT>(Sym);
+}
+
 template <class ELFT> static bool shouldDemote(Symbol &Sym) {
   // If all references to a DSO happen to be weak, the DSO is not added to
   // DT_NEEDED. If that happens, we need to eliminate shared symbols created
@@ -1388,11 +1403,20 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
   // in a bitcode file in an archive member, we need to arrange to use LTO to
   // compile those archive members by adding them to the link beforehand.
   //
-  // With this the symbol table should be complete. After this, no new names
-  // except a few linker-synthesized ones will be added to the symbol table.
+  // However, adding all libcall symbols to the link can have undesired
+  // consequences. For example, the libgcc implementation of
+  // __sync_val_compare_and_swap_8 on 32-bit ARM pulls in an .init_array entry
+  // that aborts the program if the Linux kernel does not support 64-bit
+  // atomics, which would prevent the program from running even if it does not
+  // use 64-bit atomics.
+  //
+  // Therefore, we only add libcall symbols to the link before LTO if we have
+  // to, i.e. if the symbol's definition is in bitcode. Any other required
+  // libcall symbols will be added to the link after LTO when we add the LTO
+  // object file to the link.
   if (!BitcodeFiles.empty())
     for (const char *S : LibcallRoutineNames)
-      handleUndefined<ELFT>(S);
+      handleLibcall<ELFT>(S);
 
   // Return if there were name resolution errors.
   if (errorCount())
@@ -1434,6 +1458,9 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
 
   // Do link-time optimization if given files are LLVM bitcode files.
   // This compiles bitcode files into real object files.
+  //
+  // With this the symbol table should be complete. After this, no new names
+  // except a few linker-synthesized ones will be added to the symbol table.
   Symtab->addCombinedLTOObject<ELFT>();
   if (errorCount())
     return;
index 4243cb1..72942e0 100644 (file)
@@ -204,6 +204,15 @@ void Symbol::parseSymbolVersion() {
 
 InputFile *LazyArchive::fetch() { return cast<ArchiveFile>(File)->fetch(Sym); }
 
+MemoryBufferRef LazyArchive::getMemberBuffer() {
+  Archive::Child C = CHECK(
+      Sym.getMember(), "could not get the member for symbol " + Sym.getName());
+
+  return CHECK(C.getMemoryBufferRef(),
+               "could not get the buffer for the member defining symbol " +
+                   Sym.getName());
+}
+
 uint8_t Symbol::computeBinding() const {
   if (Config->Relocatable)
     return Binding;
index 8c9513b..3019e47 100644 (file)
@@ -286,6 +286,7 @@ public:
   static bool classof(const Symbol *S) { return S->kind() == LazyArchiveKind; }
 
   InputFile *fetch();
+  MemoryBufferRef getMemberBuffer();
 
 private:
   const llvm::object::Archive::Symbol Sym;
diff --git a/lld/test/ELF/lto/Inputs/libcall-archive.s b/lld/test/ELF/lto/Inputs/libcall-archive.s
new file mode 100644 (file)
index 0000000..6ca6e5f
--- /dev/null
@@ -0,0 +1,2 @@
+.globl __sync_val_compare_and_swap_8
+__sync_val_compare_and_swap_8:
index 0435329..7e8ac18 100644 (file)
@@ -2,10 +2,14 @@
 ; RUN: rm -f %t.a
 ; RUN: llvm-as -o %t.o %s
 ; RUN: llvm-as -o %t2.o %S/Inputs/libcall-archive.ll
-; RUN: llvm-ar rcs %t.a %t2.o
+; RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux -o %t3.o %S/Inputs/libcall-archive.s
+; RUN: llvm-ar rcs %t.a %t2.o %t3.o
 ; RUN: ld.lld -o %t %t.o %t.a
 ; RUN: llvm-nm %t | FileCheck %s
+; RUN: ld.lld -o %t2 %t.o --start-lib %t2.o %t3.o --end-lib
+; RUN: llvm-nm %t2 | FileCheck %s
 
+; CHECK-NOT: T __sync_val_compare_and_swap_8
 ; CHECK: T _start
 ; CHECK: T memcpy