forked from OSchip/llvm-project
[PowerPC] Support PCRelative Callees for R_PPC64_REL24 Relocation
The R_PPC64_REL24 is used in function calls when the caller requires a valid TOC pointer. If the callee shares the same TOC or does not clobber the TOC pointer then a direct call can be made. If the callee does not share the TOC a thunk must be added to save the TOC pointer for the caller. Up until PC Relative was introduced all local calls on medium and large code models were assumed to share a TOC. This is no longer the case because if the caller requires a TOC and the callee is PC Relative then the callee can clobber the TOC even if it is in the same DSO. This patch is to add support for a TOC caller calling a PC Relative callee that clobbers the TOC. Reviewed By: sfertile, MaskRay Differential Revision: https://reviews.llvm.org/D82950
This commit is contained in:
parent
5d075beae7
commit
beb52b12cb
|
@ -1039,6 +1039,11 @@ bool PPC64::needsThunk(RelExpr expr, RelType type, const InputFile *file,
|
|||
if (s.isInPlt())
|
||||
return true;
|
||||
|
||||
// This check looks at the st_other bits of the callee. If the value is 1
|
||||
// then the callee clobbers the TOC and we need an R2 save stub.
|
||||
if ((s.stOther >> 5) == 1)
|
||||
return true;
|
||||
|
||||
// If a symbol is a weak undefined and we are compiling an executable
|
||||
// it doesn't need a range-extending thunk since it can't be called.
|
||||
if (s.isUndefWeak() && !config->shared)
|
||||
|
|
|
@ -279,6 +279,20 @@ public:
|
|||
void addSymbols(ThunkSection &isec) override;
|
||||
};
|
||||
|
||||
// PPC64 R2 Save Stub
|
||||
// When the caller requires a valid R2 TOC pointer but the callee does not
|
||||
// require a TOC pointer and the callee cannot guarantee that it doesn't
|
||||
// clobber R2 then we need to save R2. This stub:
|
||||
// 1) Saves the TOC pointer to the stack.
|
||||
// 2) Tail calls the callee.
|
||||
class PPC64R2SaveStub final : public Thunk {
|
||||
public:
|
||||
PPC64R2SaveStub(Symbol &dest) : Thunk(dest, 0) {}
|
||||
uint32_t size() override { return 8; }
|
||||
void writeTo(uint8_t *buf) override;
|
||||
void addSymbols(ThunkSection &isec) override;
|
||||
};
|
||||
|
||||
// A bl instruction uses a signed 24 bit offset, with an implicit 4 byte
|
||||
// alignment. This gives a possible 26 bits of 'reach'. If the call offset is
|
||||
// larger then that we need to emit a long-branch thunk. The target address
|
||||
|
@ -822,6 +836,21 @@ void PPC64PltCallStub::addSymbols(ThunkSection &isec) {
|
|||
s->file = destination.file;
|
||||
}
|
||||
|
||||
void PPC64R2SaveStub::writeTo(uint8_t *buf) {
|
||||
int64_t offset = destination.getVA() - (getThunkTargetSym()->getVA() + 4);
|
||||
// The branch offset needs to fit in 26 bits.
|
||||
if (!isInt<26>(offset))
|
||||
fatal("R2 save stub branch offset is too large: " + Twine(offset));
|
||||
write32(buf + 0, 0xf8410018); // std r2,24(r1)
|
||||
write32(buf + 4, 0x48000000 | (offset & 0x03fffffc)); // b <offset>
|
||||
}
|
||||
|
||||
void PPC64R2SaveStub::addSymbols(ThunkSection &isec) {
|
||||
Defined *s = addSymbol(saver.save("__toc_save_" + destination.getName()),
|
||||
STT_FUNC, 0, isec);
|
||||
s->needsTocRestore = true;
|
||||
}
|
||||
|
||||
void PPC64LongBranchThunk::writeTo(uint8_t *buf) {
|
||||
int64_t offset = in.ppc64LongBranchTarget->getEntryVA(&destination, addend) -
|
||||
getPPC64TocBase();
|
||||
|
@ -950,6 +979,11 @@ static Thunk *addThunkPPC64(RelType type, Symbol &s, int64_t a) {
|
|||
if (s.isInPlt())
|
||||
return make<PPC64PltCallStub>(s);
|
||||
|
||||
// This check looks at the st_other bits of the callee. If the value is 1
|
||||
// then the callee clobbers the TOC and we need an R2 save stub.
|
||||
if ((s.stOther >> 5) == 1)
|
||||
return make<PPC64R2SaveStub>(s);
|
||||
|
||||
if (config->picThunk)
|
||||
return make<PPC64PILongBranchThunk>(s, a);
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
# RUN: llvm-mc -filetype=obj -triple=powerpc64le %s -o %t.o
|
||||
# RUN: not ld.lld %t.o -o /dev/null 2>&1 | FileCheck %s
|
||||
|
||||
# RUN: llvm-mc -filetype=obj -triple=powerpc64 %s -o %t.o
|
||||
# RUN: not ld.lld %t.o -o /dev/null 2>&1 | FileCheck %s
|
||||
|
||||
## This test checks that the linker produces errors when it is missing the nop
|
||||
## after a local call to a callee with st_other=1.
|
||||
|
||||
# CHECK: (.text+0xC): call to save_callee lacks nop, can't restore toc
|
||||
# CHECK: (.text+0x1C): call to save_callee lacks nop, can't restore toc
|
||||
|
||||
callee:
|
||||
.localentry callee, 1
|
||||
blr # 0x0
|
||||
|
||||
caller:
|
||||
.Lfunc_gep1:
|
||||
addis 2, 12, .TOC.-.Lfunc_gep1@ha
|
||||
addi 2, 2, .TOC.-.Lfunc_gep1@l
|
||||
.Lfunc_lep1:
|
||||
.localentry caller, .Lfunc_lep1-.Lfunc_gep1
|
||||
bl callee # 0xC
|
||||
blr
|
||||
|
||||
caller_tail:
|
||||
.Lfunc_gep2:
|
||||
addis 2, 12, .TOC.-.Lfunc_gep2@ha
|
||||
addi 2, 2, .TOC.-.Lfunc_gep2@l
|
||||
.Lfunc_lep2:
|
||||
.localentry caller_tail, .Lfunc_lep2-.Lfunc_gep2
|
||||
b callee # 0x1C
|
|
@ -0,0 +1,33 @@
|
|||
# REQUIRES: ppc
|
||||
# RUN: echo 'SECTIONS { \
|
||||
# RUN: .text_callee 0x10010000 : { *(.text_callee) } \
|
||||
# RUN: .text_caller 0x20020000 : { *(.text_caller) } \
|
||||
# RUN: }' > %t.script
|
||||
|
||||
# RUN: llvm-mc -filetype=obj -triple=powerpc64le %s -o %t.o
|
||||
# RUN: not ld.lld -T %t.script %t.o -o %t 2>&1 >/dev/null | FileCheck %s
|
||||
|
||||
# RUN: llvm-mc -filetype=obj -triple=powerpc64 %s -o %t.o
|
||||
# RUN: not ld.lld -T %t.script %t.o -o %t 2>&1 >/dev/null | FileCheck %s
|
||||
|
||||
# CHECK: error: R2 save stub branch offset is too large: -268501028
|
||||
|
||||
.section .text_callee, "ax", %progbits
|
||||
callee:
|
||||
.localentry callee, 1
|
||||
blr
|
||||
|
||||
.section .text_caller, "ax", %progbits
|
||||
caller:
|
||||
.Lfunc_gep1:
|
||||
addis 2, 12, .TOC.-.Lfunc_gep1@ha
|
||||
addi 2, 2, .TOC.-.Lfunc_gep1@l
|
||||
.Lfunc_lep1:
|
||||
.localentry caller, .Lfunc_lep1-.Lfunc_gep1
|
||||
addis 30, 2, global@toc@ha
|
||||
lwz 3, global@toc@l(30)
|
||||
bl callee
|
||||
nop
|
||||
blr
|
||||
global:
|
||||
.long 0
|
|
@ -0,0 +1,74 @@
|
|||
# REQUIRES: ppc
|
||||
# RUN: echo 'SECTIONS { \
|
||||
# RUN: .text_callee 0x10010000 : { *(.text_callee) } \
|
||||
# RUN: .text_caller 0x10020000 : { *(.text_caller) } \
|
||||
# RUN: }' > %t.script
|
||||
|
||||
# RUN: llvm-mc -filetype=obj -triple=powerpc64le %s -o %t.o
|
||||
# RUN: ld.lld -T %t.script %t.o -o %t
|
||||
# RUN: llvm-readelf -s %t | FileCheck %s --check-prefix=SYMBOL
|
||||
# RUN: llvm-objdump -d --no-show-raw-insn --mcpu=future %t | FileCheck %s
|
||||
|
||||
# RUN: llvm-mc -filetype=obj -triple=powerpc64 %s -o %t.o
|
||||
# RUN: ld.lld -T %t.script %t.o -o %t
|
||||
# RUN: llvm-readelf -s %t | FileCheck %s --check-prefix=SYMBOL
|
||||
# RUN: llvm-objdump -d --no-show-raw-insn --mcpu=future %t | FileCheck %s
|
||||
|
||||
# The point of this test is to make sure that when a function with TOC access
|
||||
# a local function with st_other=1, a TOC save stub is inserted.
|
||||
|
||||
# SYMBOL: Symbol table '.symtab' contains 7 entries
|
||||
# SYMBOL: 10010000 0 NOTYPE LOCAL DEFAULT [<other: 0x20>] 1 callee
|
||||
# SYMBOL: 10020000 0 NOTYPE LOCAL DEFAULT [<other: 0x60>] 2 caller
|
||||
# SYMBOL: 10020020 0 NOTYPE LOCAL DEFAULT [<other: 0x60>] 2 caller_14
|
||||
# SYMBOL: 1002003c 8 FUNC LOCAL DEFAULT 2 __toc_save_callee
|
||||
|
||||
# CHECK-LABEL: callee
|
||||
# CHECK: blr
|
||||
|
||||
# CHECK-LABEL: caller
|
||||
# CHECK: bl 0x1002003c
|
||||
# CHECK-NEXT: ld 2, 24(1)
|
||||
# CHECK-NEXT: blr
|
||||
|
||||
# CHECK-LABEL: caller_14
|
||||
# CHECK: bfl 0, 0x1002003c
|
||||
# CHECK-NEXT: ld 2, 24(1)
|
||||
# CHECK-NEXT: blr
|
||||
|
||||
# CHECK-LABEL: __toc_save_callee
|
||||
# CHECK-NEXT: std 2, 24(1)
|
||||
# CHECK-NEXT: b 0x10010000
|
||||
|
||||
|
||||
.section .text_callee, "ax", %progbits
|
||||
callee:
|
||||
.localentry callee, 1
|
||||
blr
|
||||
|
||||
.section .text_caller, "ax", %progbits
|
||||
caller:
|
||||
.Lfunc_gep1:
|
||||
addis 2, 12, .TOC.-.Lfunc_gep1@ha
|
||||
addi 2, 2, .TOC.-.Lfunc_gep1@l
|
||||
.Lfunc_lep1:
|
||||
.localentry caller, .Lfunc_lep1-.Lfunc_gep1
|
||||
addis 30, 2, global@toc@ha
|
||||
lwz 3, global@toc@l(30)
|
||||
bl callee
|
||||
nop
|
||||
blr
|
||||
global:
|
||||
.long 0
|
||||
|
||||
caller_14:
|
||||
.Lfunc_gep2:
|
||||
addis 2, 12, .TOC.-.Lfunc_gep1@ha
|
||||
addi 2, 2, .TOC.-.Lfunc_gep1@l
|
||||
.Lfunc_lep2:
|
||||
.localentry caller_14, .Lfunc_lep2-.Lfunc_gep2
|
||||
addis 30, 2, global@toc@ha
|
||||
lwz 3, global@toc@l(30)
|
||||
bcl 4, 0, callee
|
||||
nop
|
||||
blr
|
Loading…
Reference in New Issue