Index: ELF/Arch/PPC64.cpp =================================================================== --- ELF/Arch/PPC64.cpp +++ ELF/Arch/PPC64.cpp @@ -74,6 +74,32 @@ return TocVA + PPC64TocOffset; } + +unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t StOther) { + // The offset is encoded into the 3 most significant bits of the st_other + // field, with some special values described in section 3.4.1 of the ABI: + // 0 --> Zero offset between the GEP and LEP, and the function does NOT use + // the TOC pointer (r2). r2 will hold the same value on returning from + // the function as it did on entering the function. + // 1 --> Zero offset between the GEP and LEP, and r2 should be treated as a + // caller-saved register for all callers. + // 2-6 --> The binary logarithm of the offset eg: + // 2 --> 2^2 = 4 bytes --> 1 instruction. + // 6 --> 2^6 = 64 bytes --> 16 instructions. + // 7 --> Reserved. + uint8_t GepToLep = (StOther >> 5) & 7; + if (GepToLep < 2) + return 0; + + // The value encoded in the st_other bits is the + // log-base-2(offset). + if (GepToLep < 7) + return 1 << GepToLep; + + error("reserved value of 7 in the 3 most-significant-bits of st_other"); + return 0; +} + namespace { class PPC64 final : public TargetInfo { public: Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -631,16 +631,12 @@ return 0; // 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 + (1LL << StOther); + // point is used for calls where the caller and callee (may) have different + // TOC base pointers and r2 needs to be modified to hold the TOC base for + // the callee. For local calls the caller and callee share the same + // TOC base and so the TOC pointer initialization code should be skipped by + // branching to the local entry point. + return SymVA - P + getPPC64GlobalEntryToLocalEntryOffset(Sym.StOther); } case R_PPC_TOC: return getPPC64TocBase() + A; Index: ELF/Target.h =================================================================== --- ELF/Target.h +++ ELF/Target.h @@ -171,6 +171,15 @@ return getErrorPlace(Loc).Loc; } +// In the PowerPC64 Elf V2 abi a function can have 2 entry points. The first is +// a global entry point (GEP) which typically is used to intiailzie the TOC +// pointer in general purpose register 2. The second is a local entry +// point (LEP) which bypasses the TOC pointer initialization code. The +// offset between GEP and LEP is encoded in a function's st_other flags. +// This function will return the offset (in bytes) from the global entry-point +// to the local entry-point. +unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t StOther); + uint64_t getPPC64TocBase(); uint64_t getAArch64Page(uint64_t Expr);