[ELF][MIPS] Support MIPS TLS relocations

The patch adds one more partition to the MIPS GOT. This time it is for
TLS related GOT entries. Such entries are located after 'local' and 'global'
ones. We cannot get a final offset for these entries at the time of
creation because we do not know size of 'local' and 'global' partitions.
So we have to adjust the offset later using `getMipsTlsOffset()` method.

All MIPS TLS relocations which need GOT entries operates MIPS style GOT
offset - 'offset from the GOT's beginning' - MipsGPOffset constant. That
is why I add new types of relocation expressions.

One more difference from othe ABIs is that the MIPS ABI does not support
any TLS relocation relaxations. I decided to make a separate function
`handleMipsTlsRelocation` and put MIPS TLS relocation handling code
there. It is similar to `handleTlsRelocation` routine and duplicates its
code. But it allows to make the code cleaner and prevent pollution of
the `handleTlsRelocation` by MIPS 'if' statements.

Differential Revision: http://reviews.llvm.org/D21606

llvm-svn: 273569
This commit is contained in:
Simon Atanasyan
2016-06-23 15:26:31 +00:00
parent f0c9f81379
commit 002e244717
9 changed files with 276 additions and 7 deletions

View File

@@ -220,6 +220,12 @@ getSymVA(uint32_t Type, typename ELFT::uint A, typename ELFT::uint P,
// should be applied to the GOT entry content not to the GOT entry offset.
// That is why we use separate expression type.
return Out<ELFT>::Got->getMipsGotOffset(Body, A);
case R_MIPS_TLSGD:
return Out<ELFT>::Got->getGlobalDynOffset(Body) +
Out<ELFT>::Got->getMipsTlsOffset() - MipsGPOffset;
case R_MIPS_TLSLD:
return Out<ELFT>::Got->getTlsIndexOff() +
Out<ELFT>::Got->getMipsTlsOffset() - MipsGPOffset;
case R_PPC_OPD: {
uint64_t SymVA = Body.getVA<ELFT>(A);
// If we have an undefined weak symbol, we might get here with a symbol

View File

@@ -135,6 +135,14 @@ void GotSection<ELFT>::addMipsEntry(SymbolBody &Sym, uintX_t Addend,
MipsOutSections.insert(OutSec);
return;
}
if (Sym.isTls()) {
// GOT entries created for MIPS TLS relocations behave like
// almost GOT entries from other ABIs. They go to the end
// of the global offset table.
Sym.GotIndex = Entries.size();
Entries.push_back(&Sym);
return;
}
auto AddEntry = [&](SymbolBody &S, uintX_t A, MipsGotEntries &Items) {
if (S.isInGot() && !A)
return;
@@ -192,7 +200,9 @@ template <class ELFT>
typename GotSection<ELFT>::uintX_t
GotSection<ELFT>::getMipsGotOffset(const SymbolBody &B, uintX_t Addend) const {
uintX_t Off = MipsPageEntries;
if (B.IsInGlobalMipsGot)
if (B.isTls())
Off += MipsLocal.size() + MipsGlobal.size() + B.GotIndex;
else if (B.IsInGlobalMipsGot)
Off += MipsLocal.size() + B.GotIndex;
else if (B.isInGot())
Off += B.GotIndex;
@@ -204,6 +214,12 @@ GotSection<ELFT>::getMipsGotOffset(const SymbolBody &B, uintX_t Addend) const {
return Off * sizeof(uintX_t) - MipsGPOffset;
}
template <class ELFT>
typename GotSection<ELFT>::uintX_t GotSection<ELFT>::getMipsTlsOffset() {
return (MipsPageEntries + MipsLocal.size() + MipsGlobal.size()) *
sizeof(uintX_t);
}
template <class ELFT>
typename GotSection<ELFT>::uintX_t
GotSection<ELFT>::getGlobalDynAddr(const SymbolBody &B) const {
@@ -355,6 +371,11 @@ template <class ELFT> void RelocationSection<ELFT>::writeTo(uint8_t *Buf) {
if (Config->Rela)
P->r_addend = Rel.getAddend();
P->r_offset = Rel.getOffset();
if (Config->EMachine == EM_MIPS && Rel.getOutputSec() == Out<ELFT>::Got)
// Dynamic relocation against MIPS GOT section make deal TLS entries
// allocated in the end of the GOT. We need to adjust the offset to take
// in account 'local' and 'global' GOT entries.
P->r_offset += Out<ELFT>::Got->getMipsTlsOffset();
P->setSymbolAndType(Rel.getSymIndex(), Rel.Type, Config->Mips64EL);
}

View File

@@ -134,6 +134,10 @@ public:
// the number of reserved entries. This method is MIPS-specific.
unsigned getMipsLocalEntriesNum() const;
// Returns offset of TLS part of the MIPS GOT table. This part goes
// after 'local' and 'global' entries.
uintX_t getMipsTlsOffset();
uintX_t getTlsIndexVA() { return Base::getVA() + TlsIndexOff; }
uint32_t getTlsIndexOff() { return TlsIndexOff; }
@@ -215,6 +219,7 @@ public:
uintX_t getOffset() const;
uintX_t getAddend() const;
uint32_t getSymIndex() const;
const OutputSectionBase<ELFT> *getOutputSec() const { return OutputSec; }
uint32_t Type;

View File

@@ -60,7 +60,8 @@ namespace elf {
static bool refersToGotEntry(RelExpr Expr) {
return Expr == R_GOT || Expr == R_GOT_OFF || Expr == R_MIPS_GOT_LOCAL_PAGE ||
Expr == R_MIPS_GOT_OFF || Expr == R_GOT_PAGE_PC || Expr == R_GOT_PC ||
Expr == R_MIPS_GOT_OFF || Expr == R_MIPS_TLSGD ||
Expr == R_MIPS_TLSLD || Expr == R_GOT_PAGE_PC || Expr == R_GOT_PC ||
Expr == R_GOT_FROM_END || Expr == R_TLSGD || Expr == R_TLSGD_PC ||
Expr == R_TLSDESC || Expr == R_TLSDESC_PAGE;
}
@@ -81,6 +82,39 @@ static bool isPreemptible(const SymbolBody &Body, uint32_t Type) {
return Body.isPreemptible();
}
// This function is similar to the `handleTlsRelocation`. MIPS does not support
// any relaxations for TLS relocations so by factoring out MIPS handling into
// the separate function we can simplify the code and does not pollute
// `handleTlsRelocation` by MIPS `ifs` statements.
template <class ELFT>
static unsigned
handleMipsTlsRelocation(uint32_t Type, SymbolBody &Body,
InputSectionBase<ELFT> &C, typename ELFT::uint Offset,
typename ELFT::uint Addend, RelExpr Expr) {
if (Expr == R_MIPS_TLSLD) {
if (Out<ELFT>::Got->addTlsIndex())
Out<ELFT>::RelaDyn->addReloc({Target->TlsModuleIndexRel, Out<ELFT>::Got,
Out<ELFT>::Got->getTlsIndexOff(), false,
nullptr, 0});
C.Relocations.push_back({Expr, Type, &C, Offset, Addend, &Body});
return 1;
}
if (Target->isTlsGlobalDynamicRel(Type)) {
if (Out<ELFT>::Got->addDynTlsEntry(Body)) {
typedef typename ELFT::uint uintX_t;
uintX_t Off = Out<ELFT>::Got->getGlobalDynOffset(Body);
Out<ELFT>::RelaDyn->addReloc(
{Target->TlsModuleIndexRel, Out<ELFT>::Got, Off, false, &Body, 0});
Out<ELFT>::RelaDyn->addReloc({Target->TlsOffsetRel, Out<ELFT>::Got,
Off + (uintX_t)sizeof(uintX_t), false,
&Body, 0});
}
C.Relocations.push_back({Expr, Type, &C, Offset, Addend, &Body});
return 1;
}
return 0;
}
// Returns the number of relocations processed.
template <class ELFT>
static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body,
@@ -95,6 +129,9 @@ static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body,
typedef typename ELFT::uint uintX_t;
if (Config->EMachine == EM_MIPS)
return handleMipsTlsRelocation<ELFT>(Type, Body, C, Offset, Addend, Expr);
if ((Expr == R_TLSDESC || Expr == R_TLSDESC_PAGE || Expr == R_HINT) &&
Config->Shared) {
if (Out<ELFT>::Got->addDynTlsEntry(Body)) {
@@ -254,9 +291,9 @@ static bool isStaticLinkTimeConstant(RelExpr E, uint32_t Type,
const SymbolBody &Body) {
// These expressions always compute a constant
if (E == R_SIZE || E == R_GOT_FROM_END || E == R_GOT_OFF ||
E == R_MIPS_GOT_LOCAL_PAGE || E == R_MIPS_GOT_OFF || E == R_GOT_PAGE_PC ||
E == R_GOT_PC || E == R_PLT_PC || E == R_TLSGD_PC || E == R_TLSGD ||
E == R_PPC_PLT_OPD || E == R_TLSDESC_PAGE || E == R_HINT)
E == R_MIPS_GOT_LOCAL_PAGE || E == R_MIPS_GOT_OFF || E == R_MIPS_TLSGD ||
E == R_GOT_PAGE_PC || E == R_GOT_PC || E == R_PLT_PC || E == R_TLSGD_PC ||
E == R_TLSGD || E == R_PPC_PLT_OPD || E == R_TLSDESC_PAGE || E == R_HINT)
return true;
// These never do, except if the entire file is position dependent or if
@@ -603,6 +640,9 @@ static void scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
// for detailed description:
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
Out<ELFT>::Got->addMipsEntry(Body, Addend, Expr);
if (Body.isTls())
AddDyn({Target->TlsGotRel, Out<ELFT>::Got, Body.getGotOffset<ELFT>(),
!Preemptible, &Body, 0});
continue;
}

View File

@@ -30,6 +30,8 @@ enum RelExpr {
R_HINT,
R_MIPS_GOT_LOCAL_PAGE,
R_MIPS_GOT_OFF,
R_MIPS_TLSGD,
R_MIPS_TLSLD,
R_NEG_TLS,
R_PAGE_PC,
R_PC,

View File

@@ -190,6 +190,8 @@ public:
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override;
uint64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override;
uint32_t getDynRel(uint32_t Type) const override;
bool isTlsLocalDynamicRel(uint32_t Type) const override;
bool isTlsGlobalDynamicRel(uint32_t Type) const override;
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
void writePltHeader(uint8_t *Buf) const override;
void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
@@ -1706,10 +1708,17 @@ template <class ELFT> MipsTargetInfo<ELFT>::MipsTargetInfo() {
ThunkSize = 16;
CopyRel = R_MIPS_COPY;
PltRel = R_MIPS_JUMP_SLOT;
if (ELFT::Is64Bits)
if (ELFT::Is64Bits) {
RelativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32;
else
TlsGotRel = R_MIPS_TLS_TPREL64;
TlsModuleIndexRel = R_MIPS_TLS_DTPMOD64;
TlsOffsetRel = R_MIPS_TLS_DTPREL64;
} else {
RelativeRel = R_MIPS_REL32;
TlsGotRel = R_MIPS_TLS_TPREL32;
TlsModuleIndexRel = R_MIPS_TLS_DTPMOD32;
TlsOffsetRel = R_MIPS_TLS_DTPREL32;
}
}
template <class ELFT>
@@ -1752,9 +1761,14 @@ RelExpr MipsTargetInfo<ELFT>::getRelExpr(uint32_t Type,
// fallthrough
case R_MIPS_CALL16:
case R_MIPS_GOT_DISP:
case R_MIPS_TLS_GOTTPREL:
return R_MIPS_GOT_OFF;
case R_MIPS_GOT_PAGE:
return R_MIPS_GOT_LOCAL_PAGE;
case R_MIPS_TLS_GD:
return R_MIPS_TLSGD;
case R_MIPS_TLS_LDM:
return R_MIPS_TLSLD;
}
}
@@ -1767,6 +1781,16 @@ uint32_t MipsTargetInfo<ELFT>::getDynRel(uint32_t Type) const {
return R_MIPS_32;
}
template <class ELFT>
bool MipsTargetInfo<ELFT>::isTlsLocalDynamicRel(uint32_t Type) const {
return Type == R_MIPS_TLS_LDM;
}
template <class ELFT>
bool MipsTargetInfo<ELFT>::isTlsGlobalDynamicRel(uint32_t Type) const {
return Type == R_MIPS_TLS_GD;
}
template <class ELFT>
void MipsTargetInfo<ELFT>::writeGotPlt(uint8_t *Buf, const SymbolBody &) const {
write32<ELFT::TargetEndianness>(Buf, Out<ELFT>::Plt->getVA());
@@ -1961,6 +1985,8 @@ void MipsTargetInfo<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type,
case R_MIPS_GOT_PAGE:
case R_MIPS_GOT16:
case R_MIPS_GPREL16:
case R_MIPS_TLS_GD:
case R_MIPS_TLS_LDM:
checkInt<16>(Val, Type);
// fallthrough
case R_MIPS_CALL16:
@@ -1968,6 +1994,7 @@ void MipsTargetInfo<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type,
case R_MIPS_LO16:
case R_MIPS_PCLO16:
case R_MIPS_TLS_DTPREL_LO16:
case R_MIPS_TLS_GOTTPREL:
case R_MIPS_TLS_TPREL_LO16:
writeMipsLo16<E>(Loc, Val);
break;

View File

@@ -0,0 +1,5 @@
.globl foo
.section .tdata,"awT",%progbits
.type foo, %object
foo:
.word 0

View File

@@ -0,0 +1,86 @@
# Check MIPS TLS 64-bit relocations handling.
# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux \
# RUN: %p/Inputs/mips-dynamic.s -o %t.so.o
# RUN: ld.lld -shared %t.so.o -o %t.so
# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t.o
# RUN: ld.lld %t.o %t.so -o %t.exe
# RUN: llvm-objdump -d -s -t %t.exe | FileCheck -check-prefix=DIS %s
# RUN: llvm-readobj -r -mips-plt-got %t.exe | FileCheck %s
# REQUIRES: mips
# DIS: __start:
# DIS-NEXT: 20000: 24 62 80 28 addiu $2, $3, -32728
# DIS-NEXT: 20004: 24 62 80 38 addiu $2, $3, -32712
# DIS-NEXT: 20008: 8f 82 80 20 lw $2, -32736($gp)
# DIS-NEXT: 2000c: 24 62 80 48 addiu $2, $3, -32696
# DIS: Contents of section .got:
# DIS_NEXT: 30008 00000000 00000000 80000000 00000000
# DIS_NEXT: 30018 00000000 00020000 00000000 00000000
# DIS_NEXT: 30028 00000000 00000004 00000000 00000000
# DIS_NEXT: 30038 00000000 00000000 00000000 00000004
# DIS: 0000000000030000 l .tdata 00000000 .tdata
# DIS: 0000000000030000 l .tdata 00000000 loc
# DIS: 0000000000000004 g .tdata 00000000 foo
# CHECK: Relocations [
# CHECK-NEXT: Section (7) .rela.dyn {
# CHECK-NEXT: 0x30020 R_MIPS_TLS_DTPMOD64/R_MIPS_NONE/R_MIPS_NONE - 0x0
# CHECK-NEXT: 0x30028 R_MIPS_TLS_DTPREL64/R_MIPS_NONE/R_MIPS_NONE - 0x0
# CHECK-NEXT: 0x30030 R_MIPS_TLS_DTPMOD64/R_MIPS_NONE/R_MIPS_NONE - 0x0
# CHECK-NEXT: 0x30040 R_MIPS_TLS_TPREL64/R_MIPS_NONE/R_MIPS_NONE - 0x4
# CHECK-NEXT: }
# CHECK-NEXT: ]
# CHECK-NEXT: Primary GOT {
# CHECK-NEXT: Canonical gp value: 0x37FF8
# CHECK-NEXT: Reserved entries [
# CHECK-NEXT: Entry {
# CHECK-NEXT: Address: 0x30008
# CHECK-NEXT: Access: -32752
# CHECK-NEXT: Initial: 0x0
# CHECK-NEXT: Purpose: Lazy resolver
# CHECK-NEXT: }
# CHECK-NEXT: Entry {
# CHECK-NEXT: Address: 0x30010
# CHECK-NEXT: Access: -32744
# CHECK-NEXT: Initial: 0x80000000
# CHECK-NEXT: Purpose: Module pointer (GNU extension)
# CHECK-NEXT: }
# CHECK-NEXT: ]
# CHECK-NEXT: Local entries [
# CHECK-NEXT: ]
# CHECK-NEXT: Global entries [
# CHECK-NEXT: Entry {
# CHECK-NEXT: Address: 0x30018
# CHECK-NEXT: Access: -32736
# CHECK-NEXT: Initial: 0x0
# CHECK-NEXT: Value: 0x0
# CHECK-NEXT: Type: Function
# CHECK-NEXT: Section: Undefined
# CHECK-NEXT: Name: foo0
# CHECK-NEXT: }
# CHECK-NEXT: ]
# CHECK-NEXT: Number of TLS and multi-GOT entries: 5
# ^-- 0x30020 / -32728 - R_MIPS_TLS_GD - R_MIPS_TLS_DTPMOD32 foo
# ^-- 0x30028 / -32720 - R_MIPS_TLS_DTPREL32 foo
# ^-- 0x30030 / -32712 - R_MIPS_TLS_LDM - R_MIPS_TLS_DTPMOD32 loc
# ^-- 0x30038 / -32704
# ^-- 0x30040 / -32696 - R_MIPS_TLS_GOTTPREL - R_MIPS_TLS_TPREL32
.text
.global __start
__start:
addiu $2, $3, %tlsgd(foo) # R_MIPS_TLS_GD
addiu $2, $3, %tlsldm(loc) # R_MIPS_TLS_LDM
lw $2, %got(foo0)($gp)
addiu $2, $3, %gottprel(foo) # R_MIPS_TLS_GOTTPREL
.section .tdata,"awT",%progbits
.global foo
loc:
.word 0
foo:
.word 0

77
lld/test/ELF/mips-tls.s Normal file
View File

@@ -0,0 +1,77 @@
# Check MIPS TLS relocations handling.
# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
# RUN: %p/Inputs/mips-tls.s -o %t.so.o
# RUN: ld.lld -shared %t.so.o -o %t.so
# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o
# RUN: ld.lld %t.o %t.so -o %t.exe
# RUN: llvm-objdump -d -s -t %t.exe | FileCheck -check-prefix=DIS %s
# RUN: llvm-readobj -r -mips-plt-got %t.exe | FileCheck %s
# REQUIRES: mips
# DIS: __start:
# DIS-NEXT: 20000: 24 62 80 1c addiu $2, $3, -32740
# DIS-NEXT: 20004: 24 62 80 24 addiu $2, $3, -32732
# DIS-NEXT: 20008: 8f 82 80 18 lw $2, -32744($gp)
# DIS-NEXT: 2000c: 24 62 80 2c addiu $2, $3, -32724
# DIS: Contents of section .got:
# DIS_NEXT: 30004 00000000 80000000 00020000 00000000
# DIS_NEXT: 30014 00000000 00000000 00000000 00000000
# DIS: 00030000 l .tdata 00000000 .tdata
# DIS: 00030000 l .tdata 00000000 loc
# DIS: 00000000 g *UND* 00000000 foo
# CHECK: Relocations [
# CHECK-NEXT: Section (7) .rel.dyn {
# CHECK-NEXT: 0x30018 R_MIPS_TLS_DTPMOD32 - 0x0
# CHECK-NEXT: 0x30010 R_MIPS_TLS_DTPMOD32 foo 0x0
# CHECK-NEXT: 0x30014 R_MIPS_TLS_DTPREL32 foo 0x0
# CHECK-NEXT: 0x30020 R_MIPS_TLS_TPREL32 foo 0x0
# CHECK-NEXT: }
# CHECK-NEXT: ]
# CHECK-NEXT: Primary GOT {
# CHECK-NEXT: Canonical gp value: 0x37FF4
# CHECK-NEXT: Reserved entries [
# CHECK-NEXT: Entry {
# CHECK-NEXT: Address: 0x30004
# CHECK-NEXT: Access: -32752
# CHECK-NEXT: Initial: 0x0
# CHECK-NEXT: Purpose: Lazy resolver
# CHECK-NEXT: }
# CHECK-NEXT: Entry {
# CHECK-NEXT: Address: 0x30008
# CHECK-NEXT: Access: -32748
# CHECK-NEXT: Initial: 0x80000000
# CHECK-NEXT: Purpose: Module pointer (GNU extension)
# CHECK-NEXT: }
# CHECK-NEXT: ]
# CHECK-NEXT: Local entries [
# CHECK-NEXT: Entry {
# CHECK-NEXT: Address: 0x3000C
# CHECK-NEXT: Access: -32744
# CHECK-NEXT: Initial: 0x20000
# CHECK-NEXT: }
# CHECK-NEXT: ]
# CHECK-NEXT: Global entries [
# CHECK-NEXT: ]
# CHECK-NEXT: Number of TLS and multi-GOT entries: 5
# ^-- 0x30010 / -32740 - R_MIPS_TLS_GD - R_MIPS_TLS_DTPMOD32 foo
# ^-- 0x30018 / -32736 - R_MIPS_TLS_DTPREL32 foo
# ^-- 0x3001C / -32732 - R_MIPS_TLS_LDM - R_MIPS_TLS_DTPMOD32 loc
# ^-- 0x30020 / -32728
# ^-- 0x30024 / -32724 - R_MIPS_TLS_GOTTPREL - R_MIPS_TLS_TPREL32
.text
.global __start
__start:
addiu $2, $3, %tlsgd(foo) # R_MIPS_TLS_GD
addiu $2, $3, %tlsldm(loc) # R_MIPS_TLS_LDM
lw $2, %got(__start)($gp)
addiu $2, $3, %gottprel(foo) # R_MIPS_TLS_GOTTPREL
.section .tdata,"awT",%progbits
loc:
.word 0