Index: lld/trunk/ELF/Driver.cpp =================================================================== --- lld/trunk/ELF/Driver.cpp +++ lld/trunk/ELF/Driver.cpp @@ -251,6 +251,13 @@ Symtab.addIgnoredSym("_GLOBAL_OFFSET_TABLE_"); } + // 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. + // See "Global Data Symbols" in Chapter 6 in the following document: + // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf + if (Config->EMachine == EM_MIPS) + Symtab.addAbsoluteSym("_gp", DefinedAbsolute::MipsGp); + for (std::unique_ptr &F : Files) Symtab.addFile(std::move(F)); Index: lld/trunk/ELF/OutputSections.h =================================================================== --- lld/trunk/ELF/OutputSections.h +++ lld/trunk/ELF/OutputSections.h @@ -113,9 +113,7 @@ public: GotSection(); - void finalize() override { - this->Header.sh_size = Entries.size() * sizeof(uintX_t); - } + void finalize() override; void writeTo(uint8_t *Buf) override; void addEntry(SymbolBody *Sym); bool empty() const { return Entries.empty(); } Index: lld/trunk/ELF/OutputSections.cpp =================================================================== --- lld/trunk/ELF/OutputSections.cpp +++ lld/trunk/ELF/OutputSections.cpp @@ -72,11 +72,13 @@ GotSection::GotSection() : OutputSectionBase(".got", llvm::ELF::SHT_PROGBITS, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE) { + if (Config->EMachine == EM_MIPS) + this->Header.sh_flags |= llvm::ELF::SHF_MIPS_GPREL; this->Header.sh_addralign = sizeof(uintX_t); } template void GotSection::addEntry(SymbolBody *Sym) { - Sym->GotIndex = Entries.size(); + Sym->GotIndex = Target->getGotHeaderEntriesNum() + Entries.size(); Entries.push_back(Sym); } @@ -86,11 +88,23 @@ return this->getVA() + B.GotIndex * sizeof(uintX_t); } +template void GotSection::finalize() { + this->Header.sh_size = + (Target->getGotHeaderEntriesNum() + Entries.size()) * sizeof(uintX_t); +} + template void GotSection::writeTo(uint8_t *Buf) { + Target->writeGotHeaderEntries(Buf); + Buf += Target->getGotHeaderEntriesNum() * sizeof(uintX_t); for (const SymbolBody *B : Entries) { uint8_t *Entry = Buf; Buf += sizeof(uintX_t); - if (canBePreempted(B, false)) + // MIPS has special rules to fill up GOT entries. + // See "Global Offset Table" in Chapter 5 in the following document + // for detailed description: + // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf + // As the first approach, we can just store addresses for all symbols. + if (Config->EMachine != EM_MIPS && canBePreempted(B, false)) continue; // The dynamic linker will take care of it. uintX_t VA = getSymVA(*B); write(Entry, VA); Index: lld/trunk/ELF/SymbolTable.h =================================================================== --- lld/trunk/ELF/SymbolTable.h +++ lld/trunk/ELF/SymbolTable.h @@ -50,6 +50,8 @@ SymbolBody *addUndefined(StringRef Name); SymbolBody *addUndefinedOpt(StringRef Name); + void addAbsoluteSym(StringRef Name, + typename llvm::object::ELFFile::Elf_Sym &ESym); void addSyntheticSym(StringRef Name, OutputSectionBase &Section, typename llvm::object::ELFFile::uintX_t Value); void addIgnoredSym(StringRef Name); Index: lld/trunk/ELF/SymbolTable.cpp =================================================================== --- lld/trunk/ELF/SymbolTable.cpp +++ lld/trunk/ELF/SymbolTable.cpp @@ -71,6 +71,12 @@ } template +void SymbolTable::addAbsoluteSym(StringRef Name, + typename ELFFile::Elf_Sym &ESym) { + resolve(new (Alloc) DefinedAbsolute(Name, ESym)); +} + +template void SymbolTable::addSyntheticSym(StringRef Name, OutputSectionBase &Section, typename ELFFile::uintX_t Value) { Index: lld/trunk/ELF/Symbols.h =================================================================== --- lld/trunk/ELF/Symbols.h +++ lld/trunk/ELF/Symbols.h @@ -177,6 +177,11 @@ public: static Elf_Sym IgnoreUndef; + // The content for _gp symbol for MIPS target. + // The symbol has to be added early to reserve a place in symbol tables. + // The value of the symbol is computed later by Writer. + static Elf_Sym MipsGp; + DefinedAbsolute(StringRef N, const Elf_Sym &Sym) : Defined(Base::DefinedAbsoluteKind, N, Sym) {} @@ -188,6 +193,9 @@ template typename DefinedAbsolute::Elf_Sym DefinedAbsolute::IgnoreUndef; +template +typename DefinedAbsolute::Elf_Sym DefinedAbsolute::MipsGp; + template class DefinedCommon : public Defined { typedef ELFSymbolBody Base; typedef typename Base::Elf_Sym Elf_Sym; Index: lld/trunk/ELF/Target.h =================================================================== --- lld/trunk/ELF/Target.h +++ lld/trunk/ELF/Target.h @@ -11,6 +11,7 @@ #define LLD_ELF_TARGET_H #include "llvm/ADT/StringRef.h" +#include "llvm/Object/ELF.h" #include @@ -31,7 +32,9 @@ unsigned getPltZeroEntrySize() const { return PltZeroEntrySize; } unsigned getPltEntrySize() const { return PltEntrySize; } bool supportsLazyRelocations() const { return LazyRelocations; } + unsigned getGotHeaderEntriesNum() const { return GotHeaderEntriesNum; } virtual unsigned getPLTRefReloc(unsigned Type) const; + virtual void writeGotHeaderEntries(uint8_t *Buf) const; virtual void writeGotPltEntry(uint8_t *Buf, uint64_t Plt) const = 0; virtual void writePltZeroEntry(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr) const = 0; @@ -66,11 +69,15 @@ unsigned RelativeReloc; unsigned PltEntrySize = 8; unsigned PltZeroEntrySize = 0; + unsigned GotHeaderEntriesNum = 0; bool LazyRelocations = false; }; uint64_t getPPC64TocBase(); +template +typename llvm::object::ELFFile::uintX_t getMipsGpAddr(); + extern std::unique_ptr Target; TargetInfo *createTarget(); } Index: lld/trunk/ELF/Target.cpp =================================================================== --- lld/trunk/ELF/Target.cpp +++ lld/trunk/ELF/Target.cpp @@ -35,6 +35,14 @@ std::unique_ptr Target; +template static uint32_t read32(const uint8_t *L); +template <> uint32_t read32(const uint8_t *L) { return read32le(L); } +template <> uint32_t read32(const uint8_t *L) { return read32be(L); } + +template static void write32(uint8_t *L, uint32_t V); +template <> void write32(uint8_t *L, uint32_t V) { write32le(L, V); } +template <> void write32(uint8_t *L, uint32_t V) { write32be(L, V); } + static void add32le(uint8_t *L, int32_t V) { write32le(L, read32le(L) + V); } static void add32be(uint8_t *L, int32_t V) { write32be(L, read32be(L) + V); } static void or32le(uint8_t *L, int32_t V) { write32le(L, read32le(L) | V); } @@ -108,6 +116,7 @@ template class MipsTargetInfo final : public TargetInfo { public: MipsTargetInfo(); + void writeGotHeaderEntries(uint8_t *Buf) const override; void writeGotPltEntry(uint8_t *Buf, uint64_t Plt) const override; void writePltZeroEntry(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr) const override; @@ -155,6 +164,8 @@ bool TargetInfo::isRelRelative(uint32_t Type) const { return true; } +void TargetInfo::writeGotHeaderEntries(uint8_t *Buf) const {} + X86TargetInfo::X86TargetInfo() { PCRelReloc = R_386_PC32; GotReloc = R_386_GLOB_DAT; @@ -670,6 +681,16 @@ template MipsTargetInfo::MipsTargetInfo() { PageSize = 65536; + GotRefReloc = R_MIPS_GOT16; + GotHeaderEntriesNum = 2; +} + +template +void MipsTargetInfo::writeGotHeaderEntries(uint8_t *Buf) const { + typedef typename llvm::object::ELFFile::Elf_Off Elf_Off; + auto *P = reinterpret_cast(Buf); + // Module pointer + P[1] = ELFT::Is64Bits ? 0x8000000000000000 : 0x80000000; } template @@ -684,7 +705,7 @@ template bool MipsTargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { - return false; + return Type == R_MIPS_GOT16; } template @@ -702,9 +723,27 @@ case R_MIPS_32: add32(Loc, SA); break; + case R_MIPS_GOT16: { + int64_t V = SA - getMipsGpAddr(); + if (!isInt<16>(V)) + error("Relocation R_MIPS_GOT16 out of range"); + write32(Loc, (read32(Loc) & 0xffff0000) | (V & 0xffff)); + break; + } default: error("unrecognized reloc " + Twine(Type)); } } + +template +typename llvm::object::ELFFile::uintX_t getMipsGpAddr() { + const unsigned GPOffset = 0x7ff0; + return Out::Got->getVA() ? (Out::Got->getVA() + GPOffset) : 0; +} + +template uint32_t getMipsGpAddr(); +template uint32_t getMipsGpAddr(); +template uint64_t getMipsGpAddr(); +template uint64_t getMipsGpAddr(); } } Index: lld/trunk/ELF/Writer.cpp =================================================================== --- lld/trunk/ELF/Writer.cpp +++ lld/trunk/ELF/Writer.cpp @@ -225,6 +225,15 @@ } } + if (Config->EMachine == EM_MIPS && NeedsGot) { + // MIPS ABI has special rules to process GOT entries + // and doesn't require relocation entries for them. + // See "Global Offset Table" in Chapter 5 in the following document + // for detailed description: + // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf + Body->setUsedInDynamicReloc(); + continue; + } bool CBP = canBePreempted(Body, NeedsGot); if (!CBP && (!Config->Shared || Target->isRelRelative(Type))) continue; @@ -762,6 +771,10 @@ // Add space for section headers. SectionHeaderOff = RoundUpToAlignment(FileOff, ELFT::Is64Bits ? 8 : 4); FileSize = SectionHeaderOff + getNumSections() * sizeof(Elf_Shdr); + + // Update MIPS _gp absolute symbol so that it points to the static data. + if (Config->EMachine == EM_MIPS) + DefinedAbsolute::MipsGp.st_value = getMipsGpAddr(); } // Returns the number of PHDR entries. Index: lld/trunk/test/elf2/basic-mips.s =================================================================== --- lld/trunk/test/elf2/basic-mips.s +++ lld/trunk/test/elf2/basic-mips.s @@ -27,7 +27,7 @@ # CHECK-NEXT: Version: 1 # CHECK-NEXT: Entry: 0x20000 # CHECK-NEXT: ProgramHeaderOffset: 0x34 -# CHECK-NEXT: SectionHeaderOffset: 0x20070 +# CHECK-NEXT: SectionHeaderOffset: 0x20084 # CHECK-NEXT: Flags [ (0x0) # CHECK-NEXT: ] # CHECK-NEXT: HeaderSize: 52 @@ -138,7 +138,7 @@ # CHECK-NEXT: ] # CHECK-NEXT: Address: 0x0 # CHECK-NEXT: Offset: 0x20000 -# CHECK-NEXT: Size: 32 +# CHECK-NEXT: Size: 48 # CHECK-NEXT: Link: 8 # CHECK-NEXT: Info: 1 # CHECK-NEXT: AddressAlignment: 4 @@ -151,7 +151,7 @@ # CHECK-NEXT: Flags [ (0x0) # CHECK-NEXT: ] # CHECK-NEXT: Address: 0x0 -# CHECK-NEXT: Offset: 0x20020 +# CHECK-NEXT: Offset: 0x20030 # CHECK-NEXT: Size: 68 # CHECK-NEXT: Link: 0 # CHECK-NEXT: Info: 0 @@ -165,8 +165,8 @@ # CHECK-NEXT: Flags [ (0x0) # CHECK-NEXT: ] # CHECK-NEXT: Address: 0x0 -# CHECK-NEXT: Offset: 0x20064 -# CHECK-NEXT: Size: 9 +# CHECK-NEXT: Offset: 0x20074 +# CHECK-NEXT: Size: 13 # CHECK-NEXT: Link: 0 # CHECK-NEXT: Info: 0 # CHECK-NEXT: AddressAlignment: 1 @@ -184,6 +184,15 @@ # CHECK-NEXT: Section: Undefined (0x0) # CHECK-NEXT: } # CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: _gp +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local (0x0) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Absolute (0xFFF1) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { # CHECK-NEXT: Name: __start # CHECK-NEXT: Value: 0x20000 # CHECK-NEXT: Size: 0 Index: lld/trunk/test/elf2/mips-got-relocs.s =================================================================== --- lld/trunk/test/elf2/mips-got-relocs.s +++ lld/trunk/test/elf2/mips-got-relocs.s @@ -0,0 +1,99 @@ +# Check R_MIPS_GOT16 relocation calculation. + +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t-be.o +# RUN: ld.lld2 %t-be.o -o %t-be.exe +# RUN: llvm-objdump -section-headers -t %t-be.exe | FileCheck -check-prefix=EXE_SYM %s +# RUN: llvm-objdump -s -section=.got %t-be.exe | FileCheck -check-prefix=EXE_GOT_BE %s +# RUN: llvm-objdump -d %t-be.exe | FileCheck -check-prefix=EXE_DIS_BE %s +# RUN: llvm-readobj -relocations %t-be.exe | FileCheck -check-prefix=NORELOC %s +# RUN: llvm-readobj -sections %t-be.exe | FileCheck -check-prefix=SHFLAGS %s + +# RUN: llvm-mc -filetype=obj -triple=mipsel-unknown-linux %s -o %t-el.o +# RUN: ld.lld2 %t-el.o -o %t-el.exe +# RUN: llvm-objdump -section-headers -t %t-el.exe | FileCheck -check-prefix=EXE_SYM %s +# RUN: llvm-objdump -s -section=.got %t-el.exe | FileCheck -check-prefix=EXE_GOT_EL %s +# RUN: llvm-objdump -d %t-el.exe | FileCheck -check-prefix=EXE_DIS_EL %s +# RUN: llvm-readobj -relocations %t-el.exe | FileCheck -check-prefix=NORELOC %s +# RUN: llvm-readobj -sections %t-el.exe | FileCheck -check-prefix=SHFLAGS %s + +# RUN: ld.lld2 -shared %t-be.o -o %t-be.so +# RUN: llvm-objdump -section-headers -t %t-be.so | FileCheck -check-prefix=DSO_SYM %s +# RUN: llvm-objdump -s -section=.got %t-be.so | FileCheck -check-prefix=DSO_GOT_BE %s +# RUN: llvm-objdump -d %t-be.so | FileCheck -check-prefix=DSO_DIS_BE %s +# RUN: llvm-readobj -relocations %t-be.so | FileCheck -check-prefix=NORELOC %s +# RUN: llvm-readobj -sections %t-be.so | FileCheck -check-prefix=SHFLAGS %s + +# RUN: ld.lld2 -shared %t-el.o -o %t-el.so +# RUN: llvm-objdump -section-headers -t %t-el.so | FileCheck -check-prefix=DSO_SYM %s +# RUN: llvm-objdump -s -section=.got %t-el.so | FileCheck -check-prefix=DSO_GOT_EL %s +# RUN: llvm-objdump -d %t-el.so | FileCheck -check-prefix=DSO_DIS_EL %s +# RUN: llvm-readobj -relocations %t-el.so | FileCheck -check-prefix=NORELOC %s +# RUN: llvm-readobj -sections %t-el.so | FileCheck -check-prefix=SHFLAGS %s + +# REQUIRES: mips + + .text + .globl __start +__start: + lui $2, %got(v1) + + .data + .globl v1 + .type v1,@object + .size v1,4 +v1: + .word 0 + +# EXE_SYM: Sections: +# EXE_SYM: .got 0000000c 0000000000030004 DATA +# EXE_SYM: SYMBOL TABLE: +# EXE_SYM: 00037ff4 *ABS* 00000000 _gp +# ^-- .got + GP offset (0x7ff0) +# EXE_SYM: 00030000 g .data 00000004 v1 + +# EXE_GOT_BE: Contents of section .got: +# EXE_GOT_BE: 30004 00000000 80000000 00030000 +# ^ ^ ^-- v1 (0x30000) +# | +-- Module pointer (0x80000000) +# +-- Lazy resolver (0x0) + +# EXE_GOT_EL: Contents of section .got: +# EXE_GOT_EL: 30004 00000000 00000080 00000300 +# ^ ^ ^-- v1 (0x30000) +# | +-- Module pointer (0x80000000) +# +-- Lazy resolver (0x0) + +# v1GotAddr (0x3000c) - _gp (0x37ff4) = -0x7fe8 => 0x8018 = 32792 +# EXE_DIS_BE: 20000: 3c 02 80 18 lui $2, 32792 +# EXE_DIS_EL: 20000: 18 80 02 3c lui $2, 32792 + +# DSO_SYM: Sections: +# DSO_SYM: .got 0000000c 0000000000020034 DATA +# DSO_SYM: SYMBOL TABLE: +# DSO_SYM: 00028024 *ABS* 00000000 _gp +# ^-- .got + GP offset (0x7ff0) +# DSO_SYM: 00020000 g .data 00000004 v1 + +# DSO_GOT_BE: Contents of section .got: +# DSO_GOT_BE: 20034 00000000 80000000 00020000 +# ^ ^ ^-- v1 (0x20000) +# | +-- Module pointer (0x80000000) +# +-- Lazy resolver (0x0) + +# DSO_GOT_EL: Contents of section .got: +# DSO_GOT_EL: 20034 00000000 00000080 00000200 +# ^ ^ ^-- v1 (0x20000) +# | +-- Module pointer (0x80000000) +# +-- Lazy resolver (0x0) + +# v1GotAddr (0x2003c) - _gp (0x28024) = -0x7fe8 => 0x8018 = 32792 +# DSO_DIS_BE: 10000: 3c 02 80 18 lui $2, 32792 +# DSO_DIS_EL: 10000: 18 80 02 3c lui $2, 32792 + +# NORELOC: Relocations [ +# NORELOC-NEXT: ] + +# SHFLAGS: Name: .got +# SHFLAGS-NEXT: Type: SHT_PROGBITS +# SHFLAGS-NEXT: Flags [ (0x10000003) +# ^-- SHF_MIPS_GPREL | SHF_ALLOC | SHF_WRITE