From 8469b8841c4bbb8276cfb6b172a18d9dea8b093a Mon Sep 17 00:00:00 2001 From: Simon Atanasyan Date: Wed, 23 Nov 2016 22:22:16 +0000 Subject: [PATCH] [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 llvm-svn: 287832 --- lld/ELF/InputSection.cpp | 19 ++++++++++++------- lld/ELF/Symbols.h | 10 +++++++--- lld/ELF/SyntheticSections.cpp | 12 ++++++++---- lld/ELF/SyntheticSections.h | 2 ++ lld/ELF/Target.h | 2 -- lld/ELF/Writer.cpp | 22 ++++++++++++++++------ lld/test/ELF/mips-gp-ext.s | 41 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 86 insertions(+), 22 deletions(-) create mode 100644 lld/test/ELF/mips-gp-ext.s diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index e2ba2af..998e8c6 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -389,21 +389,26 @@ static typename ELFT::uint getSymVA(uint32_t Type, typename ELFT::uint A, // 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::MipsGot->getPageEntryOffset(Body.getVA(A)); + return In::MipsGot->getVA() + + In::MipsGot->getPageEntryOffset(Body.getVA(A)) - + In::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::MipsGot->getBodyEntryOffset(Body, A); + return In::MipsGot->getVA() + + In::MipsGot->getBodyEntryOffset(Body, A) - + In::MipsGot->getGp(); case R_MIPS_GOTREL: - return Body.getVA(A) - In::MipsGot->getVA() - MipsGPOffset; + return Body.getVA(A) - In::MipsGot->getGp(); case R_MIPS_TLSGD: - return In::MipsGot->getGlobalDynOffset(Body) + - In::MipsGot->getTlsOffset() - MipsGPOffset; + return In::MipsGot->getVA() + In::MipsGot->getTlsOffset() + + In::MipsGot->getGlobalDynOffset(Body) - + In::MipsGot->getGp(); case R_MIPS_TLSLD: - return In::MipsGot->getTlsIndexOff() + - In::MipsGot->getTlsOffset() - MipsGPOffset; + return In::MipsGot->getVA() + In::MipsGot->getTlsOffset() + + In::MipsGot->getTlsIndexOff() - In::MipsGot->getGp(); case R_PPC_OPD: { uint64_t SymVA = Body.getVA(A); // If we have an undefined weak symbol, we might get here with a symbol diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index 8bce7bf..43dd518 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -361,8 +361,10 @@ template struct ElfSym { static DefinedRegular *End; static DefinedRegular *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 *MipsGpDisp; + static DefinedRegular *MipsLocalGp; + static SymbolBody *MipsGp; }; template DefinedRegular *ElfSym::EhdrStart; @@ -372,7 +374,9 @@ template DefinedRegular *ElfSym::Edata; template DefinedRegular *ElfSym::Edata2; template DefinedRegular *ElfSym::End; template DefinedRegular *ElfSym::End2; -template SymbolBody *ElfSym::MipsGpDisp; +template DefinedRegular *ElfSym::MipsGpDisp; +template DefinedRegular *ElfSym::MipsLocalGp; +template SymbolBody *ElfSym::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 diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index 5918f0b..d659854 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -177,7 +177,7 @@ template void MipsOptionsSection::writeTo(uint8_t *Buf) { Options->size = getSize(); if (!Config->Relocatable) - Reginfo.ri_gp_value = In::MipsGot->getVA() + MipsGPOffset; + Reginfo.ri_gp_value = In::MipsGot->getGp(); memcpy(Buf + sizeof(typename ELFT::uint), &Reginfo, sizeof(Reginfo)); } @@ -233,7 +233,7 @@ MipsReginfoSection::MipsReginfoSection(Elf_Mips_RegInfo Reginfo) template void MipsReginfoSection::writeTo(uint8_t *Buf) { if (!Config->Relocatable) - Reginfo.ri_gp_value = In::MipsGot->getVA() + MipsGPOffset; + Reginfo.ri_gp_value = In::MipsGot->getGp(); memcpy(Buf, &Reginfo, sizeof(Reginfo)); } @@ -546,7 +546,7 @@ MipsGotSection::getPageEntryOffset(uintX_t EntryValue) { 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 @@ -572,7 +572,7 @@ MipsGotSection::getBodyEntryOffset(const SymbolBody &B, assert(It != EntryIndexMap.end()); GotIndex = It->second; } - return GotBlockOff + GotIndex * sizeof(uintX_t) - MipsGPOffset; + return GotBlockOff + GotIndex * sizeof(uintX_t); } template @@ -614,6 +614,10 @@ template void MipsGotSection::finalize() { Size = EntriesNum * sizeof(uintX_t); } +template unsigned MipsGotSection::getGp() const { + return ElfSym::MipsGp->template getVA(0); +} + template static void writeUint(uint8_t *Buf, typename ELFT::uint Val) { typedef typename ELFT::uint uintX_t; diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h index ecb20f3..45c2d02 100644 --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -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: diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index cd01ad5..94981493 100644 --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -105,8 +105,6 @@ public: std::string toString(uint32_t RelType); uint64_t getPPC64TocBase(); -const unsigned MipsGPOffset = 0x7ff0; - extern TargetInfo *Target; TargetInfo *createTarget(); } diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 0285e8a..39ece19 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -605,22 +605,22 @@ template void Writer::addReservedSymbols() { 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::MipsGot, MipsGPOffset); + ElfSym::MipsGp = addRegular("_gp", In::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::MipsGot, MipsGPOffset); - if (Sym) - ElfSym::MipsGpDisp = Sym->body(); + if (Symbol *S = addOptionalRegular("_gp_disp", In::MipsGot, 0)) + ElfSym::MipsGpDisp = cast>(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::MipsGot, MipsGPOffset); + if (Symbol *S = addOptionalRegular("__gnu_local_gp", In::MipsGot, 0)) + ElfSym::MipsLocalGp = cast>(S->body()); } // In the assembly for 32 bit x86 the _GLOBAL_OFFSET_TABLE_ symbol @@ -1413,6 +1413,16 @@ template void Writer::fixAbsoluteSymbols() { else Set(ElfSym::Etext, ElfSym::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::MipsGot->getGp() - In::MipsGot->getVA(); + if (ElfSym::MipsGpDisp) + ElfSym::MipsGpDisp->Value = GpDisp; + if (ElfSym::MipsLocalGp) + ElfSym::MipsLocalGp->Value = GpDisp; + } } template void Writer::writeHeader() { diff --git a/lld/test/ELF/mips-gp-ext.s b/lld/test/ELF/mips-gp-ext.s new file mode 100644 index 0000000..780489a1 --- /dev/null +++ b/lld/test/ELF/mips-gp-ext.s @@ -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 -- 2.7.4