diff options
-rw-r--r-- | lld/ELF/InputSection.h | 3 | ||||
-rw-r--r-- | lld/ELF/Writer.cpp | 185 | ||||
-rw-r--r-- | lld/test/ELF/Inputs/protected-shared.s | 3 | ||||
-rw-r--r-- | lld/test/ELF/Inputs/undef-with-plt-addr.s | 4 | ||||
-rw-r--r-- | lld/test/ELF/copy-errors.s | 15 | ||||
-rw-r--r-- | lld/test/ELF/copy-in-shared.s | 12 | ||||
-rw-r--r-- | lld/test/ELF/eh-frame-dyn-rel.s | 7 | ||||
-rw-r--r-- | lld/test/ELF/i386-got-and-copy.s | 1 | ||||
-rw-r--r-- | lld/test/ELF/undef-with-plt-addr.s | 26 | ||||
-rw-r--r-- | lld/test/ELF/x86-64-reloc-32-fpic.s | 2 | ||||
-rw-r--r-- | lld/test/ELF/x86-64-reloc-pc32-fpic.s | 2 |
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 |