forked from OSchip/llvm-project
[ELF][PPC64] Implement IPLT code sequence for non-preemptible IFUNC
Non-preemptible IFUNC are placed in in.iplt (.glink on EM_PPC64). If there is a non-GOT non-PLT relocation, for pointer equality, we change the type of the symbol from STT_IFUNC and STT_FUNC and bind it to the .glink entry. On EM_386, EM_X86_64, EM_ARM, and EM_AARCH64, the PLT code sequence loads the address from its associated .got.plt slot. An IPLT also has an associated .got.plt slot and can use the same code sequence. On EM_PPC64, the PLT code sequence is actually a bl instruction in .glink . It jumps to `__glink_PLTresolve` (the PLT header). and `__glink_PLTresolve` computes the .plt slot (relocated by R_PPC64_JUMP_SLOT). An IPLT does not have an associated R_PPC64_JUMP_SLOT, so we cannot use `bl` in .iplt . Instead, create a call stub which has a similar code sequence as PPC64PltCallStub. We don't save the TOC pointer, so such scenarios will not work: a function pointer to a non-preemptible ifunc, which resolves to a function defined in another DSO. This is the restriction described by https://sourceware.org/glibc/wiki/GNU_IFUNC (though on many architectures it works in practice): Requirement (a): Resolver must be defined in the same translation unit as the implementations. If an ifunc is taken address but not called, technically we don't need an entry for it, but we currently do that. This patch makes // clang -fuse-ld=lld -fno-pie -no-pie a.c // clang -fuse-ld=lld -fPIE -pie a.c #include <stdio.h> static void impl(void) { puts("meow"); } void thefunc(void) __attribute__((ifunc("resolver"))); void *resolver(void) { return &impl; } int main(void) { thefunc(); void (*theptr)(void) = &thefunc; theptr(); } work on Linux glibc and FreeBSD. Calling a function pointer pointing to a Non-preemptible IFUNC never worked before. Differential Revision: https://reviews.llvm.org/D71509
This commit is contained in:
parent
6f9b4c6826
commit
45acc35ac2
|
@ -9,6 +9,7 @@
|
|||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "Thunks.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
|
@ -202,6 +203,8 @@ public:
|
|||
void writePltHeader(uint8_t *buf) const override;
|
||||
void writePlt(uint8_t *buf, const Symbol &sym,
|
||||
uint64_t pltEntryAddr) const override;
|
||||
void writeIplt(uint8_t *buf, const Symbol &sym,
|
||||
uint64_t pltEntryAddr) const override;
|
||||
void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
void writeGotHeader(uint8_t *buf) const override;
|
||||
bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
|
||||
|
@ -298,7 +301,7 @@ PPC64::PPC64() {
|
|||
symbolicRel = R_PPC64_ADDR64;
|
||||
pltHeaderSize = 60;
|
||||
pltEntrySize = 4;
|
||||
ipltEntrySize = 4;
|
||||
ipltEntrySize = 16; // PPC64PltCallStub::size
|
||||
gotBaseSymInGotPlt = false;
|
||||
gotHeaderEntriesNum = 1;
|
||||
gotPltHeaderEntriesNum = 2;
|
||||
|
@ -676,6 +679,11 @@ void PPC64::writePlt(uint8_t *buf, const Symbol &sym,
|
|||
write32(buf, 0x48000000 | ((-offset) & 0x03FFFFFc));
|
||||
}
|
||||
|
||||
void PPC64::writeIplt(uint8_t *buf, const Symbol &sym,
|
||||
uint64_t /*pltEntryAddr*/) const {
|
||||
writePPC64LoadAndBranch(buf, sym.getGotPltVA() - getPPC64TocBase());
|
||||
}
|
||||
|
||||
static std::pair<RelType, uint64_t> toAddr16Rel(RelType type, uint64_t val) {
|
||||
// Relocations relative to the toc-base need to be adjusted by the Toc offset.
|
||||
uint64_t tocBiasedVal = val - ppc64TocOffset;
|
||||
|
|
|
@ -761,7 +761,7 @@ bool PPC32PltCallStub::isCompatibleWith(const InputSection &isec,
|
|||
return !config->isPic || (isec.file == file && rel.addend == addend);
|
||||
}
|
||||
|
||||
static void writePPCLoadAndBranch(uint8_t *buf, int64_t offset) {
|
||||
void writePPC64LoadAndBranch(uint8_t *buf, int64_t offset) {
|
||||
uint16_t offHa = (offset + 0x8000) >> 16;
|
||||
uint16_t offLo = offset & 0xffff;
|
||||
|
||||
|
@ -775,7 +775,7 @@ void PPC64PltCallStub::writeTo(uint8_t *buf) {
|
|||
int64_t offset = destination.getGotPltVA() - getPPC64TocBase();
|
||||
// Save the TOC pointer to the save-slot reserved in the call frame.
|
||||
write32(buf + 0, 0xf8410018); // std r2,24(r1)
|
||||
writePPCLoadAndBranch(buf + 4, offset);
|
||||
writePPC64LoadAndBranch(buf + 4, offset);
|
||||
}
|
||||
|
||||
void PPC64PltCallStub::addSymbols(ThunkSection &isec) {
|
||||
|
@ -787,7 +787,7 @@ void PPC64PltCallStub::addSymbols(ThunkSection &isec) {
|
|||
void PPC64LongBranchThunk::writeTo(uint8_t *buf) {
|
||||
int64_t offset = in.ppc64LongBranchTarget->getEntryVA(&destination, addend) -
|
||||
getPPC64TocBase();
|
||||
writePPCLoadAndBranch(buf, offset);
|
||||
writePPC64LoadAndBranch(buf, offset);
|
||||
}
|
||||
|
||||
void PPC64LongBranchThunk::addSymbols(ThunkSection &isec) {
|
||||
|
|
|
@ -68,6 +68,8 @@ public:
|
|||
// ThunkSection.
|
||||
Thunk *addThunk(const InputSection &isec, Relocation &rel);
|
||||
|
||||
void writePPC64LoadAndBranch(uint8_t *buf, int64_t offset);
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
|
|
|
@ -2,66 +2,94 @@
|
|||
|
||||
# RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %s -o %t.o
|
||||
# RUN: ld.lld %t.o -o %t
|
||||
# RUN: llvm-nm %t | FileCheck --check-prefix=NM %s
|
||||
# RUN: llvm-readelf -s %t | FileCheck --check-prefix=SYM %s
|
||||
# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SECTIONS %s
|
||||
# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s
|
||||
# RUN: llvm-readobj -r %t | FileCheck --check-prefix=REL %s
|
||||
|
||||
# RUN: llvm-mc -filetype=obj -triple=powerpc64-unknown-linux %s -o %t.o
|
||||
# RUN: ld.lld %t.o -o %t
|
||||
# RUN: llvm-nm %t | FileCheck --check-prefix=NM %s
|
||||
# RUN: llvm-readelf -s %t | FileCheck --check-prefix=SYM %s
|
||||
# RUN: llvm-readelf -S %t | FileCheck --check-prefix=SECTIONS %s
|
||||
# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s
|
||||
# RUN: llvm-readobj -r %t | FileCheck --check-prefix=REL %s
|
||||
|
||||
# NM-DAG: 0000000010028248 d .TOC.
|
||||
# NM-DAG: 00000000100101f8 i ifunc
|
||||
# NM-DAG: 00000000100101fc i ifunc2
|
||||
# SYM: Value Size Type Bind Vis Ndx
|
||||
# SYM: 0000000010028298 0 NOTYPE LOCAL HIDDEN 4 .TOC.
|
||||
# SYM: 0000000010010288 0 FUNC GLOBAL DEFAULT 3 ifunc1
|
||||
# SYM: 0000000010010210 0 IFUNC GLOBAL DEFAULT 2 ifunc2
|
||||
# SYM: 0000000010010278 0 FUNC GLOBAL DEFAULT 3 ifunc3
|
||||
|
||||
# SECTIONS: .plt NOBITS 0000000010030250 000250 000010 00 WA 0 0 8
|
||||
# SECTIONS: .plt NOBITS 00000000100302a0 0002a0 000018 00 WA 0 0 8
|
||||
|
||||
# __plt_ifunc - . = 0x10010218 - 0x10010208 = 16
|
||||
# __plt_ifunc2 - . = 0x1001022c - 0x10010210 = 28
|
||||
# CHECK: _start:
|
||||
# CHECK-NEXT: addis 2, 12, 2
|
||||
# CHECK-NEXT: addi 2, 2, -32696
|
||||
# CHECK-NEXT: 10010208: bl .+16
|
||||
# CHECK-NEXT: addi 2, 2, -32636
|
||||
# CHECK-NEXT: 1001021c: bl .+36
|
||||
# CHECK-NEXT: ld 2, 24(1)
|
||||
# CHECK-NEXT: 10010210: bl .+28
|
||||
# CHECK-NEXT: 10010224: bl .+48
|
||||
# CHECK-NEXT: ld 2, 24(1)
|
||||
# CHECK-NEXT: addis 3, 2, -2
|
||||
# CHECK-NEXT: addi 3, 3, 32752
|
||||
# CHECK-NEXT: addis 3, 2, -2
|
||||
# CHECK-NEXT: addi 3, 3, 32736
|
||||
|
||||
# .plt[0] - .TOC. = 0x10030250 - 0x10028248 = (1<<16) - 32760
|
||||
# CHECK: __plt_ifunc:
|
||||
# .plt[0] - .TOC. = 0x100302b0 - 0x100282a8 = (1<<16) - 32760
|
||||
# CHECK: __plt_ifunc2:
|
||||
# CHECK-NEXT: std 2, 24(1)
|
||||
# CHECK-NEXT: addis 12, 2, 1
|
||||
# CHECK-NEXT: ld 12, -32760(12)
|
||||
# CHECK-NEXT: mtctr 12
|
||||
# CHECK-NEXT: bctr
|
||||
|
||||
# .plt[1] - .TOC. = 0x10030250+8 - 0x10028248 = (1<<16) - 32752
|
||||
# CHECK: __plt_ifunc2:
|
||||
# .plt[1] - .TOC. = 0x100302b0+8 - 0x100282a8 = (1<<16) - 32752
|
||||
# CHECK: __plt_ifunc3:
|
||||
# CHECK-NEXT: std 2, 24(1)
|
||||
# CHECK-NEXT: addis 12, 2, 1
|
||||
# CHECK-NEXT: ld 12, -32752(12)
|
||||
# CHECK-NEXT: mtctr 12
|
||||
# CHECK-NEXT: bctr
|
||||
# CHECK-EMPTY:
|
||||
|
||||
## Check that we emit 2 R_PPC64_IRELATIVE in .rela.dyn.
|
||||
## glibc powerpc64 does not eagerly resolve R_PPC64_IRELATIVE if they are in .rela.plt.
|
||||
## .glink has 3 IPLT entries for ifunc1, ifunc2 and ifunc3.
|
||||
## ifunc2 and ifunc3 have the same code sequence as their PLT call stubs.
|
||||
# CHECK: Disassembly of section .glink:
|
||||
# CHECK-EMPTY:
|
||||
# CHECK-NEXT: 0000000010010268 .glink:
|
||||
# CHECK-NEXT: addis 12, 2, 1
|
||||
# CHECK-NEXT: ld 12, -32760(12)
|
||||
# CHECK-NEXT: mtctr 12
|
||||
# CHECK-NEXT: bctr
|
||||
# CHECK-EMPTY:
|
||||
# CHECK-NEXT: 0000000010010278 ifunc3:
|
||||
# CHECK-NEXT: addis 12, 2, 1
|
||||
# CHECK-NEXT: ld 12, -32752(12)
|
||||
# CHECK-NEXT: mtctr 12
|
||||
# CHECK-NEXT: bctr
|
||||
# CHECK-EMPTY:
|
||||
# CHECK-NEXT: 0000000010010288 ifunc1:
|
||||
# CHECK-NEXT: addis 12, 2, 1
|
||||
# CHECK-NEXT: ld 12, -32744(12)
|
||||
# CHECK-NEXT: mtctr 12
|
||||
# CHECK-NEXT: bctr
|
||||
|
||||
## Check that we emit 3 R_PPC64_IRELATIVE in .rela.dyn.
|
||||
# REL: .rela.dyn {
|
||||
# REL-NEXT: 0x10030250 R_PPC64_IRELATIVE - 0x100101F8
|
||||
# REL-NEXT: 0x10030258 R_PPC64_IRELATIVE - 0x100101FC
|
||||
# REL-NEXT: 0x100302A0 R_PPC64_IRELATIVE - 0x10010210
|
||||
# REL-NEXT: 0x100302A8 R_PPC64_IRELATIVE - 0x10010210
|
||||
# REL-NEXT: 0x100302B0 R_PPC64_IRELATIVE - 0x10010210
|
||||
# REL-NEXT: }
|
||||
|
||||
.type ifunc STT_GNU_IFUNC
|
||||
.globl ifunc
|
||||
ifunc:
|
||||
nop
|
||||
|
||||
.type ifunc2 STT_GNU_IFUNC
|
||||
.globl ifunc2
|
||||
.type ifunc1,@gnu_indirect_function
|
||||
.type ifunc2,@gnu_indirect_function
|
||||
.type ifunc3,@gnu_indirect_function
|
||||
.globl ifunc1, ifunc2, ifunc3
|
||||
ifunc1:
|
||||
ifunc2:
|
||||
nop
|
||||
ifunc3:
|
||||
blr
|
||||
|
||||
.global _start
|
||||
.type _start,@function
|
||||
|
@ -72,7 +100,20 @@ _start:
|
|||
addi 2, 2, .TOC.-.Lfunc_gep0@l
|
||||
.Lfunc_lep0:
|
||||
.localentry _start, .Lfunc_lep0-.Lfunc_gep0
|
||||
bl ifunc
|
||||
nop
|
||||
|
||||
## ifunc1 is taken address.
|
||||
## ifunc2 is called.
|
||||
## ifunc3 is both taken address and called.
|
||||
## We need to create IPLT entries in .glink for ifunc1 and ifunc3, change
|
||||
## their types from STT_GNU_IFUNC to STT_FUNC, and set their st_shndx/st_value
|
||||
## to their .glink entries. Technically we don't need an entry for ifunc2 in
|
||||
## .glink, but we currently do that.
|
||||
bl ifunc2
|
||||
nop
|
||||
bl ifunc3
|
||||
nop
|
||||
|
||||
addis 3, 2, ifunc1@toc@ha
|
||||
addi 3, 3, ifunc1@toc@l
|
||||
addis 3, 2, ifunc3@toc@ha
|
||||
addi 3, 3, ifunc3@toc@l
|
||||
|
|
|
@ -14,12 +14,12 @@
|
|||
## to the address of the canonical PLT is fixed.
|
||||
|
||||
# SEC: .text PROGBITS 00000000100101e0
|
||||
# SEC: .plt NOBITS 00000000100301f8
|
||||
# SEC: .plt NOBITS 0000000010030200
|
||||
# SEC: 00000000100101e8 0 FUNC GLOBAL DEFAULT 3 ifunc
|
||||
|
||||
## .toc[0] stores the address of the canonical PLT.
|
||||
# HEX: section '.toc':
|
||||
# HEX-NEXT: 0x100201f0 e8010110 00000000
|
||||
# HEX-NEXT: 0x100201f8 e8010110 00000000
|
||||
|
||||
# REL: .rela.dyn {
|
||||
# REL-NEXT: 0x100301f8 R_PPC64_IRELATIVE - 0x100101e8
|
||||
|
|
Loading…
Reference in New Issue