summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael Espindola <rafael.espindola@gmail.com>2016-04-30 01:15:17 +0000
committerRafael Espindola <rafael.espindola@gmail.com>2016-04-30 01:15:17 +0000
commita85efd985c74716429b69cebbac1a5f438fd45d1 (patch)
tree50a919ea3166bf28411c317dab86696f14c7a852
parent47cf2affbdb8a8152b84965134282581692baace (diff)
downloadbcm5719-llvm-a85efd985c74716429b69cebbac1a5f438fd45d1.tar.gz
bcm5719-llvm-a85efd985c74716429b69cebbac1a5f438fd45d1.zip
Don't create dynamic relocations to ro segments.
These would just crash at runtime. If we ever decide to support rw text segments this should make it easier to implement as there is now a single point where we notice the problem. I have tested this with a freebsd buildworld. It found a non pic assembly file being linked into a .so,. With that fixed, buildworld finished. llvm-svn: 268149
-rw-r--r--lld/ELF/InputSection.h3
-rw-r--r--lld/ELF/Writer.cpp185
-rw-r--r--lld/test/ELF/Inputs/protected-shared.s3
-rw-r--r--lld/test/ELF/Inputs/undef-with-plt-addr.s4
-rw-r--r--lld/test/ELF/copy-errors.s15
-rw-r--r--lld/test/ELF/copy-in-shared.s12
-rw-r--r--lld/test/ELF/eh-frame-dyn-rel.s7
-rw-r--r--lld/test/ELF/i386-got-and-copy.s1
-rw-r--r--lld/test/ELF/undef-with-plt-addr.s26
-rw-r--r--lld/test/ELF/x86-64-reloc-32-fpic.s2
-rw-r--r--lld/test/ELF/x86-64-reloc-pc32-fpic.s2
11 files changed, 152 insertions, 108 deletions
diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h
index 21e536f2f6a..4fef6085767 100644
--- a/lld/ELF/InputSection.h
+++ b/lld/ELF/InputSection.h
@@ -62,7 +62,8 @@ enum RelExpr {
inline bool refersToGotEntry(RelExpr Expr) {
return Expr == R_GOT || Expr == R_GOT_OFF || Expr == R_MIPS_GOT ||
Expr == R_MIPS_GOT_LOCAL || Expr == R_GOT_PAGE_PC ||
- Expr == R_GOT_PC || Expr == R_GOT_FROM_END;
+ Expr == R_GOT_PC || Expr == R_GOT_FROM_END || Expr == R_TLSGD ||
+ Expr == R_TLSGD_PC;
}
struct Relocation {
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 86915f31beb..ca668ebe349 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -69,6 +69,7 @@ private:
void scanRelocs(InputSection<ELFT> &C);
void scanRelocs(InputSectionBase<ELFT> &S, const Elf_Shdr &RelSec);
+ RelExpr adjustExpr(SymbolBody &S, bool IsWrite, RelExpr Expr, uint32_t Type);
void createPhdrs();
void assignAddresses();
void assignFileOffsets();
@@ -439,64 +440,10 @@ template <class ELFT> static bool isAbsolute(const SymbolBody &Body) {
return false;
}
-namespace {
-enum PltNeed { Plt_No, Plt_Explicit, Plt_Implicit };
-}
-
static bool needsPlt(RelExpr Expr) {
return Expr == R_PLT_PC || Expr == R_PPC_PLT_OPD || Expr == R_PLT;
}
-static PltNeed needsPlt(RelExpr Expr, uint32_t Type, const SymbolBody &S) {
- if (S.isGnuIFunc())
- return Plt_Explicit;
- if (S.isPreemptible() && needsPlt(Expr))
- return Plt_Explicit;
-
- // This handles a non PIC program call to function in a shared library.
- // In an ideal world, we could just report an error saying the relocation
- // can overflow at runtime.
- // In the real world with glibc, crt1.o has a R_X86_64_PC32 pointing to
- // libc.so.
- //
- // The general idea on how to handle such cases is to create a PLT entry
- // and use that as the function value.
- //
- // For the static linking part, we just return true and everything else
- // will use the the PLT entry as the address.
- //
- // The remaining problem is making sure pointer equality still works. We
- // need the help of the dynamic linker for that. We let it know that we have
- // a direct reference to a so symbol by creating an undefined symbol with a
- // non zero st_value. Seeing that, the dynamic linker resolves the symbol to
- // the value of the symbol we created. This is true even for got entries, so
- // pointer equality is maintained. To avoid an infinite loop, the only entry
- // that points to the real function is a dedicated got entry used by the
- // plt. That is identified by special relocation types (R_X86_64_JUMP_SLOT,
- // R_386_JMP_SLOT, etc).
- if (S.isShared() && !Config->Pic && S.isFunc())
- if (!refersToGotEntry(Expr))
- return Plt_Implicit;
-
- return Plt_No;
-}
-
-static bool needsCopyRel(RelExpr E, const SymbolBody &S) {
- if (Config->Shared)
- return false;
- if (!S.isShared())
- return false;
- if (!S.isObject())
- return false;
- if (refersToGotEntry(E))
- return false;
- if (needsPlt(E))
- return false;
- if (E == R_SIZE)
- return false;
- return true;
-}
-
template <class ELFT>
static bool isRelRelative(RelExpr E, uint32_t Type, const SymbolBody &Body) {
if (E == R_SIZE)
@@ -515,6 +462,91 @@ static bool isRelRelative(RelExpr E, uint32_t Type, const SymbolBody &Body) {
return Target->usesOnlyLowPageBits(Type);
}
+static RelExpr toPlt(RelExpr Expr) {
+ if (Expr == R_PPC_OPD)
+ return R_PPC_PLT_OPD;
+ if (Expr == R_PC)
+ return R_PLT_PC;
+ if (Expr == R_ABS)
+ return R_PLT;
+ return Expr;
+}
+
+static RelExpr fromPlt(RelExpr Expr) {
+ // We decided not to use a plt. Optimize a reference to the plt to a
+ // reference to the symbol itself.
+ if (Expr == R_PLT_PC)
+ return R_PC;
+ if (Expr == R_PPC_PLT_OPD)
+ return R_PPC_OPD;
+ if (Expr == R_PLT)
+ return R_ABS;
+ return Expr;
+}
+
+template <class ELFT>
+RelExpr Writer<ELFT>::adjustExpr(SymbolBody &Body, bool IsWrite, RelExpr Expr,
+ uint32_t Type) {
+ if (Body.isGnuIFunc())
+ return toPlt(Expr);
+ bool Preemptible = Body.isPreemptible();
+ if (needsPlt(Expr)) {
+ if (Preemptible)
+ return Expr;
+ return fromPlt(Expr);
+ }
+
+ if (!IsWrite && !refersToGotEntry(Expr) && !needsPlt(Expr) && Preemptible) {
+ // This relocation would require the dynamic linker to write a value
+ // to read only memory. We can hack around it if we are producing an
+ // executable and the refered symbol can be preemepted to refer to the
+ // executable.
+ if (Config->Shared) {
+ StringRef S = getELFRelocationTypeName(Config->EMachine, Type);
+ error("relocation " + S + " cannot be used when making a shared "
+ "object; recompile with -fPIC.");
+ return Expr;
+ }
+ if (Body.getVisibility() != STV_DEFAULT) {
+ error("Cannot preempt symbol");
+ return Expr;
+ }
+ if (Body.isObject()) {
+ // Produce a copy relocation.
+ auto *B = cast<SharedSymbol<ELFT>>(&Body);
+ if (!B->needsCopy())
+ addCopyRelSymbol(B);
+ return Expr;
+ }
+ if (Body.isFunc()) {
+ // This handles a non PIC program call to function in a shared library.In
+ // an ideal world, we could just report an error saying the relocation can
+ // overflow at runtime. In the real world with glibc, crt1.o has a
+ // R_X86_64_PC32 pointing to libc.so.
+ //
+ // The general idea on how to handle such cases is to create a PLT entry
+ // and use that as the function value.
+ //
+ // For the static linking part, we just return a plt expr and everything
+ // else will use the the PLT entry as the address.
+ //
+ // The remaining problem is making sure pointer equality still works. We
+ // need the help of the dynamic linker for that. We let it know that we
+ // have a direct reference to a so symbol by creating an undefined symbol
+ // with a non zero st_value. Seeing that, the dynamic linker resolves the
+ // symbol to the value of the symbol we created. This is true even for got
+ // entries, so pointer equality is maintained. To avoid an infinite loop,
+ // the only entry that points to the real function is a dedicated got
+ // entry used by the plt. That is identified by special relocation types
+ // (R_X86_64_JUMP_SLOT, R_386_JMP_SLOT, etc).
+ Body.NeedsCopyOrPltAddr = true;
+ return toPlt(Expr);
+ }
+ error("Symbol is missing type");
+ }
+ return Expr;
+}
+
// The reason we have to do this early scan is as follows
// * To mmap the output file, we need to know the size
// * For that, we need to know how many dynamic relocs we will have.
@@ -556,6 +588,14 @@ void Writer<ELFT>::scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
RelExpr Expr = Target->getRelExpr(Type, Body);
+ Expr = adjustExpr(Body, IsWrite, Expr, Type);
+ if (HasError)
+ continue;
+ bool Preemptible = Body.isPreemptible();
+ if (auto *B = dyn_cast<SharedSymbol<ELFT>>(&Body))
+ if (B->needsCopy())
+ Preemptible = false;
+
// This relocation does not require got entry, but it is relative to got and
// needs it to be created. Here we request for that.
if (Expr == R_GOTONLY_PC || Expr == R_GOTREL || Expr == R_PPC_TOC)
@@ -587,34 +627,10 @@ void Writer<ELFT>::scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
AddDyn({Target->RelativeRel, C.OutSec, Offset, true, &Body,
getAddend<ELFT>(RI)});
- // If a symbol in a DSO is referenced directly instead of through GOT
- // in a read-only section, we need to create a copy relocation for the
- // symbol.
- if (auto *B = dyn_cast<SharedSymbol<ELFT>>(&Body)) {
- if (!IsWrite && needsCopyRel(Expr, *B)) {
- if (!B->needsCopy())
- addCopyRelSymbol(B);
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
- continue;
- }
- }
-
- bool Preemptible = Body.isPreemptible();
-
// If a relocation needs PLT, we create a PLT and a GOT slot
// for the symbol.
- PltNeed NeedPlt = needsPlt(Expr, Type, Body);
- if (NeedPlt) {
- if (NeedPlt == Plt_Implicit)
- Body.NeedsCopyOrPltAddr = true;
- RelExpr E = Expr;
- if (Expr == R_PPC_OPD)
- E = R_PPC_PLT_OPD;
- else if (Expr == R_PC)
- E = R_PLT_PC;
- else if (Expr == R_ABS)
- E = R_PLT;
- C.Relocations.push_back({E, Type, Offset, Addend, &Body});
+ if (needsPlt(Expr)) {
+ C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
if (Body.isInPlt())
continue;
@@ -641,15 +657,6 @@ void Writer<ELFT>::scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
continue;
}
- // We decided not to use a plt. Optimize a reference to the plt to a
- // reference to the symbol itself.
- if (Expr == R_PLT_PC)
- Expr = R_PC;
- if (Expr == R_PPC_PLT_OPD)
- Expr = R_PPC_OPD;
- if (Expr == R_PLT)
- Expr = R_ABS;
-
if (Target->needsThunk(Type, File, Body)) {
C.Relocations.push_back({R_THUNK, Type, Offset, Addend, &Body});
continue;
diff --git a/lld/test/ELF/Inputs/protected-shared.s b/lld/test/ELF/Inputs/protected-shared.s
index 342c37950a8..5f4b1daf63c 100644
--- a/lld/test/ELF/Inputs/protected-shared.s
+++ b/lld/test/ELF/Inputs/protected-shared.s
@@ -5,3 +5,6 @@ foo:
.global bar
.protected bar
bar:
+
+ .global zed
+zed:
diff --git a/lld/test/ELF/Inputs/undef-with-plt-addr.s b/lld/test/ELF/Inputs/undef-with-plt-addr.s
index 4b3a5d3352d..b12737d9e09 100644
--- a/lld/test/ELF/Inputs/undef-with-plt-addr.s
+++ b/lld/test/ELF/Inputs/undef-with-plt-addr.s
@@ -1,3 +1,7 @@
.globl set_data
.type set_data,@function
set_data:
+
+ .globl foo
+ .type foo,@function
+foo:
diff --git a/lld/test/ELF/copy-errors.s b/lld/test/ELF/copy-errors.s
new file mode 100644
index 00000000000..bc28e328ce6
--- /dev/null
+++ b/lld/test/ELF/copy-errors.s
@@ -0,0 +1,15 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/protected-shared.s -o %t2.o
+// RUN: ld.lld %t2.o -o %t2.so -shared
+// RUN: not ld.lld %t.o %t2.so -o %t 2>&1 | FileCheck %s
+
+.global _start
+_start:
+
+
+call bar
+// CHECK: Cannot preempt symbol
+
+call zed
+// CHECK: Symbol is missing type
diff --git a/lld/test/ELF/copy-in-shared.s b/lld/test/ELF/copy-in-shared.s
index 1e670138687..273fffa5090 100644
--- a/lld/test/ELF/copy-in-shared.s
+++ b/lld/test/ELF/copy-in-shared.s
@@ -1,14 +1,10 @@
+// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/copy-in-shared.s -o %t1.o
// RUN: ld.lld -shared %t1.o -o %t1.so
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t2.o
-// RUN: ld.lld %t2.o %t1.so -o %t2.so -shared
-// RUN: llvm-readobj -r %t2.so | FileCheck %s
-// REQUIRES: x86
+// RUN: not ld.lld %t2.o %t1.so -o %t2.so -shared 2>&1 | FileCheck %s
+
.quad foo
-// CHECK: Relocations [
-// CHECK-NEXT: Section ({{.*}}) .rela.dyn {
-// CHECK-NEXT: R_X86_64_64 foo 0x0
-// CHECK-NEXT: }
-// CHECK-NEXT: ]
+// CHECK: relocation R_X86_64_64 cannot be used when making a shared object; recompile with -fPIC.
diff --git a/lld/test/ELF/eh-frame-dyn-rel.s b/lld/test/ELF/eh-frame-dyn-rel.s
index 47827a7143a..612e5fd0c20 100644
--- a/lld/test/ELF/eh-frame-dyn-rel.s
+++ b/lld/test/ELF/eh-frame-dyn-rel.s
@@ -1,13 +1,10 @@
// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
-// RUN: ld.lld %t.o %t.o -o %t -shared
-// RUN: llvm-readobj -r %t | FileCheck %s
+// RUN: not ld.lld %t.o %t.o -o %t -shared 2>&1 | FileCheck %s
.section bar,"axG",@progbits,foo,comdat
.cfi_startproc
.cfi_personality 0x8c, foo
.cfi_endproc
-// CHECK: Section ({{.*}}) .rela.dyn {
-// CHECK-NEXT: 0x1DA R_X86_64_64 foo 0x0
-// CHECK-NEXT: }
+// CHECK: relocation R_X86_64_64 cannot be used when making a shared object; recompile with -fPIC.
diff --git a/lld/test/ELF/i386-got-and-copy.s b/lld/test/ELF/i386-got-and-copy.s
index 3d812d9fbd6..f5b0b8ec5bb 100644
--- a/lld/test/ELF/i386-got-and-copy.s
+++ b/lld/test/ELF/i386-got-and-copy.s
@@ -15,7 +15,6 @@
# CHECK: Relocations [
# CHECK-NEXT: Section (4) .rel.dyn {
# CHECK-NEXT: 0x{{[0-9A-F]+}} R_386_COPY foo
-# CHECK-NEXT: 0x{{[0-9A-F]+}} R_386_GLOB_DAT foo
# CHECK-NEXT: }
# CHECK-NEXT: ]
diff --git a/lld/test/ELF/undef-with-plt-addr.s b/lld/test/ELF/undef-with-plt-addr.s
index 89a85dfa684..792d85f3da6 100644
--- a/lld/test/ELF/undef-with-plt-addr.s
+++ b/lld/test/ELF/undef-with-plt-addr.s
@@ -3,13 +3,15 @@
// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/undef-with-plt-addr.s -o %t2.o
// RUN: ld.lld %t2.o -o %t2.so -shared
// RUN: ld.lld %t.o %t2.so -o %t3
-// RUN: llvm-readobj -t -s %t3 | FileCheck %s
+// RUN: llvm-readobj -t -s -r %t3 | FileCheck %s
.globl _start
_start:
movabsq $set_data, %rax
-// Test that set_data has an address in the .plt
+.data
+.quad foo
+// Test that set_data has an address in the .plt, but foo is not
// CHECK: Name: .plt
// CHECK-NEXT: Type: SHT_PROGBITS
@@ -19,5 +21,25 @@ movabsq $set_data, %rax
// CHECK-NEXT: ]
// CHECK-NEXT: Address: 0x11010
+// CHECK: Section ({{.*}}) .rela.dyn {
+// CHECK-NEXT: 0x13000 R_X86_64_64 foo 0x0
+// CHECK-NEXT: }
+// CHECK-NEXT: Section ({{.*}}) .rela.plt {
+// CHECK-NEXT: 0x13020 R_X86_64_JUMP_SLOT set_data 0x0
+// CHECK-NEXT: }
+
+// CHECK: Name: foo
+// CHECK-NEXT: Value: 0x0
+// CHECK-NEXT: Size: 0
+// CHECK-NEXT: Binding: Global
+// CHECK-NEXT: Type: Function
+// CHECK-NEXT: Other: 0
+// CHECK-NEXT: Section: Undefined
+
// CHECK: Name: set_data
// CHECK-NEXT: Value: 0x11020
+// CHECK-NEXT: Size: 0
+// CHECK-NEXT: Binding: Global
+// CHECK-NEXT: Type: Function
+// CHECK-NEXT: Other: 0
+// CHECK-NEXT: Section: Undefined
diff --git a/lld/test/ELF/x86-64-reloc-32-fpic.s b/lld/test/ELF/x86-64-reloc-32-fpic.s
index d46c5531fa8..b47c0a36c5f 100644
--- a/lld/test/ELF/x86-64-reloc-32-fpic.s
+++ b/lld/test/ELF/x86-64-reloc-32-fpic.s
@@ -1,6 +1,6 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
# RUN: not ld.lld -shared %t.o -o %t.so 2>&1 | FileCheck %s
-# CHECK: R_X86_64_32 cannot be a dynamic relocation
+# CHECK: relocation R_X86_64_32 cannot be used when making a shared object; recompile with -fPIC.
.long _shared
diff --git a/lld/test/ELF/x86-64-reloc-pc32-fpic.s b/lld/test/ELF/x86-64-reloc-pc32-fpic.s
index 6723177f729..83bf6659098 100644
--- a/lld/test/ELF/x86-64-reloc-pc32-fpic.s
+++ b/lld/test/ELF/x86-64-reloc-pc32-fpic.s
@@ -1,6 +1,6 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
# RUN: not ld.lld -shared %t.o -o %t.so 2>&1 | FileCheck %s
-# CHECK: R_X86_64_PC32 cannot be a dynamic relocation
+# CHECK: relocation R_X86_64_PC32 cannot be used when making a shared object; recompile with -fPIC.
call _shared
OpenPOWER on IntegriCloud