forked from OSchip/llvm-project
[ELF][PPC32] Implement IPLT code sequence for non-preemptible IFUNC
Similar to D71509 (EM_PPC64), on EM_PPC, the IPLT code sequence should be similar to a PLT call stub. Unlike EM_PPC64, EM_PPC -msecure-plt has small/large PIC model differences. * -fpic/-fpie: R_PPC_PLTREL24 r_addend=0. The call stub loads an address relative to `_GLOBAL_OFFSET_TABLE_`. * -fPIC/-fPIE: R_PPC_PLTREL24 r_addend=0x8000. (A partial linked object file may have an addend larger than 0x8000.) The call stub loads an address relative to .got2+0x8000. Just assume large PIC model for now. This patch makes: // clang -fuse-ld=lld -msecure-plt -fno-pie -no-pie a.c // clang -fuse-ld=lld -msecure-plt -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. -fpie will crash because the compiler and the linker do not agree on the value which r30 stores (_GLOBAL_OFFSET_TABLE_ vs .got2+0x8000). Differential Revision: https://reviews.llvm.org/D71621
This commit is contained in:
parent
45acc35ac2
commit
fb2944bd7f
|
@ -10,6 +10,7 @@
|
|||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "Thunks.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
|
@ -35,6 +36,8 @@ public:
|
|||
uint64_t pltEntryAddr) const override {
|
||||
llvm_unreachable("should call writePPC32GlinkSection() instead");
|
||||
}
|
||||
void writeIplt(uint8_t *buf, const Symbol &sym,
|
||||
uint64_t pltEntryAddr) const override;
|
||||
void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
|
||||
bool needsThunk(RelExpr expr, RelType relocType, const InputFile *file,
|
||||
uint64_t branchAddr, const Symbol &s,
|
||||
|
@ -144,7 +147,7 @@ PPC::PPC() {
|
|||
gotPltHeaderEntriesNum = 0;
|
||||
pltHeaderSize = 64; // size of PLTresolve in .glink
|
||||
pltEntrySize = 4;
|
||||
ipltEntrySize = 4;
|
||||
ipltEntrySize = 16;
|
||||
|
||||
needsThunks = true;
|
||||
|
||||
|
@ -158,6 +161,13 @@ PPC::PPC() {
|
|||
write32(trapInstr.data(), 0x7fe00008);
|
||||
}
|
||||
|
||||
void PPC::writeIplt(uint8_t *buf, const Symbol &sym,
|
||||
uint64_t /*pltEntryAddr*/) const {
|
||||
// In -pie or -shared mode, assume r30 points to .got2+0x8000, and use a
|
||||
// .got2.plt_pic32. thunk.
|
||||
writePPC32PltCallStub(buf, sym.getGotPltVA(), sym.file, 0x8000);
|
||||
}
|
||||
|
||||
void PPC::writeGotHeader(uint8_t *buf) const {
|
||||
// _GLOBAL_OFFSET_TABLE_[0] = _DYNAMIC
|
||||
// glibc stores _dl_runtime_resolve in _GLOBAL_OFFSET_TABLE_[1],
|
||||
|
|
|
@ -707,13 +707,13 @@ InputSection *MicroMipsR6Thunk::getTargetInputSection() const {
|
|||
return dyn_cast<InputSection>(dr.section);
|
||||
}
|
||||
|
||||
void PPC32PltCallStub::writeTo(uint8_t *buf) {
|
||||
void writePPC32PltCallStub(uint8_t *buf, uint64_t gotPltVA,
|
||||
const InputFile *file, int64_t addend) {
|
||||
if (!config->isPic) {
|
||||
uint64_t va = destination.getGotPltVA();
|
||||
write32(buf + 0, 0x3d600000 | (va + 0x8000) >> 16); // lis r11,ha
|
||||
write32(buf + 4, 0x816b0000 | (uint16_t)va); // lwz r11,l(r11)
|
||||
write32(buf + 8, 0x7d6903a6); // mtctr r11
|
||||
write32(buf + 12, 0x4e800420); // bctr
|
||||
write32(buf + 0, 0x3d600000 | (gotPltVA + 0x8000) >> 16); // lis r11,ha
|
||||
write32(buf + 4, 0x816b0000 | (uint16_t)gotPltVA); // lwz r11,l(r11)
|
||||
write32(buf + 8, 0x7d6903a6); // mtctr r11
|
||||
write32(buf + 12, 0x4e800420); // bctr
|
||||
return;
|
||||
}
|
||||
uint32_t offset;
|
||||
|
@ -721,12 +721,12 @@ void PPC32PltCallStub::writeTo(uint8_t *buf) {
|
|||
// The stub loads an address relative to r30 (.got2+Addend). Addend is
|
||||
// almost always 0x8000. The address of .got2 is different in another object
|
||||
// file, so a stub cannot be shared.
|
||||
offset = destination.getGotPltVA() - (in.ppc32Got2->getParent()->getVA() +
|
||||
file->ppc32Got2OutSecOff + addend);
|
||||
offset = gotPltVA - (in.ppc32Got2->getParent()->getVA() +
|
||||
file->ppc32Got2OutSecOff + addend);
|
||||
} else {
|
||||
// The stub loads an address relative to _GLOBAL_OFFSET_TABLE_ (which is
|
||||
// currently the address of .got).
|
||||
offset = destination.getGotPltVA() - in.got->getVA();
|
||||
offset = gotPltVA - in.got->getVA();
|
||||
}
|
||||
uint16_t ha = (offset + 0x8000) >> 16, l = (uint16_t)offset;
|
||||
if (ha == 0) {
|
||||
|
@ -742,6 +742,10 @@ void PPC32PltCallStub::writeTo(uint8_t *buf) {
|
|||
}
|
||||
}
|
||||
|
||||
void PPC32PltCallStub::writeTo(uint8_t *buf) {
|
||||
writePPC32PltCallStub(buf, destination.getGotPltVA(), file, addend);
|
||||
}
|
||||
|
||||
void PPC32PltCallStub::addSymbols(ThunkSection &isec) {
|
||||
std::string buf;
|
||||
raw_string_ostream os(buf);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
namespace lld {
|
||||
namespace elf {
|
||||
class Defined;
|
||||
class InputFile;
|
||||
class Symbol;
|
||||
class ThunkSection;
|
||||
// Class to describe an instance of a Thunk.
|
||||
|
@ -68,6 +69,8 @@ public:
|
|||
// ThunkSection.
|
||||
Thunk *addThunk(const InputSection &isec, Relocation &rel);
|
||||
|
||||
void writePPC32PltCallStub(uint8_t *buf, uint64_t gotPltVA,
|
||||
const InputFile *file, int64_t addend);
|
||||
void writePPC64LoadAndBranch(uint8_t *buf, int64_t offset);
|
||||
|
||||
} // namespace elf
|
||||
|
|
|
@ -1,23 +1,19 @@
|
|||
# REQUIRES: ppc, asserts
|
||||
# XFAIL: *
|
||||
# REQUIRES: ppc
|
||||
# RUN: llvm-mc -filetype=obj -triple=powerpc %s -o %t.o
|
||||
# RUN: ld.lld %t.o -o %t
|
||||
# RUN: llvm-readobj -r %t | FileCheck --check-prefix=RELOC %s
|
||||
# RUN: llvm-readelf -s %t | FileCheck --check-prefix=SYM %s
|
||||
# RUN: llvm-readelf -x .got2 %t | FileCheck --check-prefix=HEX %s
|
||||
# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s
|
||||
|
||||
# RELOC: .rela.dyn {
|
||||
# RELOC-NEXT: 0x10020108 R_PPC_IRELATIVE - 0x100100E0
|
||||
# RELOC-NEXT: 0x10020110 R_PPC_IRELATIVE - 0x100100E0
|
||||
# RELOC-NEXT: }
|
||||
|
||||
# SYM: 10010100 0 FUNC GLOBAL DEFAULT {{.*}} func
|
||||
# HEX: 0x10020104 10010100
|
||||
# HEX: 0x10020110 10010100
|
||||
|
||||
.section .got2,"aw"
|
||||
.long func
|
||||
|
||||
# CHECK: func_resolver:
|
||||
# CHECK: Disassembly of section .text:
|
||||
# CHECK: .text:
|
||||
# CHECK-NEXT: 100100e0: blr
|
||||
# CHECK: _start:
|
||||
# CHECK-NEXT: bl .+12
|
||||
|
@ -25,17 +21,14 @@
|
|||
# CHECK-NEXT: addi 9, 9, 256
|
||||
# CHECK-EMPTY:
|
||||
# CHECK-NEXT: 00000000.plt_call32.func:
|
||||
## 0x10020108 = 65536*4098+264
|
||||
## 0x10020110 = 65536*4098+272
|
||||
# CHECK-NEXT: lis 11, 4098
|
||||
# CHECK-NEXT: lwz 11, 264(11)
|
||||
# CHECK-NEXT: lwz 11, 272(11)
|
||||
|
||||
.text
|
||||
.globl func
|
||||
.type func, @gnu_indirect_function
|
||||
func:
|
||||
.globl func_resolver
|
||||
.type func_resolver, @function
|
||||
func_resolver:
|
||||
blr
|
||||
|
||||
.globl _start
|
|
@ -0,0 +1,46 @@
|
|||
# REQUIRES: ppc
|
||||
# RUN: llvm-mc -filetype=obj -triple=powerpc %s -o %t.o
|
||||
# RUN: ld.lld -pie %t.o -o %t
|
||||
# RUN: llvm-readobj -r %t | FileCheck --check-prefix=RELOC %s
|
||||
# RUN: llvm-readelf -s %t | FileCheck --check-prefix=SYM %s
|
||||
# RUN: llvm-readelf -x .got2 %t | FileCheck --check-prefix=HEX %s
|
||||
# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s
|
||||
|
||||
# RELOC: .rela.dyn {
|
||||
# RELOC-NEXT: 0x30240 R_PPC_RELATIVE - 0x101A8
|
||||
# RELOC-NEXT: 0x30244 R_PPC_IRELATIVE - 0x10188
|
||||
# RELOC-NEXT: }
|
||||
|
||||
# SYM: 000101a8 0 FUNC GLOBAL DEFAULT {{.*}} func
|
||||
# HEX: 0x00030240 00000000
|
||||
|
||||
.section .got2,"aw"
|
||||
.long func
|
||||
|
||||
# CHECK: Disassembly of section .text:
|
||||
# CHECK: .text:
|
||||
# CHECK-NEXT: 10188: blr
|
||||
# CHECK: _start:
|
||||
# CHECK-NEXT: bl .+12
|
||||
# CHECK-NEXT: lis 9, 1
|
||||
# CHECK-NEXT: addi 9, 9, 424
|
||||
# CHECK-EMPTY:
|
||||
# CHECK-NEXT: 00008000.got2.plt_pic32.func:
|
||||
## 0x10020114 = 65536*4098+276
|
||||
# CHECK-NEXT: lwz 11, -32764(30)
|
||||
# CHECK-NEXT: mtctr 11
|
||||
# CHECK-NEXT: bctr
|
||||
# CHECK-NEXT: nop
|
||||
|
||||
.text
|
||||
.globl func
|
||||
.type func, @gnu_indirect_function
|
||||
func:
|
||||
blr
|
||||
|
||||
.globl _start
|
||||
_start:
|
||||
bl func+0x8000@plt
|
||||
|
||||
lis 9, func@ha
|
||||
la 9, func@l(9)
|
Loading…
Reference in New Issue