Don't create dynamic relocations to ro segments.
authorRafael Espindola <rafael.espindola@gmail.com>
Sat, 30 Apr 2016 01:15:17 +0000 (01:15 +0000)
committerRafael Espindola <rafael.espindola@gmail.com>
Sat, 30 Apr 2016 01:15:17 +0000 (01:15 +0000)
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

lld/ELF/InputSection.h
lld/ELF/Writer.cpp
lld/test/ELF/Inputs/protected-shared.s
lld/test/ELF/Inputs/undef-with-plt-addr.s
lld/test/ELF/copy-errors.s [new file with mode: 0644]
lld/test/ELF/copy-in-shared.s
lld/test/ELF/eh-frame-dyn-rel.s
lld/test/ELF/i386-got-and-copy.s
lld/test/ELF/undef-with-plt-addr.s
lld/test/ELF/x86-64-reloc-32-fpic.s
lld/test/ELF/x86-64-reloc-pc32-fpic.s

index 21e536f..4fef608 100644 (file)
@@ -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 {
index 86915f3..ca668eb 100644 (file)
@@ -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;
index 342c379..5f4b1da 100644 (file)
@@ -5,3 +5,6 @@ foo:
         .global bar
         .protected bar
 bar:
+
+        .global zed
+zed:
index 4b3a5d3..b12737d 100644 (file)
@@ -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 (file)
index 0000000..bc28e32
--- /dev/null
@@ -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
index 1e67013..273fffa 100644 (file)
@@ -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.
index 47827a7..612e5fd 100644 (file)
@@ -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.
index 3d812d9..f5b0b8e 100644 (file)
@@ -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: ]
 
index 89a85df..792d85f 100644 (file)
@@ -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
index d46c553..b47c0a3 100644 (file)
@@ -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
index 6723177..83bf665 100644 (file)
@@ -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