diff options
-rw-r--r-- | lld/ELF/InputFiles.cpp | 10 | ||||
-rw-r--r-- | lld/ELF/InputFiles.h | 8 | ||||
-rw-r--r-- | lld/ELF/InputSection.cpp | 13 | ||||
-rw-r--r-- | lld/ELF/InputSection.h | 1 | ||||
-rw-r--r-- | lld/ELF/Target.cpp | 10 | ||||
-rw-r--r-- | lld/test/ELF/mips-gprel32-relocs.test | 31 |
6 files changed, 71 insertions, 2 deletions
diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index a68d83626d3..e0827a3ee43 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -93,6 +93,10 @@ typename ObjectFile<ELFT>::Elf_Sym_Range ObjectFile<ELFT>::getLocalSymbols() { return this->getSymbolsHelper(true); } +template <class ELFT> uint32_t ObjectFile<ELFT>::getMipsGp0() const { + return MipsReginfo ? MipsReginfo->getGp0() : 0; +} + template <class ELFT> const typename ObjectFile<ELFT>::Elf_Sym * ObjectFile<ELFT>::getLocalSymbol(uintX_t SymIndex) { @@ -245,8 +249,10 @@ elf2::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec) { // A MIPS object file has a special section that contains register // usage info, which needs to be handled by the linker specially. - if (Config->EMachine == EM_MIPS && Name == ".reginfo") - return new (this->Alloc) MipsReginfoInputSection<ELFT>(this, &Sec); + if (Config->EMachine == EM_MIPS && Name == ".reginfo") { + MipsReginfo = new (this->Alloc) MipsReginfoInputSection<ELFT>(this, &Sec); + return MipsReginfo; + } if (Name == ".eh_frame") return new (this->EHAlloc.Allocate()) EHInputSection<ELFT>(this, &Sec); diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h index 8363acb9f2b..4e529c558cf 100644 --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -121,6 +121,11 @@ public: const Elf_Shdr *getSymbolTable() const { return this->Symtab; }; + // Get MIPS GP0 value defined by this file. This value represents the gp value + // used to create the relocatable object and required to support + // R_MIPS_GPREL16 / R_MIPS_GPREL32 relocations. + uint32_t getMipsGp0() const; + private: void initializeSections(llvm::DenseSet<StringRef> &Comdats); void initializeSymbols(); @@ -134,6 +139,9 @@ private: // List of all symbols referenced or defined by this file. std::vector<SymbolBody *> SymbolBodies; + // MIPS .reginfo section defined by this file. + MipsReginfoInputSection<ELFT> *MipsReginfo = nullptr; + llvm::BumpPtrAllocator Alloc; llvm::SpecificBumpPtrAllocator<MergeInputSection<ELFT>> MAlloc; llvm::SpecificBumpPtrAllocator<EHInputSection<ELFT>> EHAlloc; diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index 1b45ec7a559..2548200feb6 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -187,6 +187,12 @@ void InputSectionBase<ELFT>::relocate(uint8_t *Buf, uint8_t *BufEnd, uintX_t A = getAddend<ELFT>(RI); if (!Body) { uintX_t SymVA = getLocalRelTarget(*File, RI, A); + // We need to adjust SymVA value in case of R_MIPS_GPREL16/32 relocations + // because they use the following expression to calculate the relocation's + // result for local symbol: S + A + GP0 - G. + if (Config->EMachine == EM_MIPS && + (Type == R_MIPS_GPREL16 || Type == R_MIPS_GPREL32)) + SymVA += File->getMipsGp0(); Target->relocateOne(BufLoc, BufEnd, Type, AddrLoc, SymVA, 0, findMipsPairedReloc(Buf, SymIndex, Type, NextRelocs)); continue; @@ -351,6 +357,13 @@ uint32_t MipsReginfoInputSection<ELFT>::getGeneralMask() const { return reinterpret_cast<const Elf_Mips_RegInfo *>(D.data())->ri_gprmask; } +template <class ELFT> uint32_t MipsReginfoInputSection<ELFT>::getGp0() const { + ArrayRef<uint8_t> D = this->getSectionData(); + if (D.size() != sizeof(Elf_Mips_RegInfo)) + error("Invalid size of .reginfo section"); + return reinterpret_cast<const Elf_Mips_RegInfo *>(D.data())->ri_gp_value; +} + template <class ELFT> bool MipsReginfoInputSection<ELFT>::classof(const InputSectionBase<ELFT> *S) { return S->SectionKind == InputSectionBase<ELFT>::MipsReginfo; diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h index 2f955c2f687..d4dc9864ae9 100644 --- a/lld/ELF/InputSection.h +++ b/lld/ELF/InputSection.h @@ -176,6 +176,7 @@ public: MipsReginfoInputSection(ObjectFile<ELFT> *F, const Elf_Shdr *Header); uint32_t getGeneralMask() const; + uint32_t getGp0() const; static bool classof(const InputSectionBase<ELFT> *S); }; diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp index a80c672b8d2..8d848d040c6 100644 --- a/lld/ELF/Target.cpp +++ b/lld/ELF/Target.cpp @@ -1382,6 +1382,16 @@ void MipsTargetInfo<ELFT>::relocateOne(uint8_t *Loc, uint8_t *BufEnd, write32<E>(Loc, (read32<E>(Loc) & 0xffff0000) | (V & 0xffff)); break; } + case R_MIPS_GPREL16: { + uint32_t Instr = read32<E>(Loc); + int64_t V = SA + SignExtend64<16>(Instr & 0xffff) - getMipsGpAddr<ELFT>(); + checkInt<16>(V, Type); + write32<E>(Loc, (Instr & 0xffff0000) | (V & 0xffff)); + break; + } + case R_MIPS_GPREL32: + write32<E>(Loc, SA + int32_t(read32<E>(Loc)) - getMipsGpAddr<ELFT>()); + break; case R_MIPS_HI16: { uint32_t Instr = read32<E>(Loc); if (PairedLoc) { diff --git a/lld/test/ELF/mips-gprel32-relocs.test b/lld/test/ELF/mips-gprel32-relocs.test new file mode 100644 index 00000000000..4f93d50363c --- /dev/null +++ b/lld/test/ELF/mips-gprel32-relocs.test @@ -0,0 +1,31 @@ +# Check R_MIPS_GPREL32 relocation calculation. + +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o +# RUN: ld.lld -shared -o %t.so %t.o +# RUN: llvm-objdump -s -section=.rodata -t %t.so | FileCheck %s + +# REQUIRES: mips + + .text + .globl __start +__start: + lw $t0,%call16(__start)($gp) +foo: + nop +bar: + nop + + .section .rodata, "a" +v1: + .gpword foo + .gpword bar + +# CHECK: Contents of section .rodata: +# CHECK: 0114 fffe8014 fffe8018 +# ^ 0x10004 - 0x27ff0 +# ^ 0x10008 - 0x27ff0 + +# CHECK: SYMBOL TABLE: +# CHECK: 00010008 .text 00000000 bar +# CHECK: 00010004 .text 00000000 foo +# CHECK: 00027ff0 *ABS* 00000000 _gp |