diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index 9f4554e1d63d..c2eeaf546f60 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 000000000000..5e84b03d7b2e --- /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 000000000000..438c2509906e --- /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 000000000000..2761357fd98d --- /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 000000000000..b1bc697d148b --- /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