summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lld/ELF/Driver.cpp7
-rw-r--r--lld/ELF/OutputSections.cpp18
-rw-r--r--lld/ELF/OutputSections.h4
-rw-r--r--lld/ELF/SymbolTable.cpp6
-rw-r--r--lld/ELF/SymbolTable.h2
-rw-r--r--lld/ELF/Symbols.h8
-rw-r--r--lld/ELF/Target.cpp41
-rw-r--r--lld/ELF/Target.h7
-rw-r--r--lld/ELF/Writer.cpp13
-rw-r--r--lld/test/elf2/basic-mips.s19
-rw-r--r--lld/test/elf2/mips-got-relocs.s99
11 files changed, 213 insertions, 11 deletions
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 96ebabeaba0..6087a2142cc 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -251,6 +251,13 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
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<ELFT>::MipsGp);
+
for (std::unique_ptr<InputFile> &F : Files)
Symtab.addFile(std::move(F));
diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp
index 5007ba003fa..c2310ca617f 100644
--- a/lld/ELF/OutputSections.cpp
+++ b/lld/ELF/OutputSections.cpp
@@ -72,11 +72,13 @@ template <class ELFT>
GotSection<ELFT>::GotSection()
: OutputSectionBase<ELFT>(".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 <class ELFT> void GotSection<ELFT>::addEntry(SymbolBody *Sym) {
- Sym->GotIndex = Entries.size();
+ Sym->GotIndex = Target->getGotHeaderEntriesNum() + Entries.size();
Entries.push_back(Sym);
}
@@ -86,11 +88,23 @@ GotSection<ELFT>::getEntryAddr(const SymbolBody &B) const {
return this->getVA() + B.GotIndex * sizeof(uintX_t);
}
+template <class ELFT> void GotSection<ELFT>::finalize() {
+ this->Header.sh_size =
+ (Target->getGotHeaderEntriesNum() + Entries.size()) * sizeof(uintX_t);
+}
+
template <class ELFT> void GotSection<ELFT>::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<ELFT>(*B);
write<uintX_t, ELFT::TargetEndianness, sizeof(uintX_t)>(Entry, VA);
diff --git a/lld/ELF/OutputSections.h b/lld/ELF/OutputSections.h
index 8468da5b69f..a30f9979274 100644
--- a/lld/ELF/OutputSections.h
+++ b/lld/ELF/OutputSections.h
@@ -113,9 +113,7 @@ template <class ELFT> class GotSection final : public OutputSectionBase<ELFT> {
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(); }
diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp
index 6a0fa028469..f47035be9cc 100644
--- a/lld/ELF/SymbolTable.cpp
+++ b/lld/ELF/SymbolTable.cpp
@@ -71,6 +71,12 @@ SymbolBody *SymbolTable<ELFT>::addUndefinedOpt(StringRef Name) {
}
template <class ELFT>
+void SymbolTable<ELFT>::addAbsoluteSym(StringRef Name,
+ typename ELFFile<ELFT>::Elf_Sym &ESym) {
+ resolve(new (Alloc) DefinedAbsolute<ELFT>(Name, ESym));
+}
+
+template <class ELFT>
void SymbolTable<ELFT>::addSyntheticSym(StringRef Name,
OutputSectionBase<ELFT> &Section,
typename ELFFile<ELFT>::uintX_t Value) {
diff --git a/lld/ELF/SymbolTable.h b/lld/ELF/SymbolTable.h
index d05be7d83e1..a778aabbdb2 100644
--- a/lld/ELF/SymbolTable.h
+++ b/lld/ELF/SymbolTable.h
@@ -50,6 +50,8 @@ public:
SymbolBody *addUndefined(StringRef Name);
SymbolBody *addUndefinedOpt(StringRef Name);
+ void addAbsoluteSym(StringRef Name,
+ typename llvm::object::ELFFile<ELFT>::Elf_Sym &ESym);
void addSyntheticSym(StringRef Name, OutputSectionBase<ELFT> &Section,
typename llvm::object::ELFFile<ELFT>::uintX_t Value);
void addIgnoredSym(StringRef Name);
diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h
index 98533cf3cbe..07729a98963 100644
--- a/lld/ELF/Symbols.h
+++ b/lld/ELF/Symbols.h
@@ -177,6 +177,11 @@ template <class ELFT> class DefinedAbsolute : public Defined<ELFT> {
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<ELFT>(Base::DefinedAbsoluteKind, N, Sym) {}
@@ -188,6 +193,9 @@ public:
template <class ELFT>
typename DefinedAbsolute<ELFT>::Elf_Sym DefinedAbsolute<ELFT>::IgnoreUndef;
+template <class ELFT>
+typename DefinedAbsolute<ELFT>::Elf_Sym DefinedAbsolute<ELFT>::MipsGp;
+
template <class ELFT> class DefinedCommon : public Defined<ELFT> {
typedef ELFSymbolBody<ELFT> Base;
typedef typename Base::Elf_Sym Elf_Sym;
diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp
index 3e0152ef834..96e7d04d304 100644
--- a/lld/ELF/Target.cpp
+++ b/lld/ELF/Target.cpp
@@ -35,6 +35,14 @@ namespace elf2 {
std::unique_ptr<TargetInfo> Target;
+template <bool IsLE> static uint32_t read32(const uint8_t *L);
+template <> uint32_t read32<true>(const uint8_t *L) { return read32le(L); }
+template <> uint32_t read32<false>(const uint8_t *L) { return read32be(L); }
+
+template <bool IsLE> static void write32(uint8_t *L, uint32_t V);
+template <> void write32<true>(uint8_t *L, uint32_t V) { write32le(L, V); }
+template <> void write32<false>(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 @@ public:
template <class ELFT> 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::relocPointsToGot(uint32_t Type) const { return false; }
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 @@ void AArch64TargetInfo::relocateOne(uint8_t *Loc, uint8_t *BufEnd,
template <class ELFT> MipsTargetInfo<ELFT>::MipsTargetInfo() {
PageSize = 65536;
+ GotRefReloc = R_MIPS_GOT16;
+ GotHeaderEntriesNum = 2;
+}
+
+template <class ELFT>
+void MipsTargetInfo<ELFT>::writeGotHeaderEntries(uint8_t *Buf) const {
+ typedef typename llvm::object::ELFFile<ELFT>::Elf_Off Elf_Off;
+ auto *P = reinterpret_cast<Elf_Off *>(Buf);
+ // Module pointer
+ P[1] = ELFT::Is64Bits ? 0x8000000000000000 : 0x80000000;
}
template <class ELFT>
@@ -684,7 +705,7 @@ void MipsTargetInfo<ELFT>::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr,
template <class ELFT>
bool MipsTargetInfo<ELFT>::relocNeedsGot(uint32_t Type,
const SymbolBody &S) const {
- return false;
+ return Type == R_MIPS_GOT16;
}
template <class ELFT>
@@ -702,9 +723,27 @@ void MipsTargetInfo<ELFT>::relocateOne(uint8_t *Loc, uint8_t *BufEnd,
case R_MIPS_32:
add32<IsLE>(Loc, SA);
break;
+ case R_MIPS_GOT16: {
+ int64_t V = SA - getMipsGpAddr<ELFT>();
+ if (!isInt<16>(V))
+ error("Relocation R_MIPS_GOT16 out of range");
+ write32<IsLE>(Loc, (read32<IsLE>(Loc) & 0xffff0000) | (V & 0xffff));
+ break;
+ }
default:
error("unrecognized reloc " + Twine(Type));
}
}
+
+template <class ELFT>
+typename llvm::object::ELFFile<ELFT>::uintX_t getMipsGpAddr() {
+ const unsigned GPOffset = 0x7ff0;
+ return Out<ELFT>::Got->getVA() ? (Out<ELFT>::Got->getVA() + GPOffset) : 0;
+}
+
+template uint32_t getMipsGpAddr<ELF32LE>();
+template uint32_t getMipsGpAddr<ELF32BE>();
+template uint64_t getMipsGpAddr<ELF64LE>();
+template uint64_t getMipsGpAddr<ELF64BE>();
}
}
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 7114f1d3549..3c78beae2c5 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -11,6 +11,7 @@
#define LLD_ELF_TARGET_H
#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/ELF.h"
#include <memory>
@@ -31,7 +32,9 @@ public:
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 @@ protected:
unsigned RelativeReloc;
unsigned PltEntrySize = 8;
unsigned PltZeroEntrySize = 0;
+ unsigned GotHeaderEntriesNum = 0;
bool LazyRelocations = false;
};
uint64_t getPPC64TocBase();
+template <class ELFT>
+typename llvm::object::ELFFile<ELFT>::uintX_t getMipsGpAddr();
+
extern std::unique_ptr<TargetInfo> Target;
TargetInfo *createTarget();
}
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 00dc4733d66..3ba94b156a2 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -225,6 +225,15 @@ void Writer<ELFT>::scanRelocs(
}
}
+ 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 @@ template <class ELFT> void Writer<ELFT>::assignAddresses() {
// 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<ELFT>::MipsGp.st_value = getMipsGpAddr<ELFT>();
}
// Returns the number of PHDR entries.
diff --git a/lld/test/elf2/basic-mips.s b/lld/test/elf2/basic-mips.s
index 00e4885b45a..399f8b340b4 100644
--- a/lld/test/elf2/basic-mips.s
+++ b/lld/test/elf2/basic-mips.s
@@ -27,7 +27,7 @@ __start:
# 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 @@ __start:
# 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 @@ __start:
# 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 @@ __start:
# 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 @@ __start:
# 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
diff --git a/lld/test/elf2/mips-got-relocs.s b/lld/test/elf2/mips-got-relocs.s
new file mode 100644
index 00000000000..f9c7dd66c19
--- /dev/null
+++ b/lld/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
OpenPOWER on IntegriCloud