| //===-- PPCELFObjectWriter.cpp - PPC ELF Writer ---------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "MCTargetDesc/PPCFixupKinds.h" | |
| #include "MCTargetDesc/PPCMCAsmInfo.h" | |
| #include "MCTargetDesc/PPCMCTargetDesc.h" | |
| #include "llvm/MC/MCContext.h" | |
| #include "llvm/MC/MCELFObjectWriter.h" | |
| #include "llvm/MC/MCExpr.h" | |
| #include "llvm/MC/MCObjectWriter.h" | |
| #include "llvm/MC/MCSymbolELF.h" | |
| #include "llvm/MC/MCValue.h" | |
| #include "llvm/Support/ErrorHandling.h" | |
| using namespace llvm; | |
| namespace { | |
| class PPCELFObjectWriter : public MCELFObjectTargetWriter { | |
| public: | |
| PPCELFObjectWriter(bool Is64Bit, uint8_t OSABI); | |
| protected: | |
| unsigned getRelocType(const MCFixup &Fixup, const MCValue &Target, | |
| bool IsPCRel) const override; | |
| bool needsRelocateWithSymbol(const MCValue &, unsigned Type) const override; | |
| }; | |
| } | |
| PPCELFObjectWriter::PPCELFObjectWriter(bool Is64Bit, uint8_t OSABI) | |
| : MCELFObjectTargetWriter(Is64Bit, OSABI, | |
| Is64Bit ? ELF::EM_PPC64 : ELF::EM_PPC, | |
| /*HasRelocationAddend*/ true) {} | |
| unsigned PPCELFObjectWriter::getRelocType(const MCFixup &Fixup, | |
| const MCValue &Target, | |
| bool IsPCRel) const { | |
| SMLoc Loc = Fixup.getValue()->getLoc(); | |
| auto Spec = static_cast<PPCMCExpr::Specifier>(Target.getSpecifier()); | |
| switch (Spec) { | |
| case PPC::S_DTPMOD: | |
| case PPC::S_DTPREL: | |
| case PPC::S_DTPREL_HA: | |
| case PPC::S_DTPREL_HI: | |
| case PPC::S_DTPREL_HIGH: | |
| case PPC::S_DTPREL_HIGHA: | |
| case PPC::S_DTPREL_HIGHER: | |
| case PPC::S_DTPREL_HIGHERA: | |
| case PPC::S_DTPREL_HIGHEST: | |
| case PPC::S_DTPREL_HIGHESTA: | |
| case PPC::S_DTPREL_LO: | |
| case PPC::S_GOT_DTPREL: | |
| case PPC::S_GOT_DTPREL_HA: | |
| case PPC::S_GOT_DTPREL_HI: | |
| case PPC::S_GOT_DTPREL_LO: | |
| case PPC::S_GOT_TLSGD: | |
| case PPC::S_GOT_TLSGD_HA: | |
| case PPC::S_GOT_TLSGD_HI: | |
| case PPC::S_GOT_TLSGD_LO: | |
| case PPC::S_GOT_TLSGD_PCREL: | |
| case PPC::S_GOT_TLSLD: | |
| case PPC::S_GOT_TLSLD_HA: | |
| case PPC::S_GOT_TLSLD_HI: | |
| case PPC::S_GOT_TLSLD_LO: | |
| case PPC::S_GOT_TPREL: | |
| case PPC::S_GOT_TPREL_HA: | |
| case PPC::S_GOT_TPREL_HI: | |
| case PPC::S_GOT_TPREL_LO: | |
| case PPC::S_GOT_TPREL_PCREL: | |
| case PPC::S_TLS: | |
| case PPC::S_TLSGD: | |
| case PPC::S_TLSLD: | |
| case PPC::S_TLS_PCREL: | |
| case PPC::S_TPREL: | |
| case PPC::S_TPREL_HA: | |
| case PPC::S_TPREL_HI: | |
| case PPC::S_TPREL_HIGH: | |
| case PPC::S_TPREL_HIGHA: | |
| case PPC::S_TPREL_HIGHER: | |
| case PPC::S_TPREL_HIGHERA: | |
| case PPC::S_TPREL_HIGHEST: | |
| case PPC::S_TPREL_HIGHESTA: | |
| case PPC::S_TPREL_LO: | |
| if (auto *SA = const_cast<MCSymbol *>(Target.getAddSym())) | |
| static_cast<MCSymbolELF *>(SA)->setType(ELF::STT_TLS); | |
| break; | |
| default: | |
| break; | |
| } | |
| // determine the type of the relocation | |
| unsigned Type = 0; | |
| if (IsPCRel) { | |
| switch (Fixup.getKind()) { | |
| default: | |
| llvm_unreachable("Unimplemented"); | |
| case PPC::fixup_ppc_br24: | |
| case PPC::fixup_ppc_br24abs: | |
| case PPC::fixup_ppc_br24_notoc: | |
| switch (Spec) { | |
| default: | |
| reportError(Loc, "unsupported relocation type"); | |
| break; | |
| case PPC::S_None: | |
| Type = ELF::R_PPC_REL24; | |
| break; | |
| case PPC::S_PLT: | |
| Type = ELF::R_PPC_PLTREL24; | |
| break; | |
| case PPC::S_LOCAL: | |
| Type = ELF::R_PPC_LOCAL24PC; | |
| break; | |
| case PPC::S_NOTOC: | |
| Type = ELF::R_PPC64_REL24_NOTOC; | |
| break; | |
| } | |
| break; | |
| case PPC::fixup_ppc_brcond14: | |
| case PPC::fixup_ppc_brcond14abs: | |
| Type = ELF::R_PPC_REL14; | |
| break; | |
| case PPC::fixup_ppc_half16: | |
| switch (Spec) { | |
| default: | |
| reportError(Loc, "unsupported relocation type"); | |
| return ELF::R_PPC_NONE; | |
| case PPC::S_None: | |
| return ELF::R_PPC_REL16; | |
| case PPC::S_LO: | |
| return ELF::R_PPC_REL16_LO; | |
| case PPC::S_HI: | |
| return ELF::R_PPC_REL16_HI; | |
| case PPC::S_HA: | |
| return ELF::R_PPC_REL16_HA; | |
| } | |
| break; | |
| case PPC::fixup_ppc_half16ds: | |
| case PPC::fixup_ppc_half16dq: | |
| reportError(Loc, "unsupported relocation type"); | |
| break; | |
| case PPC::fixup_ppc_pcrel34: | |
| switch (Spec) { | |
| default: | |
| reportError(Loc, "unsupported relocation type"); | |
| break; | |
| case PPC::S_PCREL: | |
| Type = ELF::R_PPC64_PCREL34; | |
| break; | |
| case PPC::S_GOT_PCREL: | |
| Type = ELF::R_PPC64_GOT_PCREL34; | |
| break; | |
| case PPC::S_GOT_TLSGD_PCREL: | |
| Type = ELF::R_PPC64_GOT_TLSGD_PCREL34; | |
| break; | |
| case PPC::S_GOT_TLSLD_PCREL: | |
| Type = ELF::R_PPC64_GOT_TLSLD_PCREL34; | |
| break; | |
| case PPC::S_GOT_TPREL_PCREL: | |
| Type = ELF::R_PPC64_GOT_TPREL_PCREL34; | |
| break; | |
| } | |
| break; | |
| case FK_Data_4: | |
| Type = ELF::R_PPC_REL32; | |
| break; | |
| case FK_Data_8: | |
| Type = ELF::R_PPC64_REL64; | |
| break; | |
| } | |
| } else { | |
| switch (Fixup.getKind()) { | |
| default: | |
| llvm_unreachable("invalid fixup kind!"); | |
| case PPC::fixup_ppc_br24abs: | |
| Type = ELF::R_PPC_ADDR24; | |
| break; | |
| case PPC::fixup_ppc_brcond14abs: | |
| Type = ELF::R_PPC_ADDR14; // XXX: or BRNTAKEN?_ | |
| break; | |
| case PPC::fixup_ppc_half16: | |
| switch (Spec) { | |
| default: | |
| reportError(Loc, "unsupported relocation type"); | |
| break; | |
| case PPC::S_LO: | |
| return ELF::R_PPC_ADDR16_LO; | |
| case PPC::S_HI: | |
| return ELF::R_PPC_ADDR16_HI; | |
| case PPC::S_HA: | |
| return ELF::R_PPC_ADDR16_HA; | |
| case PPC::S_HIGH: | |
| return ELF::R_PPC64_ADDR16_HIGH; | |
| case PPC::S_HIGHA: | |
| return ELF::R_PPC64_ADDR16_HIGHA; | |
| case PPC::S_HIGHER: | |
| return ELF::R_PPC64_ADDR16_HIGHER; | |
| case PPC::S_HIGHERA: | |
| return ELF::R_PPC64_ADDR16_HIGHERA; | |
| case PPC::S_HIGHEST: | |
| return ELF::R_PPC64_ADDR16_HIGHEST; | |
| case PPC::S_HIGHESTA: | |
| return ELF::R_PPC64_ADDR16_HIGHESTA; | |
| case PPC::S_None: | |
| Type = ELF::R_PPC_ADDR16; | |
| break; | |
| case PPC::S_GOT: | |
| Type = ELF::R_PPC_GOT16; | |
| break; | |
| case PPC::S_GOT_LO: | |
| Type = ELF::R_PPC_GOT16_LO; | |
| break; | |
| case PPC::S_GOT_HI: | |
| Type = ELF::R_PPC_GOT16_HI; | |
| break; | |
| case PPC::S_GOT_HA: | |
| Type = ELF::R_PPC_GOT16_HA; | |
| break; | |
| case PPC::S_TOC: | |
| Type = ELF::R_PPC64_TOC16; | |
| break; | |
| case PPC::S_TOC_LO: | |
| Type = ELF::R_PPC64_TOC16_LO; | |
| break; | |
| case PPC::S_TOC_HI: | |
| Type = ELF::R_PPC64_TOC16_HI; | |
| break; | |
| case PPC::S_TOC_HA: | |
| Type = ELF::R_PPC64_TOC16_HA; | |
| break; | |
| case PPC::S_TPREL: | |
| Type = ELF::R_PPC_TPREL16; | |
| break; | |
| case PPC::S_TPREL_LO: | |
| Type = ELF::R_PPC_TPREL16_LO; | |
| break; | |
| case PPC::S_TPREL_HI: | |
| Type = ELF::R_PPC_TPREL16_HI; | |
| break; | |
| case PPC::S_TPREL_HA: | |
| Type = ELF::R_PPC_TPREL16_HA; | |
| break; | |
| case PPC::S_TPREL_HIGH: | |
| Type = ELF::R_PPC64_TPREL16_HIGH; | |
| break; | |
| case PPC::S_TPREL_HIGHA: | |
| Type = ELF::R_PPC64_TPREL16_HIGHA; | |
| break; | |
| case PPC::S_TPREL_HIGHER: | |
| Type = ELF::R_PPC64_TPREL16_HIGHER; | |
| break; | |
| case PPC::S_TPREL_HIGHERA: | |
| Type = ELF::R_PPC64_TPREL16_HIGHERA; | |
| break; | |
| case PPC::S_TPREL_HIGHEST: | |
| Type = ELF::R_PPC64_TPREL16_HIGHEST; | |
| break; | |
| case PPC::S_TPREL_HIGHESTA: | |
| Type = ELF::R_PPC64_TPREL16_HIGHESTA; | |
| break; | |
| case PPC::S_DTPREL: | |
| Type = ELF::R_PPC64_DTPREL16; | |
| break; | |
| case PPC::S_DTPREL_LO: | |
| Type = ELF::R_PPC64_DTPREL16_LO; | |
| break; | |
| case PPC::S_DTPREL_HI: | |
| Type = ELF::R_PPC64_DTPREL16_HI; | |
| break; | |
| case PPC::S_DTPREL_HA: | |
| Type = ELF::R_PPC64_DTPREL16_HA; | |
| break; | |
| case PPC::S_DTPREL_HIGH: | |
| Type = ELF::R_PPC64_DTPREL16_HIGH; | |
| break; | |
| case PPC::S_DTPREL_HIGHA: | |
| Type = ELF::R_PPC64_DTPREL16_HIGHA; | |
| break; | |
| case PPC::S_DTPREL_HIGHER: | |
| Type = ELF::R_PPC64_DTPREL16_HIGHER; | |
| break; | |
| case PPC::S_DTPREL_HIGHERA: | |
| Type = ELF::R_PPC64_DTPREL16_HIGHERA; | |
| break; | |
| case PPC::S_DTPREL_HIGHEST: | |
| Type = ELF::R_PPC64_DTPREL16_HIGHEST; | |
| break; | |
| case PPC::S_DTPREL_HIGHESTA: | |
| Type = ELF::R_PPC64_DTPREL16_HIGHESTA; | |
| break; | |
| case PPC::S_GOT_TLSGD: | |
| if (is64Bit()) | |
| Type = ELF::R_PPC64_GOT_TLSGD16; | |
| else | |
| Type = ELF::R_PPC_GOT_TLSGD16; | |
| break; | |
| case PPC::S_GOT_TLSGD_LO: | |
| Type = ELF::R_PPC64_GOT_TLSGD16_LO; | |
| break; | |
| case PPC::S_GOT_TLSGD_HI: | |
| Type = ELF::R_PPC64_GOT_TLSGD16_HI; | |
| break; | |
| case PPC::S_GOT_TLSGD_HA: | |
| Type = ELF::R_PPC64_GOT_TLSGD16_HA; | |
| break; | |
| case PPC::S_GOT_TLSLD: | |
| if (is64Bit()) | |
| Type = ELF::R_PPC64_GOT_TLSLD16; | |
| else | |
| Type = ELF::R_PPC_GOT_TLSLD16; | |
| break; | |
| case PPC::S_GOT_TLSLD_LO: | |
| Type = ELF::R_PPC64_GOT_TLSLD16_LO; | |
| break; | |
| case PPC::S_GOT_TLSLD_HI: | |
| Type = ELF::R_PPC64_GOT_TLSLD16_HI; | |
| break; | |
| case PPC::S_GOT_TLSLD_HA: | |
| Type = ELF::R_PPC64_GOT_TLSLD16_HA; | |
| break; | |
| case PPC::S_GOT_TPREL: | |
| /* We don't have R_PPC64_GOT_TPREL16, but since GOT offsets | |
| are always 4-aligned, we can use R_PPC64_GOT_TPREL16_DS. */ | |
| Type = ELF::R_PPC64_GOT_TPREL16_DS; | |
| break; | |
| case PPC::S_GOT_TPREL_LO: | |
| /* We don't have R_PPC64_GOT_TPREL16_LO, but since GOT offsets | |
| are always 4-aligned, we can use R_PPC64_GOT_TPREL16_LO_DS. */ | |
| Type = ELF::R_PPC64_GOT_TPREL16_LO_DS; | |
| break; | |
| case PPC::S_GOT_TPREL_HI: | |
| Type = ELF::R_PPC64_GOT_TPREL16_HI; | |
| break; | |
| case PPC::S_GOT_DTPREL: | |
| /* We don't have R_PPC64_GOT_DTPREL16, but since GOT offsets | |
| are always 4-aligned, we can use R_PPC64_GOT_DTPREL16_DS. */ | |
| Type = ELF::R_PPC64_GOT_DTPREL16_DS; | |
| break; | |
| case PPC::S_GOT_DTPREL_LO: | |
| /* We don't have R_PPC64_GOT_DTPREL16_LO, but since GOT offsets | |
| are always 4-aligned, we can use R_PPC64_GOT_DTPREL16_LO_DS. */ | |
| Type = ELF::R_PPC64_GOT_DTPREL16_LO_DS; | |
| break; | |
| case PPC::S_GOT_TPREL_HA: | |
| Type = ELF::R_PPC64_GOT_TPREL16_HA; | |
| break; | |
| case PPC::S_GOT_DTPREL_HI: | |
| Type = ELF::R_PPC64_GOT_DTPREL16_HI; | |
| break; | |
| case PPC::S_GOT_DTPREL_HA: | |
| Type = ELF::R_PPC64_GOT_DTPREL16_HA; | |
| break; | |
| } | |
| break; | |
| case PPC::fixup_ppc_half16ds: | |
| case PPC::fixup_ppc_half16dq: | |
| switch (Spec) { | |
| default: | |
| reportError(Loc, "unsupported relocation type"); | |
| break; | |
| case PPC::S_LO: | |
| return ELF::R_PPC64_ADDR16_LO_DS; | |
| case PPC::S_None: | |
| Type = ELF::R_PPC64_ADDR16_DS; | |
| break; | |
| case PPC::S_GOT: | |
| Type = ELF::R_PPC64_GOT16_DS; | |
| break; | |
| case PPC::S_GOT_LO: | |
| Type = ELF::R_PPC64_GOT16_LO_DS; | |
| break; | |
| case PPC::S_TOC: | |
| Type = ELF::R_PPC64_TOC16_DS; | |
| break; | |
| case PPC::S_TOC_LO: | |
| Type = ELF::R_PPC64_TOC16_LO_DS; | |
| break; | |
| case PPC::S_TPREL: | |
| Type = ELF::R_PPC64_TPREL16_DS; | |
| break; | |
| case PPC::S_TPREL_LO: | |
| Type = ELF::R_PPC64_TPREL16_LO_DS; | |
| break; | |
| case PPC::S_DTPREL: | |
| Type = ELF::R_PPC64_DTPREL16_DS; | |
| break; | |
| case PPC::S_DTPREL_LO: | |
| Type = ELF::R_PPC64_DTPREL16_LO_DS; | |
| break; | |
| case PPC::S_GOT_TPREL: | |
| Type = ELF::R_PPC64_GOT_TPREL16_DS; | |
| break; | |
| case PPC::S_GOT_TPREL_LO: | |
| Type = ELF::R_PPC64_GOT_TPREL16_LO_DS; | |
| break; | |
| case PPC::S_GOT_DTPREL: | |
| Type = ELF::R_PPC64_GOT_DTPREL16_DS; | |
| break; | |
| case PPC::S_GOT_DTPREL_LO: | |
| Type = ELF::R_PPC64_GOT_DTPREL16_LO_DS; | |
| break; | |
| } | |
| break; | |
| case PPC::fixup_ppc_nofixup: | |
| switch (Spec) { | |
| default: | |
| reportError(Loc, "unsupported relocation type"); | |
| break; | |
| case PPC::S_TLSGD: | |
| if (is64Bit()) | |
| Type = ELF::R_PPC64_TLSGD; | |
| else | |
| Type = ELF::R_PPC_TLSGD; | |
| break; | |
| case PPC::S_TLSLD: | |
| if (is64Bit()) | |
| Type = ELF::R_PPC64_TLSLD; | |
| else | |
| Type = ELF::R_PPC_TLSLD; | |
| break; | |
| case PPC::S_TLS: | |
| if (is64Bit()) | |
| Type = ELF::R_PPC64_TLS; | |
| else | |
| Type = ELF::R_PPC_TLS; | |
| break; | |
| case PPC::S_TLS_PCREL: | |
| Type = ELF::R_PPC64_TLS; | |
| break; | |
| } | |
| break; | |
| case PPC::fixup_ppc_imm34: | |
| switch (Spec) { | |
| default: | |
| reportError(Loc, "unsupported relocation type"); | |
| break; | |
| case PPC::S_DTPREL: | |
| Type = ELF::R_PPC64_DTPREL34; | |
| break; | |
| case PPC::S_TPREL: | |
| Type = ELF::R_PPC64_TPREL34; | |
| break; | |
| } | |
| break; | |
| case FK_Data_8: | |
| switch (Spec) { | |
| default: | |
| reportError(Loc, "unsupported relocation type"); | |
| break; | |
| case PPC::S_TOCBASE: | |
| Type = ELF::R_PPC64_TOC; | |
| break; | |
| case PPC::S_None: | |
| Type = ELF::R_PPC64_ADDR64; | |
| break; | |
| case PPC::S_DTPMOD: | |
| Type = ELF::R_PPC64_DTPMOD64; | |
| break; | |
| case PPC::S_TPREL: | |
| Type = ELF::R_PPC64_TPREL64; | |
| break; | |
| case PPC::S_DTPREL: | |
| Type = ELF::R_PPC64_DTPREL64; | |
| break; | |
| } | |
| break; | |
| case FK_Data_4: | |
| switch (Spec) { | |
| case PPC::S_DTPREL: | |
| Type = ELF::R_PPC_DTPREL32; | |
| break; | |
| default: | |
| Type = ELF::R_PPC_ADDR32; | |
| } | |
| break; | |
| case FK_Data_2: | |
| Type = ELF::R_PPC_ADDR16; | |
| break; | |
| } | |
| } | |
| return Type; | |
| } | |
| bool PPCELFObjectWriter::needsRelocateWithSymbol(const MCValue &V, | |
| unsigned Type) const { | |
| switch (Type) { | |
| default: | |
| return false; | |
| case ELF::R_PPC_REL24: | |
| case ELF::R_PPC64_REL24_NOTOC: { | |
| // If the target symbol has a local entry point, we must keep the | |
| // target symbol to preserve that information for the linker. | |
| // The "other" values are stored in the last 6 bits of the second byte. | |
| // The traditional defines for STO values assume the full byte and thus | |
| // the shift to pack it. | |
| unsigned Other = | |
| static_cast<const MCSymbolELF *>(V.getAddSym())->getOther() << 2; | |
| return (Other & ELF::STO_PPC64_LOCAL_MASK) != 0; | |
| } | |
| case ELF::R_PPC64_GOT16: | |
| case ELF::R_PPC64_GOT16_DS: | |
| case ELF::R_PPC64_GOT16_LO: | |
| case ELF::R_PPC64_GOT16_LO_DS: | |
| case ELF::R_PPC64_GOT16_HI: | |
| case ELF::R_PPC64_GOT16_HA: | |
| return true; | |
| } | |
| } | |
| std::unique_ptr<MCObjectTargetWriter> | |
| llvm::createPPCELFObjectWriter(bool Is64Bit, uint8_t OSABI) { | |
| return std::make_unique<PPCELFObjectWriter>(Is64Bit, OSABI); | |
| } |