//===-- ARMELFObjectWriter.cpp - ARM 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/ARMFixupKinds.h" #include "MCTargetDesc/ARMMCTargetDesc.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCELFObjectWriter.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCFixup.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCValue.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include using namespace llvm; namespace { class ARMELFObjectWriter : public MCELFObjectTargetWriter { enum { DefaultEABIVersion = 0x05000000U }; unsigned GetRelocTypeInner(const MCValue &Target, const MCFixup &Fixup, bool IsPCRel, MCContext &Ctx) const; public: ARMELFObjectWriter(uint8_t OSABI); ~ARMELFObjectWriter() override = default; unsigned getRelocType(MCContext &Ctx, const MCValue &Target, const MCFixup &Fixup, bool IsPCRel) const override; bool needsRelocateWithSymbol(const MCSymbol &Sym, unsigned Type) const override; void addTargetSectionFlags(MCContext &Ctx, MCSectionELF &Sec) override; }; } // end anonymous namespace ARMELFObjectWriter::ARMELFObjectWriter(uint8_t OSABI) : MCELFObjectTargetWriter(/*Is64Bit*/ false, OSABI, ELF::EM_ARM, /*HasRelocationAddend*/ false) {} bool ARMELFObjectWriter::needsRelocateWithSymbol(const MCSymbol &Sym, unsigned Type) const { // FIXME: This is extremely conservative. This really needs to use an // explicit list with a clear explanation for why each realocation needs to // point to the symbol, not to the section. switch (Type) { default: return true; case ELF::R_ARM_PREL31: case ELF::R_ARM_ABS32: return false; } } // Need to examine the Fixup when determining whether to // emit the relocation as an explicit symbol or as a section relative // offset unsigned ARMELFObjectWriter::getRelocType(MCContext &Ctx, const MCValue &Target, const MCFixup &Fixup, bool IsPCRel) const { return GetRelocTypeInner(Target, Fixup, IsPCRel, Ctx); } unsigned ARMELFObjectWriter::GetRelocTypeInner(const MCValue &Target, const MCFixup &Fixup, bool IsPCRel, MCContext &Ctx) const { unsigned Kind = Fixup.getTargetKind(); if (Kind >= FirstLiteralRelocationKind) return Kind - FirstLiteralRelocationKind; MCSymbolRefExpr::VariantKind Modifier = Target.getAccessVariant(); if (IsPCRel) { switch (Fixup.getTargetKind()) { default: Ctx.reportError(Fixup.getLoc(), "unsupported relocation on symbol"); return ELF::R_ARM_NONE; case FK_Data_4: switch (Modifier) { default: Ctx.reportError(Fixup.getLoc(), "invalid fixup for 4-byte pc-relative data relocation"); return ELF::R_ARM_NONE; case MCSymbolRefExpr::VK_None: { if (const MCSymbolRefExpr *SymRef = Target.getSymA()) { // For GNU AS compatibility expressions such as // _GLOBAL_OFFSET_TABLE_ - label emit a R_ARM_BASE_PREL relocation. if (SymRef->getSymbol().getName() == "_GLOBAL_OFFSET_TABLE_") return ELF::R_ARM_BASE_PREL; } return ELF::R_ARM_REL32; } case MCSymbolRefExpr::VK_GOTTPOFF: return ELF::R_ARM_TLS_IE32; case MCSymbolRefExpr::VK_ARM_GOT_PREL: return ELF::R_ARM_GOT_PREL; case MCSymbolRefExpr::VK_ARM_PREL31: return ELF::R_ARM_PREL31; } case ARM::fixup_arm_blx: case ARM::fixup_arm_uncondbl: switch (Modifier) { case MCSymbolRefExpr::VK_PLT: return ELF::R_ARM_CALL; case MCSymbolRefExpr::VK_TLSCALL: return ELF::R_ARM_TLS_CALL; default: return ELF::R_ARM_CALL; } case ARM::fixup_arm_condbl: case ARM::fixup_arm_condbranch: case ARM::fixup_arm_uncondbranch: return ELF::R_ARM_JUMP24; case ARM::fixup_t2_condbranch: return ELF::R_ARM_THM_JUMP19; case ARM::fixup_t2_uncondbranch: return ELF::R_ARM_THM_JUMP24; case ARM::fixup_arm_movt_hi16: return ELF::R_ARM_MOVT_PREL; case ARM::fixup_arm_movw_lo16: return ELF::R_ARM_MOVW_PREL_NC; case ARM::fixup_t2_movt_hi16: return ELF::R_ARM_THM_MOVT_PREL; case ARM::fixup_t2_movw_lo16: return ELF::R_ARM_THM_MOVW_PREL_NC; case ARM::fixup_arm_thumb_br: return ELF::R_ARM_THM_JUMP11; case ARM::fixup_arm_thumb_bcc: return ELF::R_ARM_THM_JUMP8; case ARM::fixup_arm_thumb_bl: case ARM::fixup_arm_thumb_blx: switch (Modifier) { case MCSymbolRefExpr::VK_TLSCALL: return ELF::R_ARM_THM_TLS_CALL; default: return ELF::R_ARM_THM_CALL; } case ARM::fixup_bf_target: return ELF::R_ARM_THM_BF16; case ARM::fixup_bfc_target: return ELF::R_ARM_THM_BF12; case ARM::fixup_bfl_target: return ELF::R_ARM_THM_BF18; } } switch (Kind) { default: Ctx.reportError(Fixup.getLoc(), "unsupported relocation on symbol"); return ELF::R_ARM_NONE; case FK_Data_1: switch (Modifier) { default: Ctx.reportError(Fixup.getLoc(), "invalid fixup for 1-byte data relocation"); return ELF::R_ARM_NONE; case MCSymbolRefExpr::VK_None: return ELF::R_ARM_ABS8; } case FK_Data_2: switch (Modifier) { default: Ctx.reportError(Fixup.getLoc(), "invalid fixup for 2-byte data relocation"); return ELF::R_ARM_NONE; case MCSymbolRefExpr::VK_None: return ELF::R_ARM_ABS16; } case FK_Data_4: switch (Modifier) { default: Ctx.reportError(Fixup.getLoc(), "invalid fixup for 4-byte data relocation"); return ELF::R_ARM_NONE; case MCSymbolRefExpr::VK_ARM_NONE: return ELF::R_ARM_NONE; case MCSymbolRefExpr::VK_GOT: return ELF::R_ARM_GOT_BREL; case MCSymbolRefExpr::VK_TLSGD: return ELF::R_ARM_TLS_GD32; case MCSymbolRefExpr::VK_TPOFF: return ELF::R_ARM_TLS_LE32; case MCSymbolRefExpr::VK_GOTTPOFF: return ELF::R_ARM_TLS_IE32; case MCSymbolRefExpr::VK_None: return ELF::R_ARM_ABS32; case MCSymbolRefExpr::VK_GOTOFF: return ELF::R_ARM_GOTOFF32; case MCSymbolRefExpr::VK_ARM_GOT_PREL: return ELF::R_ARM_GOT_PREL; case MCSymbolRefExpr::VK_ARM_TARGET1: return ELF::R_ARM_TARGET1; case MCSymbolRefExpr::VK_ARM_TARGET2: return ELF::R_ARM_TARGET2; case MCSymbolRefExpr::VK_ARM_PREL31: return ELF::R_ARM_PREL31; case MCSymbolRefExpr::VK_ARM_SBREL: return ELF::R_ARM_SBREL32; case MCSymbolRefExpr::VK_ARM_TLSLDO: return ELF::R_ARM_TLS_LDO32; case MCSymbolRefExpr::VK_TLSCALL: return ELF::R_ARM_TLS_CALL; case MCSymbolRefExpr::VK_TLSDESC: return ELF::R_ARM_TLS_GOTDESC; case MCSymbolRefExpr::VK_TLSLDM: return ELF::R_ARM_TLS_LDM32; case MCSymbolRefExpr::VK_ARM_TLSDESCSEQ: return ELF::R_ARM_TLS_DESCSEQ; } case ARM::fixup_arm_condbranch: case ARM::fixup_arm_uncondbranch: return ELF::R_ARM_JUMP24; case ARM::fixup_arm_movt_hi16: switch (Modifier) { default: Ctx.reportError(Fixup.getLoc(), "invalid fixup for ARM MOVT instruction"); return ELF::R_ARM_NONE; case MCSymbolRefExpr::VK_None: return ELF::R_ARM_MOVT_ABS; case MCSymbolRefExpr::VK_ARM_SBREL: return ELF::R_ARM_MOVT_BREL; } case ARM::fixup_arm_movw_lo16: switch (Modifier) { default: Ctx.reportError(Fixup.getLoc(), "invalid fixup for ARM MOVW instruction"); return ELF::R_ARM_NONE; case MCSymbolRefExpr::VK_None: return ELF::R_ARM_MOVW_ABS_NC; case MCSymbolRefExpr::VK_ARM_SBREL: return ELF::R_ARM_MOVW_BREL_NC; } case ARM::fixup_t2_movt_hi16: switch (Modifier) { default: Ctx.reportError(Fixup.getLoc(), "invalid fixup for Thumb MOVT instruction"); return ELF::R_ARM_NONE; case MCSymbolRefExpr::VK_None: return ELF::R_ARM_THM_MOVT_ABS; case MCSymbolRefExpr::VK_ARM_SBREL: return ELF::R_ARM_THM_MOVT_BREL; } case ARM::fixup_t2_movw_lo16: switch (Modifier) { default: Ctx.reportError(Fixup.getLoc(), "invalid fixup for Thumb MOVW instruction"); return ELF::R_ARM_NONE; case MCSymbolRefExpr::VK_None: return ELF::R_ARM_THM_MOVW_ABS_NC; case MCSymbolRefExpr::VK_ARM_SBREL: return ELF::R_ARM_THM_MOVW_BREL_NC; } } } void ARMELFObjectWriter::addTargetSectionFlags(MCContext &Ctx, MCSectionELF &Sec) { // The mix of execute-only and non-execute-only at link time is // non-execute-only. To avoid the empty implicitly created .text // section from making the whole .text section non-execute-only, we // mark it execute-only if it is empty and there is at least one // execute-only section in the object. MCSectionELF *TextSection = static_cast(Ctx.getObjectFileInfo()->getTextSection()); if (Sec.getKind().isExecuteOnly() && !TextSection->hasInstructions()) { for (auto &F : TextSection->getFragmentList()) if (auto *DF = dyn_cast(&F)) if (!DF->getContents().empty()) return; TextSection->setFlags(TextSection->getFlags() | ELF::SHF_ARM_PURECODE); } } std::unique_ptr llvm::createARMELFObjectWriter(uint8_t OSABI) { return std::make_unique(OSABI); }