//===-- 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 <cstdint>
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.reportFatalError(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.reportFatalError(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<MCSectionELF *>(Ctx.getObjectFileInfo()->getTextSection());
if (Sec.getKind().isExecuteOnly() && !TextSection->hasInstructions()) {
for (auto &F : TextSection->getFragmentList())
if (auto *DF = dyn_cast<MCDataFragment>(&F))
if (!DF->getContents().empty())
return;
TextSection->setFlags(TextSection->getFlags() | ELF::SHF_ARM_PURECODE);
}
}
std::unique_ptr<MCObjectTargetWriter>
llvm::createARMELFObjectWriter(uint8_t OSABI) {
return std::make_unique<ARMELFObjectWriter>(OSABI);
}