Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -584,7 +584,18 @@ if (InOpd) SymVA = read64be(&Out::OpdBuf[SymVA - OpdStart]); } - return SymVA - P; + + // PPC64 V2 ABI describes two entry points to a function. The global entry + // point sets up the TOC base pointer. When calling a local function, the + // call should branch to the local entry point rather than the global entry + // point. Section 3.4.1 describes using the 3 most significant bits of the + // st_other field to find out how many instructions there are between the + // local and global entry point. + uint8_t StOther = (Sym.StOther >> 5) & 7; + if (StOther == 0 || StOther == 1) + return SymVA - P; + + return SymVA - P + (1 << StOther); } case R_PPC_TOC: return getPPC64TocBase() + A; Index: test/ELF/Inputs/ppc64-func-global-entry.s =================================================================== --- test/ELF/Inputs/ppc64-func-global-entry.s +++ test/ELF/Inputs/ppc64-func-global-entry.s @@ -0,0 +1,35 @@ + .text + .abiversion 2 + .globl foo_external_diff # -- Begin function foo_external_diff + .p2align 4 + .type foo_external_diff,@function +foo_external_diff: # @foo_external_diff +.Lfunc_begin0: +.Lfunc_gep0: + addis 2, 12, .TOC.-.Lfunc_gep0@ha + addi 2, 2, .TOC.-.Lfunc_gep0@l +.Lfunc_lep0: + .localentry foo_external_diff, .Lfunc_lep0-.Lfunc_gep0 +# %bb.0: # %entry + addis 5, 2, .LC0@toc@ha + add 3, 4, 3 + ld 5, .LC0@toc@l(5) + lwz 5, 0(5) + add 3, 3, 5 + extsw 3, 3 + blr + .long 0 + .quad 0 +.Lfunc_end0: + .size foo_external_diff, .Lfunc_end0-.Lfunc_begin0 + # -- End function + .section .toc,"aw",@progbits +.LC0: + .tc glob2[TC],glob2 + .type glob2,@object # @glob2 + .data + .globl glob2 + .p2align 2 +glob2: + .long 10 # 0xa + .size glob2, 4 Index: test/ELF/Inputs/ppc64-func-local-entry.s =================================================================== --- test/ELF/Inputs/ppc64-func-local-entry.s +++ test/ELF/Inputs/ppc64-func-local-entry.s @@ -0,0 +1,16 @@ + .text + .abiversion 2 + .globl foo_external_same # -- Begin function foo_external_same + .p2align 4 + .type foo_external_same,@function +foo_external_same: # @foo_external_same +.Lfunc_begin0: +# %bb.0: # %entry + add 3, 4, 3 + extsw 3, 3 + blr + .long 0 + .quad 0 +.Lfunc_end0: + .size foo_external_same, .Lfunc_end0-.Lfunc_begin0 + # -- End function Index: test/ELF/ppc64-func-entry-points.s =================================================================== --- test/ELF/ppc64-func-entry-points.s +++ test/ELF/ppc64-func-entry-points.s @@ -0,0 +1,72 @@ +// REQUIRES: ppc +// RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %s -o %t.o +// RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %p/Inputs/ppc64-func-global-entry.s -o %t2.o +// RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %p/Inputs/ppc64-func-local-entry.s -o %t3.o +// RUN: ld.lld -dynamic-linker /lib64/ld64.so.2 %t.o %t2.o %t3.o -o %t +// RUN: llvm-objdump -d %t | FileCheck %s + .text + .abiversion 2 + .globl _start # -- Begin function _start + .p2align 4 + .type _start,@function +_start: # @_start +.Lfunc_begin0: +.Lfunc_gep0: + addis 2, 12, .TOC.-.Lfunc_gep0@ha + addi 2, 2, .TOC.-.Lfunc_gep0@l +.Lfunc_lep0: + .localentry _start, .Lfunc_lep0-.Lfunc_gep0 +# %bb.0: # %entry + mflr 0 + std 0, 16(1) + stdu 1, -48(1) + li 3, 1 + li 4, 1 + std 30, 32(1) # 8-byte Folded Spill + bl foo_external_same + nop + mr 30, 3 + li 3, 2 + li 4, 2 + bl foo_external_diff + nop + addis 4, 2, .LC0@toc@ha + add 3, 3, 30 + ld 30, 32(1) # 8-byte Folded Reload + ld 4, .LC0@toc@l(4) + lwz 4, 0(4) + add 3, 3, 4 + extsw 3, 3 + addi 1, 1, 48 + ld 0, 16(1) + li 0, 1 + sc + .long 0 + .quad 0 +.Lfunc_end0: + .size _start, .Lfunc_end0-.Lfunc_begin0 + # -- End function + .section .toc,"aw",@progbits +.LC0: + .tc glob[TC],glob + .type glob,@object # @glob + .data + .globl glob + .p2align 2 +glob: + .long 10 # 0xa + .size glob, 4 + +# Check that foo_external_diff has a global entry point and we branch to +# foo_external_diff+8. Also check that foo_external_same has no global entry +# point and we branch to start of foo_external_same. + +// CHECK: _start: +// CHECK: 10010020: 91 00 00 48 bl .+144 +// CHECK: 10010034: 55 00 00 48 bl .+84 +// CHECK: foo_external_diff: +// CHECK-NEXT: 10010080: 02 00 4c 3c addis 2, 12, 2 +// CHECK-NEXT: 10010084: 80 7f 42 38 addi 2, 2, 32640 +// CHECK-NEXT: 10010088: ff ff a2 3c addis 5, 2, -1 +// CHECK: foo_external_same: +// CHECK-NEXT: 100100b0: 14 1a 64 7c add 3, 4, 3