[ELF] Use more specific method to calculate DT_PLTRELSZ

The DT_PLTRELSZ dynamic tag is calculated using the size of the
OutputSection containing the In.RelaPlt InputSection. This will work for the
default no linker script case and the majority of linker scripts.
Unfortunately it doesn't work for some 'almost' sensible linker scripts. It
is permitted by ELF to have a single OutputSection containing both
In.RelaDyn, In.RelaPlt and In.RelaIPlt. It is also permissible for the range
of memory [DT_RELA, DT_RELA + DT_RELASZ) and the range
[DT_JMPREL, DT_JMPREL + DT_JMPRELSZ) to overlap as long as the the latter
range is at the end.

To support this type of linker script use the specific InputSection sizes.

Fixes pr39678

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

llvm-svn: 347736
This commit is contained in:
Peter Smith 2018-11-28 10:04:55 +00:00
parent 06acb3a236
commit 7dc5af75ae
5 changed files with 194 additions and 1 deletions

View File

@ -1255,6 +1255,18 @@ void DynamicSection<ELFT>::addSym(int32_t Tag, Symbol *Sym) {
Entries.push_back({Tag, [=] { return Sym->getVA(); }});
}
// A Linker script may assign the RELA relocation sections to the same
// output section. When this occurs we cannot just use the OutputSection
// Size. Moreover the [DT_JMPREL, DT_JMPREL + DT_PLTRELSZ) is permitted to
// overlap with the [DT_RELA, DT_RELA + DT_RELASZ).
static uint64_t addPltRelSz() {
size_t Size = In.RelaPlt->getSize();
if (In.RelaIplt->getParent() == In.RelaPlt->getParent() &&
In.RelaIplt->Name == In.RelaPlt->Name)
Size += In.RelaIplt->getSize();
return Size;
}
// Add remaining entries to complete .dynamic contents.
template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
// Set DT_FLAGS and DT_FLAGS_1.
@ -1335,7 +1347,7 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
// .rel[a].plt section.
if (In.RelaPlt->getParent()->Live) {
addInSec(DT_JMPREL, In.RelaPlt);
addSize(DT_PLTRELSZ, In.RelaPlt->getParent());
Entries.push_back({DT_PLTRELSZ, addPltRelSz});
switch (Config->EMachine) {
case EM_MIPS:
addInSec(DT_MIPS_PLTGOT, In.GotPlt);

View File

@ -0,0 +1,51 @@
// REQUIRES: AArch64
// RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %p/Inputs/shared.s -o %t-lib.o
// RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %s -o %t.o
// RUN: ld.lld %t-lib.o --shared -o %t.so
// RUN: echo "SECTIONS { \
// RUN: .text : { *(.text) } \
// RUN: .rela.dyn : { *(.rela.dyn) *(.rela.plt) } \
// RUN: } " > %t.script
// RUN: ld.lld %t.o -o %t.axf %t.so --script %t.script
// RUN: llvm-readobj --section-headers --dynamic-table %t.axf | FileCheck %s
// The linker script above combines the .rela.dyn and .rela.plt into a single
// table. ELF is clear that the DT_PLTRELSZ should match the subset of
// relocations that is associated with the PLT. It is less clear about what
// the value of DT_RELASZ should be. ELF implies that it should be the size
// of the single table so that DT_RELASZ includes DT_PLTRELSZ. The loader in
// glibc permits this as long as .rela.plt comes after .rela.dyn in the
// combined table. In the ARM case irelative relocations do not count as PLT
// relocs. In the AArch64 case irelative relocations count as PLT relocs.
.text
.globl indirect
.type indirect,@gnu_indirect_function
indirect:
ret
.globl bar // from Inputs/shared.s
.text
.globl _start
.type _start,@function
main:
bl indirect
bl bar
adrp x8, :got:indirect
ldr x8, [x8, :got_lo12:indirect]
adrp x8, :got:bar
ldr x8, [x8, :got_lo12:bar]
ret
// CHECK: Name: .rela.dyn
// CHECK-NEXT: Type: SHT_RELA
// CHECK-NEXT: Flags [
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: ]
// CHECK-NEXT: Address:
// CHECK-NEXT: Offset:
// CHECK-NEXT: Size: 72
// CHECK: 0x0000000000000008 RELASZ 72
// CHECK: 0x0000000000000002 PLTRELSZ 48

View File

@ -0,0 +1,41 @@
// REQUIRES: AArch64
// RUN: llvm-mc --triple=aarch64-linux-gnu -filetype=obj -o %t.o %s
// RUN: echo "SECTIONS { \
// RUN: .text : { *(.text) } \
// RUN: .rela.dyn : { *(.rela.dyn) *(.rela.plt) } \
// RUN: } " > %t.script
// RUN: ld.lld %t.o -o %t.so --shared --script %t.script
// RUN: llvm-readobj --section-headers --dynamic-table %t.so | FileCheck %s
// The linker script above combines the .rela.dyn and .rela.plt into a single
// table. ELF is clear that the DT_PLTRELSZ should match the subset of
// relocations that is associated with the PLT. It is less clear about what
// the value of DT_RELASZ should be. ELF implies that it should be the size
// of the single table so that DT_RELASZ includes DT_PLTRELSZ. The loader in
// glibc permits this as long as .rela.plt comes after .rela.dyn in the
// combined table.
.text
.globl func
.type func, %function
.globl foo
.type foo, %object
.globl _start
.type _start, %function
_start:
bl func
adrp x8, :got:foo
ldr x8, [x8, :got_lo12:foo]
ret
// CHECK: Name: .rela.dyn
// CHECK-NEXT: Type: SHT_RELA
// CHECK-NEXT: Flags [
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: ]
// CHECK-NEXT: Address:
// CHECK-NEXT: Offset:
// CHECK-NEXT: Size: 48
// CHECK: 0x0000000000000008 RELASZ 48
// CHECK: 0x0000000000000002 PLTRELSZ 24

View File

@ -0,0 +1,49 @@
// REQUIRES: arm
// RUN: llvm-mc -filetype=obj -arm-add-build-attributes -triple=armv7a-linux-gnueabihf %p/Inputs/arm-shared.s -o %t-lib.o
// RUN: llvm-mc -filetype=obj -arm-add-build-attributes -triple=armv7a-linux-gnueabihf %s -o %t.o
// RUN: ld.lld %t-lib.o --shared -o %t.so
// RUN: echo "SECTIONS { \
// RUN: .text : { *(.text) } \
// RUN: .rela.dyn : { *(.rel.dyn) *(.rel.plt) } \
// RUN: } " > %t.script
// RUN: ld.lld %t.o -o %t.axf %t.so --script %t.script
// RUN: llvm-readobj --section-headers --dynamic-table %t.axf | FileCheck %s
// The linker script above combines the .rela.dyn and .rela.plt into a single
// table. ELF is clear that the DT_PLTRELSZ should match the subset of
// relocations that is associated with the PLT. It is less clear about what
// the value of DT_RELASZ should be. ELF implies that it should be the size
// of the single table so that DT_RELASZ includes DT_PLTRELSZ. The loader in
// glibc permits this as long as .rela.plt comes after .rela.dyn in the
// combined table. In the ARM case irelative relocations do not count as PLT
// relocs.
.text
.globl indirect
.type indirect,%gnu_indirect_function
indirect:
bx lr
.globl bar2 // from Inputs/arm-shared.s
.text
.globl _start
.type _start,%function
main:
bl indirect
bl bar2
.word indirect(got)
.word bar2(got)
bx lr
// CHECK: Name: .rela.dyn
// CHECK-NEXT: Type: SHT_REL
// CHECK-NEXT: Flags [
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: ]
// CHECK-NEXT: Address:
// CHECK-NEXT: Offset:
// CHECK-NEXT: Size: 24
// CHECK: 0x00000012 RELSZ 24 (bytes)
// CHECK: 0x00000002 PLTRELSZ 8 (bytes)

View File

@ -0,0 +1,40 @@
# REQUIRES: x86
# RUN: llvm-mc --triple=x86_64-pc-linux -filetype=obj -o %t.o %s
# RUN: echo "SECTIONS { \
# RUN: .text : { *(.text) } \
# RUN: .rela.dyn : { *(.rela.dyn) *(.rela.plt) } \
# RUN: } " > %t.script
# RUN: ld.lld %t.o -o %t.so --shared --script %t.script
# RUN: llvm-readobj --section-headers --dynamic-table %t.so | FileCheck %s
// The linker script above combines the .rela.dyn and .rela.plt into a single
// table. ELF is clear that the DT_PLTRELSZ should match the subset of
// relocations that is associated with the PLT. It is less clear about what
// the value of DT_RELASZ should be. ELF implies that it should be the size
// of the single table so that DT_RELASZ includes DT_PLTRELSZ. The loader in
// glibc permits this as long as .rela.plt comes after .rela.dyn in the
// combined table.
.text
.globl func
.type func, %function
.globl foo
.type foo, %object
.globl _start
.type _start, %function
_start:
call func@plt
movq func@GOTPCREL (%rip), %rax
# CHECK: Name: .rela.dyn
# CHECK-NEXT: Type: SHT_RELA
# CHECK-NEXT: Flags [
# CHECK-NEXT: SHF_ALLOC
# CHECK-NEXT: ]
# CHECK-NEXT: Address:
# CHECK-NEXT: Offset:
# CHECK-NEXT: Size: 48
# CHECK: 0x0000000000000008 RELASZ 48
# CHECK: 0x0000000000000002 PLTRELSZ 24