[ELF][MIPS] Always resolve MIPS GP-relative relocations to 'local' definitions

In case of MIPS, GP-relative relocations always resolve to a definition
in a regular input file, ignoring the one-definition rule. Such
relocations are used to setup GP relative offsets in a function's
prologue. So we, for example, should not attempt to create a dynamic
relocation even if the target symbol is preemptible.

Fixes bug 27880.

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

llvm-svn: 271100
This commit is contained in:
Simon Atanasyan 2016-05-28 04:49:57 +00:00
parent 9be88629d5
commit 9a9a3169e3
2 changed files with 45 additions and 6 deletions

View File

@ -65,6 +65,22 @@ static bool refersToGotEntry(RelExpr Expr) {
Expr == R_TLSGD_PC; Expr == R_TLSGD_PC;
} }
static bool isPreemptible(const SymbolBody &Body, uint32_t Type) {
// In case of MIPS GP-relative relocations always resolve to a definition
// in a regular input file, ignoring the one-definition rule. So we,
// for example, should not attempt to create a dynamic relocation even
// if the target symbol is preemptible. There are two two MIPS GP-relative
// relocations R_MIPS_GPREL16 and R_MIPS_GPREL32. But only R_MIPS_GPREL16
// can be against a preemptible symbol.
// To get MIPS relocation type we apply 0xf mask. In case of O32 ABI all
// relocation types occupy eight bit. In case of N64 ABI we extract first
// relocation from 3-in-1 packet because only the first relocation can
// be against a real symbol.
if (Config->EMachine == EM_MIPS && (Type & 0xf) == R_MIPS_GPREL16)
return false;
return Body.isPreemptible();
}
// Returns the number of relocations processed. // Returns the number of relocations processed.
template <class ELFT> template <class ELFT>
static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body, static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body,
@ -116,7 +132,7 @@ static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body,
// Global-Dynamic relocs can be relaxed to Initial-Exec or Local-Exec // Global-Dynamic relocs can be relaxed to Initial-Exec or Local-Exec
// depending on the symbol being locally defined or not. // depending on the symbol being locally defined or not.
if (Body.isPreemptible()) { if (isPreemptible(Body, Type)) {
C.Relocations.push_back( C.Relocations.push_back(
{R_RELAX_TLS_GD_TO_IE, Type, Offset, Addend, &Body}); {R_RELAX_TLS_GD_TO_IE, Type, Offset, Addend, &Body});
if (!Body.isInGot()) { if (!Body.isInGot()) {
@ -135,7 +151,7 @@ static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body,
// Initial-Exec relocs can be relaxed to Local-Exec if the symbol is locally // Initial-Exec relocs can be relaxed to Local-Exec if the symbol is locally
// defined. // defined.
if (Target->isTlsInitialExecRel(Type) && !Config->Shared && if (Target->isTlsInitialExecRel(Type) && !Config->Shared &&
!Body.isPreemptible()) { !isPreemptible(Body, Type)) {
C.Relocations.push_back( C.Relocations.push_back(
{R_RELAX_TLS_IE_TO_LE, Type, Offset, Addend, &Body}); {R_RELAX_TLS_IE_TO_LE, Type, Offset, Addend, &Body});
return 1; return 1;
@ -246,7 +262,7 @@ static bool isStaticLinkTimeConstant(RelExpr E, uint32_t Type,
if (E == R_GOT || E == R_PLT) if (E == R_GOT || E == R_PLT)
return Target->usesOnlyLowPageBits(Type) || !Config->Pic; return Target->usesOnlyLowPageBits(Type) || !Config->Pic;
if (Body.isPreemptible()) if (isPreemptible(Body, Type))
return false; return false;
if (!Config->Pic) if (!Config->Pic)
@ -348,7 +364,7 @@ static RelExpr adjustExpr(const elf::ObjectFile<ELFT> &File, SymbolBody &Body,
const uint8_t *Data, typename ELFT::uint Offset) { const uint8_t *Data, typename ELFT::uint Offset) {
if (Target->needsThunk(Type, File, Body)) if (Target->needsThunk(Type, File, Body))
return R_THUNK; return R_THUNK;
bool Preemptible = Body.isPreemptible(); bool Preemptible = isPreemptible(Body, Type);
if (Body.isGnuIFunc()) { if (Body.isGnuIFunc()) {
Expr = toPlt(Expr); Expr = toPlt(Expr);
} else if (!Preemptible) { } else if (!Preemptible) {
@ -485,7 +501,7 @@ static void scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
if (Offset == (uintX_t)-1) if (Offset == (uintX_t)-1)
continue; continue;
bool Preemptible = Body.isPreemptible(); bool Preemptible = isPreemptible(Body, Type);
Expr = adjustExpr(File, Body, IsWrite, Expr, Type, Buf, Offset); Expr = adjustExpr(File, Body, IsWrite, Expr, Type, Buf, Offset);
if (HasError) if (HasError)
continue; continue;
@ -504,7 +520,7 @@ static void scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
} }
if (needsPlt(Expr) || Expr == R_THUNK || refersToGotEntry(Expr) || if (needsPlt(Expr) || Expr == R_THUNK || refersToGotEntry(Expr) ||
!Body.isPreemptible()) { !isPreemptible(Body, Type)) {
// If the relocation points to something in the file, we can process it. // If the relocation points to something in the file, we can process it.
bool Constant = isStaticLinkTimeConstant<ELFT>(Expr, Type, Body); bool Constant = isStaticLinkTimeConstant<ELFT>(Expr, Type, Body);

View File

@ -0,0 +1,23 @@
# Check setup of GP relative offsets in a function's prologue.
# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t.o
# RUN: ld.lld %t.o -shared -o %t.so
# RUN: llvm-objdump -d -t %t.so | FileCheck %s
# REQUIRES: mips
# CHECK: Disassembly of section .text:
# CHECK-NEXT: foo:
# CHECK-NEXT: 10000: 3c 1c 00 01 lui $gp, 1
# CHECK-NEXT: 10004: 03 99 e0 2d daddu $gp, $gp, $25
# CHECK-NEXT: 10008: 67 9c 7f f0 daddiu $gp, $gp, 32752
# CHECK: 0000000000027ff0 .got 00000000 .hidden _gp
# CHECK: 0000000000010000 .text 00000000 foo
.text
.global foo
foo:
lui $gp,%hi(%neg(%gp_rel(foo)))
daddu $gp,$gp,$t9
daddiu $gp,$gp,%lo(%neg(%gp_rel(foo)))