[lld] r287832 - [ELF][MIPS] Fix handling of _gp/_gp_disp/__gnu_local_gp symbols

Simon Atanasyan via llvm-commits llvm-commits at lists.llvm.org
Wed Nov 23 14:22:17 PST 2016


Author: atanasyan
Date: Wed Nov 23 16:22:16 2016
New Revision: 287832

URL: http://llvm.org/viewvc/llvm-project?rev=287832&view=rev
Log:
[ELF][MIPS] Fix handling of _gp/_gp_disp/__gnu_local_gp symbols

Offset between beginning of a .got section and _gp symbols used in MIPS
GOT relocations calculations. Usually the expression looks like
VA + Offset - GP, where VA is the .got section address, Offset - offset
of the GOT entry, GP - offset between .got and _gp. Also there two "magic"
symbols _gp_disp and __gnu_local_gp which hold the offset mentioned above.
These symbols might be referenced by MIPS relocations.

Now the linker always defines _gp symbol and uses hardcoded value for
its initialization. So offset between .got and _gp is 0x7ff0. The _gp_disp
and __gnu_local_gp defined if required and initialized by 0x7ff0.
In fact that is not correct because _gp symbol might be defined by a linker
script and holds arbitrary value. In that case we need to use this value
in relocation calculation and initialize _gp_disp and __gnu_local_gp
properly.

The patch fixes the problem and completes fixing the bug #30311.
https://llvm.org/bugs/show_bug.cgi?id=30311

Differential revision: https://reviews.llvm.org/D27036

Added:
    lld/trunk/test/ELF/mips-gp-ext.s
Modified:
    lld/trunk/ELF/InputSection.cpp
    lld/trunk/ELF/Symbols.h
    lld/trunk/ELF/SyntheticSections.cpp
    lld/trunk/ELF/SyntheticSections.h
    lld/trunk/ELF/Target.h
    lld/trunk/ELF/Writer.cpp

Modified: lld/trunk/ELF/InputSection.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/InputSection.cpp?rev=287832&r1=287831&r2=287832&view=diff
==============================================================================
--- lld/trunk/ELF/InputSection.cpp (original)
+++ lld/trunk/ELF/InputSection.cpp Wed Nov 23 16:22:16 2016
@@ -389,21 +389,26 @@ static typename ELFT::uint getSymVA(uint
     // If relocation against MIPS local symbol requires GOT entry, this entry
     // should be initialized by 'page address'. This address is high 16-bits
     // of sum the symbol's value and the addend.
-    return In<ELFT>::MipsGot->getPageEntryOffset(Body.getVA<ELFT>(A));
+    return In<ELFT>::MipsGot->getVA() +
+           In<ELFT>::MipsGot->getPageEntryOffset(Body.getVA<ELFT>(A)) -
+           In<ELFT>::MipsGot->getGp();
   case R_MIPS_GOT_OFF:
   case R_MIPS_GOT_OFF32:
     // In case of MIPS if a GOT relocation has non-zero addend this addend
     // should be applied to the GOT entry content not to the GOT entry offset.
     // That is why we use separate expression type.
-    return In<ELFT>::MipsGot->getBodyEntryOffset(Body, A);
+    return In<ELFT>::MipsGot->getVA() +
+           In<ELFT>::MipsGot->getBodyEntryOffset(Body, A) -
+           In<ELFT>::MipsGot->getGp();
   case R_MIPS_GOTREL:
-    return Body.getVA<ELFT>(A) - In<ELFT>::MipsGot->getVA() - MipsGPOffset;
+    return Body.getVA<ELFT>(A) - In<ELFT>::MipsGot->getGp();
   case R_MIPS_TLSGD:
-    return In<ELFT>::MipsGot->getGlobalDynOffset(Body) +
-           In<ELFT>::MipsGot->getTlsOffset() - MipsGPOffset;
+    return In<ELFT>::MipsGot->getVA() + In<ELFT>::MipsGot->getTlsOffset() +
+           In<ELFT>::MipsGot->getGlobalDynOffset(Body) -
+           In<ELFT>::MipsGot->getGp();
   case R_MIPS_TLSLD:
-    return In<ELFT>::MipsGot->getTlsIndexOff() +
-           In<ELFT>::MipsGot->getTlsOffset() - MipsGPOffset;
+    return In<ELFT>::MipsGot->getVA() + In<ELFT>::MipsGot->getTlsOffset() +
+           In<ELFT>::MipsGot->getTlsIndexOff() - In<ELFT>::MipsGot->getGp();
   case R_PPC_OPD: {
     uint64_t SymVA = Body.getVA<ELFT>(A);
     // If we have an undefined weak symbol, we might get here with a symbol

Modified: lld/trunk/ELF/Symbols.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Symbols.h?rev=287832&r1=287831&r2=287832&view=diff
==============================================================================
--- lld/trunk/ELF/Symbols.h (original)
+++ lld/trunk/ELF/Symbols.h Wed Nov 23 16:22:16 2016
@@ -361,8 +361,10 @@ template <class ELFT> struct ElfSym {
   static DefinedRegular<ELFT> *End;
   static DefinedRegular<ELFT> *End2;
 
-  // The content for _gp_disp symbol for MIPS target.
-  static SymbolBody *MipsGpDisp;
+  // The content for _gp_disp/__gnu_local_gp symbols for MIPS target.
+  static DefinedRegular<ELFT> *MipsGpDisp;
+  static DefinedRegular<ELFT> *MipsLocalGp;
+  static SymbolBody *MipsGp;
 };
 
 template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::EhdrStart;
@@ -372,7 +374,9 @@ template <class ELFT> DefinedRegular<ELF
 template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::Edata2;
 template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::End;
 template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::End2;
-template <class ELFT> SymbolBody *ElfSym<ELFT>::MipsGpDisp;
+template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::MipsGpDisp;
+template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::MipsLocalGp;
+template <class ELFT> SymbolBody *ElfSym<ELFT>::MipsGp;
 
 // A real symbol object, SymbolBody, is usually stored within a Symbol. There's
 // always one Symbol for each symbol name. The resolver updates the SymbolBody

Modified: lld/trunk/ELF/SyntheticSections.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/SyntheticSections.cpp?rev=287832&r1=287831&r2=287832&view=diff
==============================================================================
--- lld/trunk/ELF/SyntheticSections.cpp (original)
+++ lld/trunk/ELF/SyntheticSections.cpp Wed Nov 23 16:22:16 2016
@@ -177,7 +177,7 @@ template <class ELFT> void MipsOptionsSe
   Options->size = getSize();
 
   if (!Config->Relocatable)
-    Reginfo.ri_gp_value = In<ELFT>::MipsGot->getVA() + MipsGPOffset;
+    Reginfo.ri_gp_value = In<ELFT>::MipsGot->getGp();
   memcpy(Buf + sizeof(typename ELFT::uint), &Reginfo, sizeof(Reginfo));
 }
 
@@ -233,7 +233,7 @@ MipsReginfoSection<ELFT>::MipsReginfoSec
 
 template <class ELFT> void MipsReginfoSection<ELFT>::writeTo(uint8_t *Buf) {
   if (!Config->Relocatable)
-    Reginfo.ri_gp_value = In<ELFT>::MipsGot->getVA() + MipsGPOffset;
+    Reginfo.ri_gp_value = In<ELFT>::MipsGot->getGp();
   memcpy(Buf, &Reginfo, sizeof(Reginfo));
 }
 
@@ -546,7 +546,7 @@ MipsGotSection<ELFT>::getPageEntryOffset
   size_t NewIndex = PageIndexMap.size() + 2;
   auto P = PageIndexMap.insert(std::make_pair(EntryValue, NewIndex));
   assert(!P.second || PageIndexMap.size() <= PageEntriesNum);
-  return (uintX_t)P.first->second * sizeof(uintX_t) - MipsGPOffset;
+  return (uintX_t)P.first->second * sizeof(uintX_t);
 }
 
 template <class ELFT>
@@ -572,7 +572,7 @@ MipsGotSection<ELFT>::getBodyEntryOffset
     assert(It != EntryIndexMap.end());
     GotIndex = It->second;
   }
-  return GotBlockOff + GotIndex * sizeof(uintX_t) - MipsGPOffset;
+  return GotBlockOff + GotIndex * sizeof(uintX_t);
 }
 
 template <class ELFT>
@@ -614,6 +614,10 @@ template <class ELFT> void MipsGotSectio
   Size = EntriesNum * sizeof(uintX_t);
 }
 
+template <class ELFT> unsigned MipsGotSection<ELFT>::getGp() const {
+  return ElfSym<ELFT>::MipsGp->template getVA<ELFT>(0);
+}
+
 template <class ELFT>
 static void writeUint(uint8_t *Buf, typename ELFT::uint Val) {
   typedef typename ELFT::uint uintX_t;

Modified: lld/trunk/ELF/SyntheticSections.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/SyntheticSections.h?rev=287832&r1=287831&r2=287832&view=diff
==============================================================================
--- lld/trunk/ELF/SyntheticSections.h (original)
+++ lld/trunk/ELF/SyntheticSections.h Wed Nov 23 16:22:16 2016
@@ -122,6 +122,8 @@ public:
 
   uint32_t getTlsIndexOff() const { return TlsIndexOff; }
 
+  unsigned getGp() const;
+
 private:
   // MIPS GOT consists of three parts: local, global and tls. Each part
   // contains different types of entries. Here is a layout of GOT:

Modified: lld/trunk/ELF/Target.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Target.h?rev=287832&r1=287831&r2=287832&view=diff
==============================================================================
--- lld/trunk/ELF/Target.h (original)
+++ lld/trunk/ELF/Target.h Wed Nov 23 16:22:16 2016
@@ -105,8 +105,6 @@ public:
 std::string toString(uint32_t RelType);
 uint64_t getPPC64TocBase();
 
-const unsigned MipsGPOffset = 0x7ff0;
-
 extern TargetInfo *Target;
 TargetInfo *createTarget();
 }

Modified: lld/trunk/ELF/Writer.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Writer.cpp?rev=287832&r1=287831&r2=287832&view=diff
==============================================================================
--- lld/trunk/ELF/Writer.cpp (original)
+++ lld/trunk/ELF/Writer.cpp Wed Nov 23 16:22:16 2016
@@ -605,22 +605,22 @@ template <class ELFT> void Writer<ELFT>:
   if (Config->EMachine == EM_MIPS) {
     // Define _gp for MIPS. st_value of _gp symbol will be updated by Writer
     // so that it points to an absolute address which is relative to GOT.
+    // Default offset is 0x7ff0.
     // See "Global Data Symbols" in Chapter 6 in the following document:
     // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
-    addRegular("_gp", In<ELFT>::MipsGot, MipsGPOffset);
+    ElfSym<ELFT>::MipsGp = addRegular("_gp", In<ELFT>::MipsGot, 0x7ff0)->body();
 
     // On MIPS O32 ABI, _gp_disp is a magic symbol designates offset between
     // start of function and 'gp' pointer into GOT.
-    Symbol *Sym =
-        addOptionalRegular("_gp_disp", In<ELFT>::MipsGot, MipsGPOffset);
-    if (Sym)
-      ElfSym<ELFT>::MipsGpDisp = Sym->body();
+    if (Symbol *S = addOptionalRegular("_gp_disp", In<ELFT>::MipsGot, 0))
+      ElfSym<ELFT>::MipsGpDisp = cast<DefinedRegular<ELFT>>(S->body());
 
     // The __gnu_local_gp is a magic symbol equal to the current value of 'gp'
     // pointer. This symbol is used in the code generated by .cpload pseudo-op
     // in case of using -mno-shared option.
     // https://sourceware.org/ml/binutils/2004-12/msg00094.html
-    addOptionalRegular("__gnu_local_gp", In<ELFT>::MipsGot, MipsGPOffset);
+    if (Symbol *S = addOptionalRegular("__gnu_local_gp", In<ELFT>::MipsGot, 0))
+      ElfSym<ELFT>::MipsLocalGp = cast<DefinedRegular<ELFT>>(S->body());
   }
 
   // In the assembly for 32 bit x86 the _GLOBAL_OFFSET_TABLE_ symbol
@@ -1413,6 +1413,16 @@ template <class ELFT> void Writer<ELFT>:
     else
       Set(ElfSym<ELFT>::Etext, ElfSym<ELFT>::Etext2, Val);
   }
+
+  // Setup MIPS _gp_disp/__gnu_local_gp symbols which should
+  // be equal to the _gp symbol's value.
+  if (Config->EMachine == EM_MIPS) {
+    uintX_t GpDisp = In<ELFT>::MipsGot->getGp() - In<ELFT>::MipsGot->getVA();
+    if (ElfSym<ELFT>::MipsGpDisp)
+      ElfSym<ELFT>::MipsGpDisp->Value = GpDisp;
+    if (ElfSym<ELFT>::MipsLocalGp)
+      ElfSym<ELFT>::MipsLocalGp->Value = GpDisp;
+  }
 }
 
 template <class ELFT> void Writer<ELFT>::writeHeader() {

Added: lld/trunk/test/ELF/mips-gp-ext.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/mips-gp-ext.s?rev=287832&view=auto
==============================================================================
--- lld/trunk/test/ELF/mips-gp-ext.s (added)
+++ lld/trunk/test/ELF/mips-gp-ext.s Wed Nov 23 16:22:16 2016
@@ -0,0 +1,41 @@
+# Check that the linker use a value of _gp symbol defined
+# in a linker script to calculate GOT relocations.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o
+# RUN: echo "SECTIONS { \
+# RUN:          .text : { *(.text) } \
+# RUN:          _gp = . + 0x100; \
+# RUN:          .got  : { *(.got) } }" > %t.script
+# RUN: ld.lld -shared -o %t.so --script %t.script %t.o
+# RUN: llvm-objdump -s -t %t.so | FileCheck %s
+
+# REQUIRES: mips
+
+# CHECK:      Contents of section .text:
+# CHECK-NEXT:  0000 3c080000 2108010c 8f82ffe4
+#                   ^-- %hi(_gp_disp)
+#                            ^-- %lo(_gp_disp)
+#                                     ^-- 8 - (0x10c - 0xe8)
+#                                         G - (GP - .got)
+
+# CHECK:      Contents of section .reginfo:
+# CHECK-NEXT:  0028 10000104 00000000 00000000 00000000
+# CHECK-NEXT:  0038 00000000 0000010c
+#                            ^-- _gp
+
+# CHECK:      Contents of section .data:
+# CHECK-NEXT:  0100 fffffef4
+#                   ^-- 0-0x10c
+
+# CHECK: 00000000         .text           00000000 foo
+# CHECK: 0000010c         .got            00000000 .hidden _gp_disp
+# CHECK: 0000010c         .text           00000000 .hidden _gp
+
+  .text
+foo:
+  lui    $t0, %hi(_gp_disp)
+  addi   $t0, $t0, %lo(_gp_disp)
+  lw     $v0, %call16(bar)($gp)
+
+  .data
+  .gpword foo




More information about the llvm-commits mailing list