293 lines
10 KiB
C++
293 lines
10 KiB
C++
//===-- 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);
|
|
}
|