Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -11,6 +11,7 @@ #include "Config.h" #include "Error.h" #include "InputFiles.h" +#include "OutputSections.h" #include "SymbolTable.h" #include "Target.h" #include "Writer.h" @@ -251,6 +252,11 @@ 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. + if (Config->EMachine == EM_MIPS) + Symtab.addAbsoluteSym("_gp", Out::MipsGpSym); + for (std::unique_ptr &F : Files) Symtab.addFile(std::move(F)); Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ 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(); } @@ -353,6 +351,7 @@ // globally accessible. Writer initializes them, so don't use them // until Writer is initialized. template struct Out { + typedef typename llvm::object::ELFFile::Elf_Sym Elf_Sym; typedef typename llvm::object::ELFFile::uintX_t uintX_t; static DynamicSection *Dynamic; static GnuHashTableSection *GnuHashTab; @@ -371,6 +370,7 @@ static StringTableSection *StrTab; static SymbolTableSection *DynSymTab; static SymbolTableSection *SymTab; + static Elf_Sym MipsGpSym; static uintX_t TlsInitImageVA; }; @@ -391,6 +391,7 @@ template StringTableSection *Out::StrTab; template SymbolTableSection *Out::DynSymTab; template SymbolTableSection *Out::SymTab; +template typename Out::Elf_Sym Out::MipsGpSym; template typename Out::uintX_t Out::TlsInitImageVA; } // namespace elf2 Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -76,7 +76,7 @@ } template void GotSection::addEntry(SymbolBody *Sym) { - Sym->GotIndex = Entries.size(); + Sym->GotIndex = Target->getGotHeaderEntriesNum() + Entries.size(); Entries.push_back(Sym); } @@ -86,7 +86,14 @@ 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); Index: ELF/SymbolTable.h =================================================================== --- ELF/SymbolTable.h +++ 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: ELF/SymbolTable.cpp =================================================================== --- ELF/SymbolTable.cpp +++ 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: ELF/Target.h =================================================================== --- ELF/Target.h +++ 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: ELF/Target.cpp =================================================================== --- ELF/Target.cpp +++ 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; @@ -666,6 +677,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 @@ -680,7 +701,7 @@ template bool MipsTargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { - return false; + return Type == R_MIPS_GOT16; } template @@ -698,9 +719,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->empty() ? 0 : (Out::Got->getVA() + GPOffset); +} + +template uint32_t getMipsGpAddr(); +template uint32_t getMipsGpAddr(); +template uint64_t getMipsGpAddr(); +template uint64_t getMipsGpAddr(); } } Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -759,6 +759,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) + Out::MipsGpSym.st_value = getMipsGpAddr(); } // Returns the number of PHDR entries. Index: test/elf2/basic-mips.s =================================================================== --- test/elf2/basic-mips.s +++ 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: test/elf2/mips-got-relocs.s =================================================================== --- /dev/null +++ test/elf2/mips-got-relocs.s @@ -0,0 +1,50 @@ +# 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 %s +# RUN: llvm-objdump -s -section=.got %t-be.exe | FileCheck -check-prefix=BEGOT %s +# RUN: llvm-objdump -d %t-be.exe | FileCheck -check-prefix=BEDIS %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 %s +# RUN: llvm-objdump -s -section=.got %t-el.exe | FileCheck -check-prefix=ELGOT %s +# RUN: llvm-objdump -d %t-el.exe | FileCheck -check-prefix=ELDIS %s + +# REQUIRES: mips + + .text + .globl __start +__start: + lui $2, %got(v1) + + .data + .globl v1 + .type v1,@object + .size v1,4 +v1: + .word 0 + +# CHECK: Sections: +# CHECK: .got 0000000c 0000000000030004 DATA +# CHECK: SYMBOL TABLE: +# CHECK: 00037ff4 *ABS* 00000000 _gp +# ^-- .got + GP offset (0x7ff0) +# CHECK: 00030000 g .data 00000004 v1 + +# BEGOT: Contents of section .got: +# BEGOT: 30004 00000000 80000000 00030000 +# ^ ^ ^-- v1 (0x30000) +# | +-- Module pointer (0x80000000) +# +-- Lazy resolver (0x0) + +# ELGOT: Contents of section .got: +# ELGOT: 30004 00000000 00000080 00000300 +# ^ ^ ^-- v1 (0x30000) +# | +-- Module pointer (0x80000000) +# +-- Lazy resolver (0x0) + +# v1GotAddr(0x3000c) - _gp (0x37ff4) = -0x7fe8 => 0x8018 = 32792 +# BEDIS: 20000: 3c 02 80 18 lui $2, 32792 +# ELDIS: 20000: 18 80 02 3c lui $2, 32792