Handle dynamic relocs to weak undefined when possible.
llvm-svn: 250311
diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp
index b9af139..0b531c0 100644
--- a/lld/ELF/OutputSections.cpp
+++ b/lld/ELF/OutputSections.cpp
@@ -52,7 +52,7 @@
for (const SymbolBody *B : Entries) {
uint8_t *Entry = Buf;
Buf += sizeof(uintX_t);
- if (canBePreempted(B))
+ if (canBePreempted(B, false))
continue; // The dynamic linker will take care of it.
uintX_t VA = getSymVA<ELFT>(*B);
write<uintX_t, ELFT::TargetEndianness, sizeof(uintX_t)>(Entry, VA);
@@ -122,7 +122,8 @@
uint32_t Type = RI.getType(IsMips64EL);
- bool CanBePreempted = canBePreempted(Body);
+ bool NeedsGot = Body && Target->relocNeedsGot(Type, *Body);
+ bool CanBePreempted = canBePreempted(Body, NeedsGot);
uintX_t Addend = 0;
if (!CanBePreempted) {
if (IsRela) {
@@ -134,7 +135,7 @@
P->setSymbolAndType(0, Target->getRelativeReloc(), IsMips64EL);
}
- if (Body && Target->relocNeedsGot(Type, *Body)) {
+ if (NeedsGot) {
P->r_offset = Out<ELFT>::Got->getEntryAddr(*Body);
if (CanBePreempted)
P->setSymbolAndType(Body->getDynamicSymbolTableIndex(),
@@ -458,13 +459,34 @@
// Returns true if a symbol can be replaced at load-time by a symbol
// with the same name defined in other ELF executable or DSO.
-bool lld::elf2::canBePreempted(const SymbolBody *Body) {
+bool lld::elf2::canBePreempted(const SymbolBody *Body, bool NeedsGot) {
if (!Body)
return false; // Body is a local symbol.
if (Body->isShared())
return true;
- if (Body->isUndefined() && !Body->isWeak())
- return true;
+
+ if (Body->isUndefined()) {
+ if (!Body->isWeak())
+ return true;
+
+ // This is an horrible corner case. Ideally we would like to say that any
+ // undefined symbol can be preempted so that the dynamic linker has a
+ // chance of finding it at runtime.
+ //
+ // The problem is that the code sequence used to test for weak undef
+ // functions looks like
+ // if (func) func()
+ // If the code is -fPIC the first reference is a load from the got and
+ // everything works.
+ // If the code is not -fPIC there is no reasonable way to solve it:
+ // * A relocation writing to the text segment will fail (it is ro).
+ // * A copy relocation doesn't work for functions.
+ // * The trick of using a plt entry as the address would fail here since
+ // the plt entry would have a non zero address.
+ // Since we cannot do anything better, we just resolve the symbol to 0 and
+ // don't produce a dynamic relocation.
+ return NeedsGot;
+ }
if (!Config->Shared)
return false;
return Body->getMostConstrainingVisibility() == STV_DEFAULT;
diff --git a/lld/ELF/OutputSections.h b/lld/ELF/OutputSections.h
index 4d23c4b..ba9c07b 100644
--- a/lld/ELF/OutputSections.h
+++ b/lld/ELF/OutputSections.h
@@ -39,7 +39,7 @@
typename llvm::object::ELFFile<ELFT>::uintX_t
getLocalRelTarget(const ObjectFile<ELFT> &File,
const typename llvm::object::ELFFile<ELFT>::Elf_Rel &Sym);
-bool canBePreempted(const SymbolBody *Body);
+bool canBePreempted(const SymbolBody *Body, bool NeedsGot);
template <class ELFT> bool includeInSymtab(const SymbolBody &B);
bool includeInDynamicSymtab(const SymbolBody &B);
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 179e2f0..3af58b2 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -191,20 +191,22 @@
if (Body)
Body = Body->repl();
+ bool NeedsGot = false;
if (Body) {
if (Target->relocNeedsPlt(Type, *Body)) {
if (Body->isInPlt())
continue;
Out<ELFT>::Plt->addEntry(Body);
}
- if (Target->relocNeedsGot(Type, *Body)) {
+ NeedsGot = Target->relocNeedsGot(Type, *Body);
+ if (NeedsGot) {
if (Body->isInGot())
continue;
Out<ELFT>::Got->addEntry(Body);
}
}
- bool CBP = canBePreempted(Body);
+ bool CBP = canBePreempted(Body, NeedsGot);
if (!CBP && (!Config->Shared || Target->isRelRelative(Type)))
continue;
if (CBP)
diff --git a/lld/test/elf2/dynamic-reloc-weak.s b/lld/test/elf2/dynamic-reloc-weak.s
new file mode 100644
index 0000000..107321e
--- /dev/null
+++ b/lld/test/elf2/dynamic-reloc-weak.s
@@ -0,0 +1,31 @@
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t2.o
+// RUN: ld.lld2 -shared %t2.o -o %t2.so
+// RUN: ld.lld2 %t.o %t2.so -o %t
+// RUN: llvm-readobj -r %t | FileCheck %s
+// REQUIRES: x86
+
+ .globl _start
+_start:
+ .type sym1,@function
+ .weak sym1
+ .long sym1@gotpcrel
+
+ .type sym2,@function
+ .weak sym2
+ .long sym2@plt
+
+ .type sym3,@function
+ .weak sym3
+ .quad sym3
+
+// Both gold and bfd ld will produce a relocation for sym1 and sym2 only. That
+// That seems odd. If the dynamic linker must get a chance to resolve sym1
+// and sym2, that should also be the case for sym3.
+
+// CHECK: Relocations [
+// CHECK-NEXT: Section ({{.*}}) .rela.dyn {
+// CHECK-NEXT: 0x{{.*}} R_X86_64_GLOB_DAT sym1 0x0
+// CHECK-NEXT: 0x{{.*}} R_X86_64_GLOB_DAT sym2 0x0
+// CHECK-NEXT: }
+// CHECK-NEXT: ]