Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -147,6 +147,8 @@ uint64_t EntryAddr = 0; uint64_t ImageBase; uint64_t MaxPageSize; + uint64_t SdaBase = ~0; + uint64_t Sda2Base = ~0; uint64_t ZStackSize; unsigned LtoPartitions; unsigned LtoO; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -695,6 +695,27 @@ return V; } +// Parses -sda-base options. +static std::pair getSdaBases(opt::InputArgList &Args) { + // If argument not given or invalid, use ~0 value; triggering + // midpoint calculation in the first matching small output section. + auto CheckSdaArg = [](opt::Arg *Arg) -> uint64_t { + if (Arg) { + StringRef S = Arg->getValue(); + uint64_t V; + if (S.getAsInteger(0, V)) + error(Arg->getOption().getPrefixedName() + + ": number expected, but got " + S); + else + return V; + } + return ~0; + }; + + return std::make_pair(CheckSdaArg(Args.getLastArg(OPT_sda_base)), + CheckSdaArg(Args.getLastArg(OPT_sda2_base))); +} + // Do actual linking. Note that when this function is called, // all linker scripts have already been parsed. template void LinkerDriver::link(opt::InputArgList &Args) { @@ -711,6 +732,7 @@ Config->Mips64EL = (Config->EMachine == EM_MIPS && Config->EKind == ELF64LEKind); Config->ImageBase = getImageBase(Args); + std::tie(Config->SdaBase, Config->Sda2Base) = getSdaBases(Args); // Default output filename is "a.out" by the Unix tradition. if (Config->OutputFile.empty()) Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -315,7 +315,7 @@ case R_THUNK_PLT_PC: return Body.getThunkVA() + A - P; case R_PPC_TOC: - return getPPC64TocBase() + A; + return getPPC64TocBase() + A; case R_TLSGD: return In::Got->getGlobalDynOffset(Body) + A - In::Got->getSize(); @@ -431,6 +431,30 @@ if (Body.isUndefined() && !Body.isLocal() && Body.symbol()->isWeak()) return getAArch64Page(A); return getAArch64Page(Body.getVA(A)) - getAArch64Page(P); + case R_PPC_SDA: { + auto CheckReturn = [](int64_t Val, uint32_t Reg) -> typename ELFT::uint { + if (Val > 32767 || Val < -32768) + error("Small data relocation delta exceeds 16-bit value"); + return (Val & 0xffff) | (Reg << 16); + }; + const DefinedRegular *RelSym = dyn_cast>(&Body); + if (RelSym && RelSym->Section) { + const InputSectionBase *Section = RelSym->Section; + if (Section->OutSec) { + // Relocate relative to _SDA_BASE_ or _SDA2_BASE_ synthetic symbols. + const OutputSectionBase *OutSec = RelSym->Section->OutSec; + if (ElfSym::SdaBase && + (OutSec->getName() == ".sdata" || OutSec->getName() == ".sbss")) + return CheckReturn(Body.getVA(A) - + int64_t(ElfSym::SdaBase->Value), 13); + if (ElfSym::Sda2Base && OutSec->getName() == ".sdata2") + return CheckReturn(Body.getVA(A) - + int64_t(ElfSym::Sda2Base->Value), 2); + } + } + error("Unable to perform small data relocation without designated section"); + return 0; + } } llvm_unreachable("Invalid expression"); } Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -164,6 +164,10 @@ def script: S<"script">, HelpText<"Read linker script">; +def sda_base : J<"sda-base=">, HelpText<"Set the small data base address">; + +def sda2_base : J<"sda2-base=">, HelpText<"Set the second small data base address">; + def section_start: S<"section-start">, MetaVarName<"
">, HelpText<"Set address of section">; Index: ELF/Relocations.h =================================================================== --- ELF/Relocations.h +++ ELF/Relocations.h @@ -49,6 +49,7 @@ R_PPC_OPD, R_PPC_PLT_OPD, R_PPC_TOC, + R_PPC_SDA, R_RELAX_GOT_PC, R_RELAX_GOT_PC_NOPIC, R_RELAX_TLS_GD_TO_IE, Index: ELF/Relocations.cpp =================================================================== --- ELF/Relocations.cpp +++ ELF/Relocations.cpp @@ -518,7 +518,7 @@ Addend += File.MipsGp0; } if (Config->Pic && Config->EMachine == EM_PPC64 && Type == R_PPC64_TOC) - Addend += getPPC64TocBase(); + Addend += getPPC64TocBase(); return Addend; } Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -376,6 +376,10 @@ // The content for _gp_disp symbol for MIPS target. static SymbolBody *MipsGpDisp; + + // The content for _SDA_BASE_ and _SDA2_BASE_ symbols. + static DefinedSynthetic *SdaBase; + static DefinedSynthetic *Sda2Base; }; template DefinedRegular *ElfSym::EhdrStart; @@ -386,6 +390,8 @@ template DefinedRegular *ElfSym::End; template DefinedRegular *ElfSym::End2; template SymbolBody *ElfSym::MipsGpDisp; +template DefinedSynthetic *ElfSym::SdaBase; +template DefinedSynthetic *ElfSym::Sda2Base; // A real symbol object, SymbolBody, is usually stored within a Symbol. There's // always one Symbol for each symbol name. The resolver updates the SymbolBody Index: ELF/Target.h =================================================================== --- ELF/Target.h +++ ELF/Target.h @@ -105,6 +105,7 @@ }; StringRef getRelName(uint32_t Type); +template uint64_t getPPC64TocBase(); const unsigned MipsGPOffset = 0x7ff0; Index: ELF/Target.cpp =================================================================== --- ELF/Target.cpp +++ ELF/Target.cpp @@ -49,7 +49,6 @@ TargetInfo *Target; static void or32le(uint8_t *P, int32_t V) { write32le(P, read32le(P) | V); } -static void or32be(uint8_t *P, int32_t V) { write32be(P, read32be(P) | V); } StringRef getRelName(uint32_t Type) { return getELFRelocationTypeName(Config->EMachine, Type); @@ -133,14 +132,14 @@ uint8_t ModRm) const; }; -class PPCTargetInfo final : public TargetInfo { +template class PPCTargetInfo final : public TargetInfo { public: PPCTargetInfo(); void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override; RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override; }; -class PPC64TargetInfo final : public TargetInfo { +template class PPC64TargetInfo final : public TargetInfo { public: PPC64TargetInfo(); RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override; @@ -237,9 +236,23 @@ fatal("unsupported MIPS target"); } case EM_PPC: - return new PPCTargetInfo(); + switch (Config->EKind) { + case ELF32LEKind: + return new PPCTargetInfo(); + case ELF32BEKind: + return new PPCTargetInfo(); + default: + fatal("unsupported PPC target"); + } case EM_PPC64: - return new PPC64TargetInfo(); + switch (Config->EKind) { + case ELF64LEKind: + return new PPC64TargetInfo(); + case ELF64BEKind: + return new PPC64TargetInfo(); + default: + fatal("unsupported PPC target"); + } case EM_X86_64: if (Config->EKind == ELF32LEKind) return new X86_64TargetInfo(); @@ -957,40 +970,73 @@ static uint16_t applyPPCHighest(uint64_t V) { return V >> 48; } static uint16_t applyPPCHighesta(uint64_t V) { return (V + 0x8000) >> 48; } -PPCTargetInfo::PPCTargetInfo() {} +template +PPCTargetInfo::PPCTargetInfo() {} -void PPCTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type, - uint64_t Val) const { +template +void PPCTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type, + uint64_t Val) const { + const endianness E = ELFT::TargetEndianness; switch (Type) { + case R_PPC_ADDR16_HI: + write16(Loc, applyPPCHi(Val)); + break; case R_PPC_ADDR16_HA: - write16be(Loc, applyPPCHa(Val)); + write16(Loc, applyPPCHa(Val)); break; case R_PPC_ADDR16_LO: - write16be(Loc, applyPPCLo(Val)); + write16(Loc, applyPPCLo(Val)); break; case R_PPC_ADDR32: case R_PPC_REL32: - write32be(Loc, Val); + write32(Loc, Val); break; - case R_PPC_REL24: - or32be(Loc, Val & 0x3FFFFFC); + case R_PPC_ADDR24: + case R_PPC_REL24: { + checkAlignment<4>(Val, Type); + uint32_t Inst = read32(Loc) & ~0x3FFFFFC; + Inst |= Val & 0x3FFFFFC; + write32(Loc, Inst); break; + } + case R_PPC_ADDR14: + case R_PPC_REL14: { + checkAlignment<4>(Val, Type); + uint32_t Inst = read32(Loc) & ~0xFFFC; + Inst |= Val & 0xFFFC; + write32(Loc, Inst); + break; + } + case R_PPC_EMB_SDA21: { + // SDA21 relocation entry is offset one byte into instruction + uint8_t *InstLoc = Loc - (E == llvm::support::big ? 1 : 0); + uint32_t Inst = read32(InstLoc) & ~0x1FFFFF; + Inst |= Val & 0x1FFFFF; + write32(InstLoc, Inst); + break; + } default: fatal("unrecognized reloc " + Twine(Type)); } } -RelExpr PPCTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const { +template +RelExpr PPCTargetInfo::getRelExpr(uint32_t Type, + const SymbolBody &S) const { switch (Type) { case R_PPC_REL24: case R_PPC_REL32: + case R_PPC_REL14: return R_PC; + case R_PPC_EMB_SDA21: + return R_PPC_SDA; default: return R_ABS; } } -PPC64TargetInfo::PPC64TargetInfo() { +template +PPC64TargetInfo::PPC64TargetInfo() { PltRel = GotRel = R_PPC64_GLOB_DAT; RelativeRel = R_PPC64_RELATIVE; GotEntrySize = 8; @@ -1015,12 +1061,13 @@ static uint64_t PPC64TocOffset = 0x8000; +template uint64_t getPPC64TocBase() { // The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The // TOC starts where the first of these sections starts. We always create a // .got when we see a relocation that uses it, so for us the start is always // the .got. - uint64_t TocVA = In::Got->getVA(); + uint64_t TocVA = In::Got->getVA(); // Per the ppc64-elf-linux ABI, The TOC base is TOC value plus 0x8000 // thus permitting a full 64 Kbytes segment. Note that the glibc startup @@ -1029,7 +1076,13 @@ return TocVA + PPC64TocOffset; } -RelExpr PPC64TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const { +template uint64_t getPPC64TocBase(); +template uint64_t getPPC64TocBase(); +template uint64_t getPPC64TocBase(); +template uint64_t getPPC64TocBase(); + +template +RelExpr PPC64TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const { switch (Type) { default: return R_ABS; @@ -1047,10 +1100,12 @@ } } -void PPC64TargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr, - uint64_t PltEntryAddr, int32_t Index, - unsigned RelOff) const { - uint64_t Off = GotEntryAddr - getPPC64TocBase(); +template +void PPC64TargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr, + uint64_t PltEntryAddr, int32_t Index, + unsigned RelOff) const { + uint64_t Off = GotEntryAddr - getPPC64TocBase(); + const endianness E = ELFT::TargetEndianness; // FIXME: What we should do, in theory, is get the offset of the function // descriptor in the .opd section, and use that as the offset from %r2 (the @@ -1058,14 +1113,14 @@ // be a pointer to the function descriptor in the .opd section. Using // this scheme is simpler, but requires an extra indirection per PLT dispatch. - write32be(Buf, 0xf8410028); // std %r2, 40(%r1) - write32be(Buf + 4, 0x3d620000 | applyPPCHa(Off)); // addis %r11, %r2, X@ha - write32be(Buf + 8, 0xe98b0000 | applyPPCLo(Off)); // ld %r12, X@l(%r11) - write32be(Buf + 12, 0xe96c0000); // ld %r11,0(%r12) - write32be(Buf + 16, 0x7d6903a6); // mtctr %r11 - write32be(Buf + 20, 0xe84c0008); // ld %r2,8(%r12) - write32be(Buf + 24, 0xe96c0010); // ld %r11,16(%r12) - write32be(Buf + 28, 0x4e800420); // bctr + write32(Buf, 0xf8410028); // std %r2, 40(%r1) + write32(Buf + 4, 0x3d620000 | applyPPCHa(Off)); // addis %r11, %r2, X@ha + write32(Buf + 8, 0xe98b0000 | applyPPCLo(Off)); // ld %r12, X@l(%r11) + write32(Buf + 12, 0xe96c0000); // ld %r11,0(%r12) + write32(Buf + 16, 0x7d6903a6); // mtctr %r11 + write32(Buf + 20, 0xe84c0008); // ld %r2,8(%r12) + write32(Buf + 24, 0xe96c0010); // ld %r11,16(%r12) + write32(Buf + 28, 0x4e800420); // bctr } static std::pair toAddr16Rel(uint32_t Type, uint64_t Val) { @@ -1088,69 +1143,72 @@ } } -void PPC64TargetInfo::relocateOne(uint8_t *Loc, uint32_t Type, - uint64_t Val) const { +template +void PPC64TargetInfo::relocateOne(uint8_t *Loc, uint32_t Type, + uint64_t Val) const { // For a TOC-relative relocation, proceed in terms of the corresponding // ADDR16 relocation type. std::tie(Type, Val) = toAddr16Rel(Type, Val); + const endianness E = ELFT::TargetEndianness; switch (Type) { case R_PPC64_ADDR14: { checkAlignment<4>(Val, Type); // Preserve the AA/LK bits in the branch instruction - uint8_t AALK = Loc[3]; - write16be(Loc + 2, (AALK & 3) | (Val & 0xfffc)); + uint8_t AALK = Loc[E == llvm::support::big ? 3 : 0]; + write16(Loc + (E == llvm::support::big ? 2 : 0), + (AALK & 3) | (Val & 0xfffc)); break; } case R_PPC64_ADDR16: checkInt<16>(Val, Type); - write16be(Loc, Val); + write16(Loc, Val); break; case R_PPC64_ADDR16_DS: checkInt<16>(Val, Type); - write16be(Loc, (read16be(Loc) & 3) | (Val & ~3)); + write16(Loc, (read16(Loc) & 3) | (Val & ~3)); break; case R_PPC64_ADDR16_HA: case R_PPC64_REL16_HA: - write16be(Loc, applyPPCHa(Val)); + write16(Loc, applyPPCHa(Val)); break; case R_PPC64_ADDR16_HI: case R_PPC64_REL16_HI: - write16be(Loc, applyPPCHi(Val)); + write16(Loc, applyPPCHi(Val)); break; case R_PPC64_ADDR16_HIGHER: - write16be(Loc, applyPPCHigher(Val)); + write16(Loc, applyPPCHigher(Val)); break; case R_PPC64_ADDR16_HIGHERA: - write16be(Loc, applyPPCHighera(Val)); + write16(Loc, applyPPCHighera(Val)); break; case R_PPC64_ADDR16_HIGHEST: - write16be(Loc, applyPPCHighest(Val)); + write16(Loc, applyPPCHighest(Val)); break; case R_PPC64_ADDR16_HIGHESTA: - write16be(Loc, applyPPCHighesta(Val)); + write16(Loc, applyPPCHighesta(Val)); break; case R_PPC64_ADDR16_LO: - write16be(Loc, applyPPCLo(Val)); + write16(Loc, applyPPCLo(Val)); break; case R_PPC64_ADDR16_LO_DS: case R_PPC64_REL16_LO: - write16be(Loc, (read16be(Loc) & 3) | (applyPPCLo(Val) & ~3)); + write16(Loc, (read16(Loc) & 3) | (applyPPCLo(Val) & ~3)); break; case R_PPC64_ADDR32: case R_PPC64_REL32: checkInt<32>(Val, Type); - write32be(Loc, Val); + write32(Loc, Val); break; case R_PPC64_ADDR64: case R_PPC64_REL64: case R_PPC64_TOC: - write64be(Loc, Val); + write64(Loc, Val); break; case R_PPC64_REL24: { uint32_t Mask = 0x03FFFFFC; checkInt<24>(Val, Type); - write32be(Loc, (read32be(Loc) & ~Mask) | (Val & Mask)); + write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask)); break; } default: Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -98,7 +98,8 @@ for (StringRef V : {".text.", ".rodata.", ".data.rel.ro.", ".data.", ".bss.", ".init_array.", ".fini_array.", ".ctors.", ".dtors.", ".tbss.", - ".gcc_except_table.", ".tdata.", ".ARM.exidx."}) { + ".sdata.", ".sdata2.", ".sbss.", ".gcc_except_table.", + ".tdata.", ".ARM.exidx."}) { StringRef Prefix = V.drop_back(); if (Name.startswith(V) || Name == Prefix) return Prefix; @@ -626,6 +627,21 @@ addOptionalRegular("__gnu_local_gp", In::MipsGot, MipsGPOffset); } + if (Config->EMachine == EM_PPC && + Config->OSABI == ELF::ELFOSABI_STANDALONE && + !Config->Shared && !Config->Relocatable) { + // In the event a non-relocatable, embedded PPC32 target is being built, + // reserve Small Data base registers. These will be relocated midway through + // the .sdata and .sdata2 sections (if used). This symbol is meant to + // be applied in EABI systems, where r13 and r2 are initialized to these + // linker-generated symbols by the C runtime initialization. + Symbol *Sym; + Sym = Symtab::X->addSynthetic("_SDA_BASE_", nullptr, 0, STV_HIDDEN); + ElfSym::SdaBase = cast>(Sym->body()); + Sym = Symtab::X->addSynthetic("_SDA2_BASE_", nullptr, 0, STV_HIDDEN); + ElfSym::Sda2Base = cast>(Sym->body()); + } + // In the assembly for 32 bit x86 the _GLOBAL_OFFSET_TABLE_ symbol // is magical and is used to produce a R_386_GOTPC relocation. // The R_386_GOTPC relocation value doesn't actually depend on the @@ -1399,6 +1415,36 @@ if (ElfSym::EhdrStart) ElfSym::EhdrStart->Value = Out::ProgramHeaders->Addr; + auto SectionMidpoint = [](OutputSectionBase *Sec) -> + typename ELFT::uint { + return ((Sec->Size / 2) & ~0x3) + Sec->Addr; + }; + + // PPC-EABI systems will need the _SDA_BASE_ and _SDA2_BASE_ symbols + // synthesized for use by C runtime init. The section midpoints are used + // to maximize addressing range. If these values are defined with + // -sda-base/-sda2-base options, those are overridden accordingly. + if (ElfSym::SdaBase) { + if (Config->SdaBase != uint64_t(~0)) + ElfSym::SdaBase->Value = Config->SdaBase; + else { + OutputSectionBase *Sec = findSection(".sdata"); + if (!Sec) + Sec = findSection(".sbss"); + if (Sec) + ElfSym::SdaBase->Value = SectionMidpoint(Sec); + } + } + if (ElfSym::Sda2Base) { + if (Config->Sda2Base != uint64_t(~0)) + ElfSym::Sda2Base->Value = Config->Sda2Base; + else { + OutputSectionBase *Sec = findSection(".sdata2"); + if (Sec) + ElfSym::Sda2Base->Value = SectionMidpoint(Sec); + } + } + auto Set = [](DefinedRegular *S1, DefinedRegular *S2, uintX_t V) { if (S1) S1->Value = V; Index: test/ELF/ppc-eabi-relocs.s =================================================================== --- /dev/null +++ test/ELF/ppc-eabi-relocs.s @@ -0,0 +1,42 @@ +# RUN: llvm-mc -filetype=obj -triple=powerpc-unknown-unknown-eabi %s -o %t +# RUN: ld.lld %t -o %t2 +# RUN: llvm-objdump -disassemble-all %t2 | FileCheck %s +# REQUIRES: ppc + +.sdata2 +smallstr2: + .long 0xDEADD00D + .long 0xABCDEFAB + +# CHECK: Disassembly of section .sdata2: +# CHECK: smallstr2: +# CHECK: 100d4: de ad d0 0d stfdu 21, -12275(13) +# CHECK: 100d8: ab cd ef ab lha 30, -4181(13) + +.section .R_PPC_EMB_SDA21,"ax",@progbits + lis 13, _SDA_BASE_@ha + ori 13, 13, _SDA_BASE_@l + lis 2, _SDA2_BASE_@ha + ori 2, 2, _SDA2_BASE_@l + lwz 4, smallstr@sdarx(0) + lwz 5, smallstr2@sdarx(0) + +# CHECK: Disassembly of section .R_PPC_EMB_SDA21: +# CHECK: .R_PPC_EMB_SDA21: +# CHECK: 11000: 3d a0 00 01 lis 13, 1 +# CHECK: 11004: 61 ad 20 04 ori 13, 13, 8196 +# CHECK: 11008: 3c 40 00 01 lis 2, 1 +# CHECK: 1100c: 60 42 00 d8 ori 2, 2, 216 +# CHECK: 11010: 80 8d 00 00 lwz 4, 0(13) +# CHECK: 11014: 80 a2 ff fc lwz 5, -4(2) + +.sdata + .long 0xABCDEFAB +smallstr: + .long 0xDEADBEEF + +# CHECK: Disassembly of section .sdata: +# CHECK: .sdata: +# CHECK: 12000: ab cd ef ab lha 30, -4181(13) +# CHECK: smallstr: +# CHECK: 12004: de ad be ef stfdu 21, -16657(13) Index: test/ELF/ppc-relocs.s =================================================================== --- test/ELF/ppc-relocs.s +++ test/ELF/ppc-relocs.s @@ -1,6 +1,6 @@ # RUN: llvm-mc -filetype=obj -triple=powerpc-unknown-freebsd %s -o %t # RUN: ld.lld %t -o %t2 -# RUN: llvm-objdump -d %t2 | FileCheck %s +# RUN: llvm-objdump -disassemble-all %t2 | FileCheck %s # REQUIRES: ppc .section .R_PPC_ADDR16_HA,"ax",@progbits @@ -41,6 +41,17 @@ # CHECK: .FR_PPC_REL24: # CHECK: 11014: 48 00 00 04 b .+4 +.section .R_PPC_ADDR32,"ax",@progbits +.globl .FR_PPC_ADDR32 +.FR_PPC_ADDR32: + .long .Lfoox2 +.section .R_PPC_ADDR32_2,"ax",@progbits +.Lfoox2: + +# CHECK: Disassembly of section .R_PPC_ADDR32: +# CHECK: .FR_PPC_ADDR32: +# CHECK: 11018: 00 01 10 1c + .section .R_PPC_REL32,"ax",@progbits .globl .FR_PPC_REL32 .FR_PPC_REL32: @@ -50,15 +61,26 @@ # CHECK: Disassembly of section .R_PPC_REL32: # CHECK: .FR_PPC_REL32: -# CHECK: 11018: 00 00 00 04 +# CHECK: 1101c: 00 00 00 04 -.section .R_PPC_ADDR32,"ax",@progbits -.globl .FR_PPC_ADDR32 -.FR_PPC_ADDR32: - .long .Lfoox2 -.section .R_PPC_ADDR32_2,"ax",@progbits -.Lfoox2: +.section .R_PPC_ADDR14,"ax",@progbits +.globl .FR_PPC_ADDR14 +.FR_PPC_ADDR14: + beqa .Lfoox4 +.section .R_PPC_ADDR14_2,"ax",@progbits +.Lfoox4: -# CHECK: Disassembly of section .R_PPC_ADDR32: -# CHECK: .FR_PPC_ADDR32: -# CHECK: 1101c: 00 01 10 20 +# CHECK: Disassembly of section .R_PPC_ADDR14: +# CHECK: .FR_PPC_ADDR14: +# CHECK: 11020: 41 82 10 26 bta 2, 4132 + +.section .R_PPC_REL14,"ax",@progbits +.globl .FR_PPC_REL14 +.FR_PPC_REL14: + beq .Lfoox5 +.section .R_PPC_REL14_2,"ax",@progbits +.Lfoox5: + +# CHECK: Disassembly of section .R_PPC_REL14: +# CHECK: .FR_PPC_REL14: +# CHECK: 11024: 41 82 00 04 bt 2, .+4