[ELF] Make the rule to create relative relocations in a writable section stricter

The current rule is loose: `!Sym.IsPreemptible || Expr == R_GOT`.

When the symbol is non-preemptable, this allows absolute relocation
types with smaller numbers of bits, e.g. R_X86_64_{8,16,32}. They are
disallowed by ld.bfd and gold, e.g.

    ld.bfd: a.o: relocation R_X86_64_8 against `.text' can not be used when making a shared object; recompile with -fPIC

This patch:

a) Add TargetInfo::SymbolicRel to represent relocation types that resolve to a
symbol value (e.g. R_AARCH_ABS64, R_386_32, R_X86_64_64).

  As a side benefit, we currently (ab)use GotRel (R_*_GLOB_DAT) to resolve
  GOT slots that are link-time constants. Since we now use Target->SymbolRel
  to do the job, we can remove R_*_GLOB_DAT from relocateOne() for all targets.
  R_*_GLOB_DAT cannot be used as static relocation types.
b) Change the condition to `!Sym.IsPreemptible && Type != Target->SymbolicRel || Expr == R_GOT`.

Some tests are caught by the improved error checking (ld.bfd/gold also
issue errors on them). Many misuse .long where .quad should be used
instead.

Reviewed By: ruiu

Differential Revision: https://reviews.llvm.org/D63121

llvm-svn: 363059
This commit is contained in:
Fangrui Song 2019-06-11 12:59:30 +00:00
parent a5240361dd
commit 025a815d75
18 changed files with 67 additions and 28 deletions

View File

@ -59,6 +59,7 @@ AArch64::AArch64() {
GotRel = R_AARCH64_GLOB_DAT;
NoneRel = R_AARCH64_NONE;
PltRel = R_AARCH64_JUMP_SLOT;
SymbolicRel = R_AARCH64_ABS64;
TlsDescRel = R_AARCH64_TLSDESC;
TlsGotRel = R_AARCH64_TLS_TPREL64;
PltEntrySize = 16;
@ -258,7 +259,6 @@ void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
write32le(Loc, Val);
break;
case R_AARCH64_ABS64:
case R_AARCH64_GLOB_DAT:
case R_AARCH64_PREL64:
write64le(Loc, Val);
break;

View File

@ -35,6 +35,7 @@ AMDGPU::AMDGPU() {
RelativeRel = R_AMDGPU_RELATIVE64;
GotRel = R_AMDGPU_ABS64;
NoneRel = R_AMDGPU_NONE;
SymbolicRel = R_AMDGPU_ABS64;
}
static uint32_t getEFlags(InputFile *File) {

View File

@ -52,6 +52,7 @@ ARM::ARM() {
GotRel = R_ARM_GLOB_DAT;
NoneRel = R_ARM_NONE;
PltRel = R_ARM_JUMP_SLOT;
SymbolicRel = R_ARM_ABS32;
TlsGotRel = R_ARM_TLS_TPOFF32;
TlsModuleIndexRel = R_ARM_TLS_DTPMOD32;
TlsOffsetRel = R_ARM_TLS_DTPOFF32;
@ -377,7 +378,6 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_ARM_ABS32:
case R_ARM_BASE_PREL:
case R_ARM_GLOB_DAT:
case R_ARM_GOTOFF32:
case R_ARM_GOT_BREL:
case R_ARM_GOT_PREL:

View File

@ -40,6 +40,7 @@ Hexagon::Hexagon() {
PltRel = R_HEX_JMP_SLOT;
RelativeRel = R_HEX_RELATIVE;
GotRel = R_HEX_GLOB_DAT;
SymbolicRel = R_HEX_32;
// The zero'th GOT entry is reserved for the address of _DYNAMIC. The
// next 3 are reserved for the dynamic loader.

View File

@ -59,11 +59,13 @@ template <class ELFT> MIPS<ELFT>::MIPS() {
if (ELFT::Is64Bits) {
RelativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32;
SymbolicRel = R_MIPS_64;
TlsGotRel = R_MIPS_TLS_TPREL64;
TlsModuleIndexRel = R_MIPS_TLS_DTPMOD64;
TlsOffsetRel = R_MIPS_TLS_DTPREL64;
} else {
RelativeRel = R_MIPS_REL32;
SymbolicRel = R_MIPS_32;
TlsGotRel = R_MIPS_TLS_TPREL32;
TlsModuleIndexRel = R_MIPS_TLS_DTPMOD32;
TlsOffsetRel = R_MIPS_TLS_DTPREL32;

View File

@ -135,6 +135,7 @@ PPC::PPC() {
PltRel = R_PPC_JMP_SLOT;
RelativeRel = R_PPC_RELATIVE;
IRelativeRel = R_PPC_IRELATIVE;
SymbolicRel = R_PPC_ADDR32;
GotBaseSymInGotPlt = false;
GotHeaderEntriesNum = 3;
GotPltHeaderEntriesNum = 0;
@ -288,7 +289,6 @@ void PPC::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
write16(Loc, Val);
break;
case R_PPC_ADDR32:
case R_PPC_GLOB_DAT:
case R_PPC_REL32:
write32(Loc, Val);
break;

View File

@ -288,6 +288,7 @@ PPC64::PPC64() {
PltRel = R_PPC64_JMP_SLOT;
RelativeRel = R_PPC64_RELATIVE;
IRelativeRel = R_PPC64_IRELATIVE;
SymbolicRel = R_PPC64_ADDR64;
PltEntrySize = 4;
GotBaseSymInGotPlt = false;
GotHeaderEntriesNum = 1;

View File

@ -37,6 +37,7 @@ SPARCV9::SPARCV9() {
NoneRel = R_SPARC_NONE;
PltRel = R_SPARC_JMP_SLOT;
RelativeRel = R_SPARC_RELATIVE;
SymbolicRel = R_SPARC_64;
PltEntrySize = 32;
PltHeaderSize = 4 * PltEntrySize;
@ -114,7 +115,6 @@ void SPARCV9::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
break;
case R_SPARC_64:
case R_SPARC_UA64:
case R_SPARC_GLOB_DAT:
// V-xword64
write64be(Loc, Val);
break;

View File

@ -52,6 +52,7 @@ X86::X86() {
PltRel = R_386_JUMP_SLOT;
IRelativeRel = R_386_IRELATIVE;
RelativeRel = R_386_RELATIVE;
SymbolicRel = R_386_32;
TlsGotRel = R_386_TLS_TPOFF;
TlsModuleIndexRel = R_386_TLS_DTPMOD32;
TlsOffsetRel = R_386_TLS_DTPOFF32;
@ -291,7 +292,6 @@ void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
write16le(Loc, Val);
break;
case R_386_32:
case R_386_GLOB_DAT:
case R_386_GOT32:
case R_386_GOT32X:
case R_386_GOTOFF:

View File

@ -55,6 +55,7 @@ X86_64::X86_64() {
PltRel = R_X86_64_JUMP_SLOT;
RelativeRel = R_X86_64_RELATIVE;
IRelativeRel = R_X86_64_IRELATIVE;
SymbolicRel = R_X86_64_64;
TlsDescRel = R_X86_64_TLSDESC;
TlsGotRel = R_X86_64_TPOFF64;
TlsModuleIndexRel = R_X86_64_DTPMOD64;
@ -387,7 +388,6 @@ void X86_64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
break;
case R_X86_64_64:
case R_X86_64_DTPOFF64:
case R_X86_64_GLOB_DAT:
case R_X86_64_PC64:
case R_X86_64_SIZE64:
case R_X86_64_GOT64:

View File

@ -861,19 +861,19 @@ static void addGotEntry(Symbol &Sym) {
bool IsLinkTimeConstant =
!Sym.IsPreemptible && (!Config->Pic || isAbsolute(Sym));
if (IsLinkTimeConstant) {
In.Got->Relocations.push_back({Expr, Target->GotRel, Off, 0, &Sym});
In.Got->Relocations.push_back({Expr, Target->SymbolicRel, Off, 0, &Sym});
return;
}
// Otherwise, we emit a dynamic relocation to .rel[a].dyn so that
// the GOT slot will be fixed at load-time.
if (!Sym.isTls() && !Sym.IsPreemptible && Config->Pic && !isAbsolute(Sym)) {
addRelativeReloc(In.Got, Off, &Sym, 0, R_ABS, Target->GotRel);
addRelativeReloc(In.Got, Off, &Sym, 0, R_ABS, Target->SymbolicRel);
return;
}
Main->RelaDyn->addReloc(Sym.isTls() ? Target->TlsGotRel : Target->GotRel,
In.Got, Off, &Sym, 0,
Sym.IsPreemptible ? R_ADDEND : R_ABS, Target->GotRel);
Main->RelaDyn->addReloc(
Sym.isTls() ? Target->TlsGotRel : Target->GotRel, In.Got, Off, &Sym, 0,
Sym.IsPreemptible ? R_ADDEND : R_ABS, Target->SymbolicRel);
}
// Return true if we can define a symbol in the executable that
@ -919,10 +919,10 @@ static void processRelocAux(InputSectionBase &Sec, RelExpr Expr, RelType Type,
}
bool CanWrite = (Sec.Flags & SHF_WRITE) || !Config->ZText;
if (CanWrite) {
// R_GOT refers to a position in the got, even if the symbol is preemptible.
bool IsPreemptibleValue = Sym.IsPreemptible && Expr != R_GOT;
if (!IsPreemptibleValue) {
if ((!Sym.IsPreemptible && Type == Target->SymbolicRel) || Expr == R_GOT) {
// If this is a symbolic relocation to a non-preemptable symbol, or an
// R_GOT, its address is its link-time value plus load address. Represent
// it with a relative relocation.
addRelativeReloc(&Sec, Offset, &Sym, Addend, Expr, Type);
return;
} else if (RelType Rel = Target->getDynRel(Type)) {
@ -967,11 +967,18 @@ static void processRelocAux(InputSectionBase &Sec, RelExpr Expr, RelType Type,
return;
}
// Copy relocations are only possible if we are creating an executable.
if (Config->Shared) {
errorOrWarn("relocation " + toString(Type) +
" cannot be used against symbol " + toString(Sym) +
"; recompile with -fPIC" + getLocation(Sec, Sym, Offset));
// Copy relocations (for STT_OBJECT) and canonical PLT (for STT_FUNC) are only
// possible in an executable.
//
// Among R_ABS relocatoin types, SymbolicRel has the same size as the word
// size. Others have fewer bits and may cause runtime overflow in -pie/-shared
// mode. Disallow them.
if (Config->Shared ||
(Config->Pie && Expr == R_ABS && Type != Target->SymbolicRel)) {
errorOrWarn(
"relocation " + toString(Type) + " cannot be used against " +
(Sym.getName().empty() ? "local symbol" : "symbol " + toString(Sym)) +
"; recompile with -fPIC" + getLocation(Sec, Sym, Offset));
return;
}

View File

@ -95,6 +95,7 @@ public:
RelType PltRel;
RelType RelativeRel;
RelType IRelativeRel;
RelType SymbolicRel;
RelType TlsDescRel;
RelType TlsGotRel;
RelType TlsModuleIndexRel;

View File

@ -19,4 +19,4 @@
.data
.hidden foo
.long foo
.quad foo

View File

@ -6,6 +6,6 @@
.asciz "abc"
.data
.long .rodata.str1.1 + 4
.quad .rodata.str1.1 + 4
// CHECK: merge-string-error.s.tmp.o:(.rodata.str1.1): offset is outside the section

View File

@ -46,7 +46,7 @@
// CHECK-NEXT: ]
// CHECK-NEXT: Address: 0x10004
// CHECK-NEXT: Offset: 0x10004
// CHECK-NEXT: Size: 12
// CHECK-NEXT: Size: 24
.data
@ -54,9 +54,9 @@
.section foo,"aw"
foof:
.long foof
.long bar-53
.long bar
.quad foof
.quad bar-53
.quad bar
.section x,"a"
.zero 65036

View File

@ -4,6 +4,6 @@
// CHECK: relocation-before-merge-start.s.tmp.o:(.foo): offset is outside the section
.data
.long .foo - 1
.quad .foo - 1
.section .foo,"aM",@progbits,4
.quad 0

View File

@ -4,6 +4,6 @@
// CHECK: relocation-past-merge-end.s.tmp.o:(.foo): offset is outside the section
.data
.long .foo + 10
.quad .foo + 10
.section .foo,"aM",@progbits,4
.quad 0

View File

@ -0,0 +1,26 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
# RUN: not ld.lld -pie %t.o -o /dev/null 2>&1 | FileCheck %s
# RUN: not ld.lld -shared %t.o -o /dev/null 2>&1 | FileCheck %s
## Check we don't create dynamic relocations in a writable section,
## if the number of bits is smaller than the wordsize.
.globl hidden
.hidden hidden
local:
hidden:
# CHECK: error: relocation R_X86_64_8 cannot be used against local symbol; recompile with -fPIC
# CHECK-NEXT: >>> defined in {{.*}}.o
# CHECK-NEXT: >>> referenced by {{.*}}.o:(.data+0x0)
# CHECK: error: relocation R_X86_64_16 cannot be used against local symbol; recompile with -fPIC
# CHECK: error: relocation R_X86_64_32 cannot be used against local symbol; recompile with -fPIC
# CHECK: error: relocation R_X86_64_32 cannot be used against symbol hidden; recompile with -fPIC
.data
.byte local # R_X86_64_8
.short local # R_X86_64_16
.long local # R_X86_64_32
.long hidden # R_X86_64_32