[ELF] Add -z rel and -z rela

LLD supports both REL and RELA for static relocations, but emits either
of REL and RELA for dynamic relocations. The relocation entry format is
specified by each psABI.

musl ld.so supports both REL and RELA. For such ld.so implementations,
REL (.rel.dyn .rel.plt) has size benefits even if the psABI chooses RELA:
sizeof(Elf64_Rel)=16 < sizeof(Elf64_Rela)=24.

* COPY, GLOB_DAT and J[U]MP_SLOT always have 0 addend. A ld.so
  implementation does not need to read the implicit addend.
  REL is strictly better.
* A RELATIVE has a non-zero addend. Such relocations can be packed
  compactly with the RELR relocation entry format, which is out of scope
  of this patch.
* For other dynamic relocation types (e.g. symbolic relocation R_X86_64_64),
  a ld.so implementation needs to read the implicit addend. REL may have
  minor performance impact, because reading implicit addends forces
  random access reads instead of being able to blast out a bunch of
  writes while chasing the relocation array.

This patch adds -z rel and -z rela to change the relocation entry format
for dynamic relocations. I have tested that a -z rel produced x86-64
executable works with musl ld.so

-z rela may be useful for debugging purposes on processors whose psABIs
specify REL as the canonical format: addends can be easily read by a tool.

Reviewed By: grimar, mcgrathr

Differential Revision: https://reviews.llvm.org/D80496
This commit is contained in:
Fangrui Song 2020-05-29 14:22:03 -07:00
parent 2d7fdab8e3
commit 881c5eef98
4 changed files with 157 additions and 15 deletions

View File

@ -422,11 +422,11 @@ static bool isKnownZFlag(StringRef s) {
s == "nodelete" || s == "nodlopen" || s == "noexecstack" ||
s == "nognustack" || s == "nokeep-text-section-prefix" ||
s == "norelro" || s == "noseparate-code" || s == "notext" ||
s == "now" || s == "origin" || s == "pac-plt" || s == "relro" ||
s == "retpolineplt" || s == "rodynamic" || s == "shstk" ||
s == "text" || s == "undefs" || s == "wxneeded" ||
s.startswith("common-page-size=") || s.startswith("max-page-size=") ||
s.startswith("stack-size=");
s == "now" || s == "origin" || s == "pac-plt" || s == "rel" ||
s == "rela" || s == "relro" || s == "retpolineplt" ||
s == "rodynamic" || s == "shstk" || s == "text" || s == "undefs" ||
s == "wxneeded" || s.startswith("common-page-size=") ||
s.startswith("max-page-size=") || s.startswith("stack-size=");
}
// Report an error for an unknown -z option.
@ -842,6 +842,22 @@ static std::vector<StringRef> getSymbolOrderingFile(MemoryBufferRef mb) {
return names.takeVector();
}
static bool getIsRela(opt::InputArgList &args) {
// If -z rel or -z rela is specified, use the last option.
for (auto *arg : args.filtered_reverse(OPT_z)) {
StringRef s(arg->getValue());
if (s == "rel")
return false;
if (s == "rela")
return true;
}
// Otherwise use the psABI defined relocation entry format.
uint16_t m = config->emachine;
return m == EM_AARCH64 || m == EM_AMDGPU || m == EM_HEXAGON || m == EM_PPC ||
m == EM_PPC64 || m == EM_RISCV || m == EM_X86_64;
}
static void parseClangOption(StringRef opt, const Twine &msg) {
std::string err;
raw_string_ostream os(err);
@ -1204,20 +1220,19 @@ static void setConfigs(opt::InputArgList &args) {
// ELF defines two different ways to store relocation addends as shown below:
//
// Rel: Addends are stored to the location where relocations are applied.
// Rel: Addends are stored to the location where relocations are applied. It
// cannot pack the full range of addend values for all relocation types, but
// this only affects relocation types that we don't support emitting as
// dynamic relocations (see getDynRel).
// Rela: Addends are stored as part of relocation entry.
//
// In other words, Rela makes it easy to read addends at the price of extra
// 4 or 8 byte for each relocation entry. We don't know why ELF defined two
// different mechanisms in the first place, but this is how the spec is
// defined.
// 4 or 8 byte for each relocation entry.
//
// You cannot choose which one, Rel or Rela, you want to use. Instead each
// ABI defines which one you need to use. The following expression expresses
// that.
config->isRela = m == EM_AARCH64 || m == EM_AMDGPU || m == EM_HEXAGON ||
m == EM_PPC || m == EM_PPC64 || m == EM_RISCV ||
m == EM_X86_64;
// We pick the format for dynamic relocations according to the psABI for each
// processor, but a contrary choice can be made if the dynamic loader
// supports.
config->isRela = getIsRela(args);
// If the output uses REL relocations we must store the dynamic relocation
// addends to the output sections. We also store addends for RELA relocations

View File

@ -720,6 +720,12 @@ processing.
.It Cm pac-plt
AArch64 only, use pointer authentication in PLT.
.Pp
.It Cm rel
Use REL format for dynamic relocations.
.Pp
.It Cm rela
Use RELA format for dynamic relocations.
.Pp
.It Cm retpolineplt
Emit retpoline format PLT entries as a mitigation for CVE-2017-5715.
.Pp

View File

@ -0,0 +1,63 @@
# REQUIRES: x86
## The i386 psABI uses Elf64_Rela relocation entries. We produce
## Elf32_Rel dynamic relocations by default, but can use Elf32_Rela with -z rela.
# RUN: llvm-mc -filetype=obj -triple=i386 %s -o %t.o
# RUN: ld.lld -shared %t.o -o %t.so
# RUN: llvm-readobj -d -r -x .data %t.so | FileCheck --check-prefix=REL %s
# RUN: ld.lld -shared -z rel %t.o -o %t1.so
# RUN: llvm-readobj -d -r -x .data %t1.so | FileCheck --check-prefix=REL %s
# REL: REL {{.*}}
# REL-NEXT: RELSZ 32 (bytes)
# REL-NEXT: RELENT 8 (bytes)
# REL-NEXT: RELCOUNT 1
# REL-NEXT: JMPREL {{.*}}
# REL-NEXT: PLTRELSZ 8 (bytes)
# REL-NEXT: PLTGOT {{.*}}
# REL-NEXT: PLTREL REL{{$}}
# REL: .rel.dyn {
# REL-NEXT: R_386_RELATIVE - 0x0
# REL-NEXT: R_386_GLOB_DAT func 0x0
# REL-NEXT: R_386_TLS_TPOFF tls 0x0
# REL-NEXT: R_386_32 _start 0x0
# REL-NEXT: }
# REL-NEXT: .rel.plt {
# REL-NEXT: R_386_JUMP_SLOT func 0x0
# REL-NEXT: }
# REL: Hex dump of section '.data':
# REL-NEXT: 0x000042cc cc420000 2a000000
# RUN: ld.lld -shared -z rel -z rela %t.o -o %t2.so
# RUN: llvm-readobj -d -r %t2.so | FileCheck --check-prefix=RELA %s
# RELA: RELA {{.*}}
# RELA-NEXT: RELASZ 48 (bytes)
# RELA-NEXT: RELAENT 12 (bytes)
# RELA-NEXT: RELACOUNT 1
# RELA-NEXT: JMPREL {{.*}}
# RELA-NEXT: PLTRELSZ 12 (bytes)
# RELA-NEXT: PLTGOT {{.*}}
# RELA-NEXT: PLTREL RELA
# RELA: .rela.dyn {
# RELA-NEXT: R_386_RELATIVE - 0x42EC
# RELA-NEXT: R_386_GLOB_DAT func 0x0
# RELA-NEXT: R_386_TLS_TPOFF tls 0x2A
# RELA-NEXT: R_386_32 _start 0x2A
# RELA-NEXT: }
# RELA-NEXT: .rela.plt {
# RELA-NEXT: R_386_JUMP_SLOT func 0x0
# RELA-NEXT: }
.globl _start
_start:
call func@PLT
movl func@GOT(%eax), %eax
.section .text1,"awx"
movl %gs:tls@NTPOFF+42, %eax
.data
.long .data
.long _start+42

View File

@ -0,0 +1,58 @@
# REQUIRES: x86
## The x86-64 psABI uses Elf64_Rela relocation entries. We produce
## Elf64_Rel dynamic relocations by default, but can use Elf64_Rel with -z rel.
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
# RUN: ld.lld -shared %t.o -o %t.so
# RUN: llvm-readobj -d -r %t.so | FileCheck --check-prefix=RELA %s
# RUN: ld.lld -shared -z rela %t.o -o %t1.so
# RUN: llvm-readobj -d -r %t1.so | FileCheck --check-prefix=RELA %s
# RELA: RELA {{.*}}
# RELA-NEXT: RELASZ 72 (bytes)
# RELA-NEXT: RELAENT 24 (bytes)
# RELA-NEXT: RELACOUNT 1
# RELA-NEXT: JMPREL {{.*}}
# RELA-NEXT: PLTRELSZ 24 (bytes)
# RELA-NEXT: PLTGOT {{.*}}
# RELA-NEXT: PLTREL RELA
# RELA: .rela.dyn {
# RELA-NEXT: R_X86_64_RELATIVE - 0x3428
# RELA-NEXT: R_X86_64_GLOB_DAT func 0x0
# RELA-NEXT: R_X86_64_64 _start 0x2A
# RELA-NEXT: }
# RELA-NEXT: .rela.plt {
# RELA-NEXT: R_X86_64_JUMP_SLOT func 0x0
# RELA-NEXT: }
# RUN: ld.lld -shared -z rela -z rel %t.o -o %t2.so
# RUN: llvm-readobj -d -r -x .data %t2.so | FileCheck --check-prefix=REL %s
# REL: REL {{.*}}
# REL-NEXT: RELSZ 48 (bytes)
# REL-NEXT: RELENT 16 (bytes)
# REL-NEXT: RELCOUNT 1
# REL-NEXT: JMPREL {{.*}}
# REL-NEXT: PLTRELSZ 16 (bytes)
# REL-NEXT: PLTGOT {{.*}}
# REL-NEXT: PLTREL REL{{$}}
# REL: .rel.dyn {
# REL-NEXT: R_X86_64_RELATIVE - 0x0
# REL-NEXT: R_X86_64_GLOB_DAT func 0x0
# REL-NEXT: R_X86_64_64 _start 0
# REL-NEXT: }
# REL-NEXT: .rel.plt {
# REL-NEXT: R_X86_64_JUMP_SLOT func 0x0
# REL-NEXT: }
# REL: Hex dump of section '.data':
# REL-NEXT: 0x00003408 08340000 00000000 2a000000 00000000
.globl _start
_start:
call func@PLT
movq func@GOTPCREL(%rip), %rax
.data
.quad .data
.quad _start+42