diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index c54e28b4354e..b29cd7154a04 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -65,6 +65,22 @@ static bool refersToGotEntry(RelExpr Expr) { 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. template 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 // depending on the symbol being locally defined or not. - if (Body.isPreemptible()) { + if (isPreemptible(Body, Type)) { C.Relocations.push_back( {R_RELAX_TLS_GD_TO_IE, Type, Offset, Addend, &Body}); 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 // defined. if (Target->isTlsInitialExecRel(Type) && !Config->Shared && - !Body.isPreemptible()) { + !isPreemptible(Body, Type)) { C.Relocations.push_back( {R_RELAX_TLS_IE_TO_LE, Type, Offset, Addend, &Body}); return 1; @@ -246,7 +262,7 @@ static bool isStaticLinkTimeConstant(RelExpr E, uint32_t Type, if (E == R_GOT || E == R_PLT) return Target->usesOnlyLowPageBits(Type) || !Config->Pic; - if (Body.isPreemptible()) + if (isPreemptible(Body, Type)) return false; if (!Config->Pic) @@ -348,7 +364,7 @@ static RelExpr adjustExpr(const elf::ObjectFile &File, SymbolBody &Body, const uint8_t *Data, typename ELFT::uint Offset) { if (Target->needsThunk(Type, File, Body)) return R_THUNK; - bool Preemptible = Body.isPreemptible(); + bool Preemptible = isPreemptible(Body, Type); if (Body.isGnuIFunc()) { Expr = toPlt(Expr); } else if (!Preemptible) { @@ -485,7 +501,7 @@ static void scanRelocs(InputSectionBase &C, ArrayRef Rels) { if (Offset == (uintX_t)-1) continue; - bool Preemptible = Body.isPreemptible(); + bool Preemptible = isPreemptible(Body, Type); Expr = adjustExpr(File, Body, IsWrite, Expr, Type, Buf, Offset); if (HasError) continue; @@ -504,7 +520,7 @@ static void scanRelocs(InputSectionBase &C, ArrayRef Rels) { } 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. bool Constant = isStaticLinkTimeConstant(Expr, Type, Body); diff --git a/lld/test/ELF/mips-64-gprel-so.s b/lld/test/ELF/mips-64-gprel-so.s new file mode 100644 index 000000000000..437238ef5f26 --- /dev/null +++ b/lld/test/ELF/mips-64-gprel-so.s @@ -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)))