From 7dc5af75ae5b8bcfce269fbd21c893cc2e4a61f0 Mon Sep 17 00:00:00 2001 From: Peter Smith Date: Wed, 28 Nov 2018 10:04:55 +0000 Subject: [PATCH] [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 --- lld/ELF/SyntheticSections.cpp | 14 +++++++- lld/test/ELF/aarch64-combined-dynrel-ifunc.s | 51 ++++++++++++++++++++++++++++ lld/test/ELF/aarch64-combined-dynrel.s | 41 ++++++++++++++++++++++ lld/test/ELF/arm-combined-dynrel-ifunc.s | 49 ++++++++++++++++++++++++++ lld/test/ELF/x86-64-combined-dynrel.s | 40 ++++++++++++++++++++++ 5 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 lld/test/ELF/aarch64-combined-dynrel-ifunc.s create mode 100644 lld/test/ELF/aarch64-combined-dynrel.s create mode 100644 lld/test/ELF/arm-combined-dynrel-ifunc.s create mode 100644 lld/test/ELF/x86-64-combined-dynrel.s diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index 9f4554e..c2eeaf5 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -1255,6 +1255,18 @@ void DynamicSection::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 void DynamicSection::finalizeContents() { // Set DT_FLAGS and DT_FLAGS_1. @@ -1335,7 +1347,7 @@ template void DynamicSection::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); diff --git a/lld/test/ELF/aarch64-combined-dynrel-ifunc.s b/lld/test/ELF/aarch64-combined-dynrel-ifunc.s new file mode 100644 index 0000000..5e84b03 --- /dev/null +++ b/lld/test/ELF/aarch64-combined-dynrel-ifunc.s @@ -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 diff --git a/lld/test/ELF/aarch64-combined-dynrel.s b/lld/test/ELF/aarch64-combined-dynrel.s new file mode 100644 index 0000000..438c250 --- /dev/null +++ b/lld/test/ELF/aarch64-combined-dynrel.s @@ -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 diff --git a/lld/test/ELF/arm-combined-dynrel-ifunc.s b/lld/test/ELF/arm-combined-dynrel-ifunc.s new file mode 100644 index 0000000..2761357 --- /dev/null +++ b/lld/test/ELF/arm-combined-dynrel-ifunc.s @@ -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) diff --git a/lld/test/ELF/x86-64-combined-dynrel.s b/lld/test/ELF/x86-64-combined-dynrel.s new file mode 100644 index 0000000..b1bc697 --- /dev/null +++ b/lld/test/ELF/x86-64-combined-dynrel.s @@ -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 -- 2.7.4