ARMMachObjectWriter.cpp [plain text]
#include "MCTargetDesc/ARMBaseInfo.h"
#include "MCTargetDesc/ARMFixupKinds.h"
#include "llvm/ADT/Twine.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCAsmLayout.h"
#include "llvm/MC/MCMachObjectWriter.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCFixup.h"
#include "llvm/MC/MCFixupKindInfo.h"
#include "llvm/MC/MCMachOSymbolFlags.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Object/MachOFormat.h"
#include "llvm/Support/ErrorHandling.h"
using namespace llvm;
using namespace llvm::object;
namespace {
class ARMMachObjectWriter : public MCMachObjectTargetWriter {
void RecordARMScatteredRelocation(MachObjectWriter *Writer,
const MCAssembler &Asm,
const MCAsmLayout &Layout,
const MCFragment *Fragment,
const MCFixup &Fixup,
MCValue Target,
unsigned Log2Size,
uint64_t &FixedValue);
void RecordARMMovwMovtRelocation(MachObjectWriter *Writer,
const MCAssembler &Asm,
const MCAsmLayout &Layout,
const MCFragment *Fragment,
const MCFixup &Fixup, MCValue Target,
uint64_t &FixedValue);
public:
ARMMachObjectWriter(bool Is64Bit, uint32_t CPUType,
uint32_t CPUSubtype)
: MCMachObjectTargetWriter(Is64Bit, CPUType, CPUSubtype,
true) {}
void RecordRelocation(MachObjectWriter *Writer,
const MCAssembler &Asm, const MCAsmLayout &Layout,
const MCFragment *Fragment, const MCFixup &Fixup,
MCValue Target, uint64_t &FixedValue);
};
}
static bool getARMFixupKindMachOInfo(unsigned Kind, unsigned &RelocType,
unsigned &Log2Size) {
RelocType = unsigned(macho::RIT_Vanilla);
Log2Size = ~0U;
switch (Kind) {
default:
return false;
case FK_Data_1:
Log2Size = llvm::Log2_32(1);
return true;
case FK_Data_2:
Log2Size = llvm::Log2_32(2);
return true;
case FK_Data_4:
Log2Size = llvm::Log2_32(4);
return true;
case FK_Data_8:
Log2Size = llvm::Log2_32(8);
return true;
case ARM::fixup_arm_ldst_pcrel_12:
case ARM::fixup_arm_pcrel_10:
case ARM::fixup_arm_adr_pcrel_12:
case ARM::fixup_arm_condbranch:
case ARM::fixup_arm_uncondbranch:
RelocType = unsigned(macho::RIT_ARM_Branch24Bit);
Log2Size = llvm::Log2_32(4);
return true;
case ARM::fixup_arm_thumb_br:
RelocType = unsigned(macho::RIT_ARM_ThumbBranch22Bit);
Log2Size = llvm::Log2_32(2);
return true;
case ARM::fixup_t2_uncondbranch:
case ARM::fixup_arm_thumb_bl:
case ARM::fixup_arm_thumb_blx:
RelocType = unsigned(macho::RIT_ARM_ThumbBranch22Bit);
Log2Size = llvm::Log2_32(4);
return true;
case ARM::fixup_arm_movt_hi16:
case ARM::fixup_arm_movt_hi16_pcrel:
case ARM::fixup_t2_movt_hi16:
case ARM::fixup_t2_movt_hi16_pcrel:
RelocType = unsigned(macho::RIT_ARM_HalfDifference);
Log2Size = llvm::Log2_32(4);
return true;
case ARM::fixup_arm_movw_lo16:
case ARM::fixup_arm_movw_lo16_pcrel:
case ARM::fixup_t2_movw_lo16:
case ARM::fixup_t2_movw_lo16_pcrel:
RelocType = unsigned(macho::RIT_ARM_Half);
Log2Size = llvm::Log2_32(4);
return true;
}
}
void ARMMachObjectWriter::
RecordARMMovwMovtRelocation(MachObjectWriter *Writer,
const MCAssembler &Asm,
const MCAsmLayout &Layout,
const MCFragment *Fragment,
const MCFixup &Fixup,
MCValue Target,
uint64_t &FixedValue) {
uint32_t FixupOffset = Layout.getFragmentOffset(Fragment)+Fixup.getOffset();
unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
unsigned Type = macho::RIT_ARM_Half;
const MCSymbol *A = &Target.getSymA()->getSymbol();
MCSymbolData *A_SD = &Asm.getSymbolData(*A);
if (!A_SD->getFragment())
report_fatal_error("symbol '" + A->getName() +
"' can not be undefined in a subtraction expression");
uint32_t Value = Writer->getSymbolAddress(A_SD, Layout);
uint32_t Value2 = 0;
uint64_t SecAddr =
Writer->getSectionAddress(A_SD->getFragment()->getParent());
FixedValue += SecAddr;
if (const MCSymbolRefExpr *B = Target.getSymB()) {
MCSymbolData *B_SD = &Asm.getSymbolData(B->getSymbol());
if (!B_SD->getFragment())
report_fatal_error("symbol '" + B->getSymbol().getName() +
"' can not be undefined in a subtraction expression");
Type = macho::RIT_ARM_HalfDifference;
Value2 = Writer->getSymbolAddress(B_SD, Layout);
FixedValue -= Writer->getSectionAddress(B_SD->getFragment()->getParent());
}
unsigned ThumbBit = 0;
unsigned MovtBit = 0;
switch ((unsigned)Fixup.getKind()) {
default: break;
case ARM::fixup_arm_movt_hi16:
case ARM::fixup_arm_movt_hi16_pcrel:
MovtBit = 1;
if (A_SD->getFlags() & SF_ThumbFunc)
FixedValue &= 0xfffffffe;
break;
case ARM::fixup_t2_movt_hi16:
case ARM::fixup_t2_movt_hi16_pcrel:
if (A_SD->getFlags() & SF_ThumbFunc)
FixedValue &= 0xfffffffe;
MovtBit = 1;
case ARM::fixup_t2_movw_lo16:
case ARM::fixup_t2_movw_lo16_pcrel:
ThumbBit = 1;
break;
}
if (Type == macho::RIT_ARM_HalfDifference) {
uint32_t OtherHalf = MovtBit
? (FixedValue & 0xffff) : ((FixedValue & 0xffff0000) >> 16);
macho::RelocationEntry MRE;
MRE.Word0 = ((OtherHalf << 0) |
(macho::RIT_Pair << 24) |
(MovtBit << 28) |
(ThumbBit << 29) |
(IsPCRel << 30) |
macho::RF_Scattered);
MRE.Word1 = Value2;
Writer->addRelocation(Fragment->getParent(), MRE);
}
macho::RelocationEntry MRE;
MRE.Word0 = ((FixupOffset << 0) |
(Type << 24) |
(MovtBit << 28) |
(ThumbBit << 29) |
(IsPCRel << 30) |
macho::RF_Scattered);
MRE.Word1 = Value;
Writer->addRelocation(Fragment->getParent(), MRE);
}
void ARMMachObjectWriter::RecordARMScatteredRelocation(MachObjectWriter *Writer,
const MCAssembler &Asm,
const MCAsmLayout &Layout,
const MCFragment *Fragment,
const MCFixup &Fixup,
MCValue Target,
unsigned Log2Size,
uint64_t &FixedValue) {
uint32_t FixupOffset = Layout.getFragmentOffset(Fragment)+Fixup.getOffset();
unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
unsigned Type = macho::RIT_Vanilla;
const MCSymbol *A = &Target.getSymA()->getSymbol();
MCSymbolData *A_SD = &Asm.getSymbolData(*A);
if (!A_SD->getFragment())
report_fatal_error("symbol '" + A->getName() +
"' can not be undefined in a subtraction expression");
uint32_t Value = Writer->getSymbolAddress(A_SD, Layout);
uint64_t SecAddr = Writer->getSectionAddress(A_SD->getFragment()->getParent());
FixedValue += SecAddr;
uint32_t Value2 = 0;
if (const MCSymbolRefExpr *B = Target.getSymB()) {
MCSymbolData *B_SD = &Asm.getSymbolData(B->getSymbol());
if (!B_SD->getFragment())
report_fatal_error("symbol '" + B->getSymbol().getName() +
"' can not be undefined in a subtraction expression");
Type = macho::RIT_Difference;
Value2 = Writer->getSymbolAddress(B_SD, Layout);
FixedValue -= Writer->getSectionAddress(B_SD->getFragment()->getParent());
}
if (Type == macho::RIT_Difference ||
Type == macho::RIT_Generic_LocalDifference) {
macho::RelocationEntry MRE;
MRE.Word0 = ((0 << 0) |
(macho::RIT_Pair << 24) |
(Log2Size << 28) |
(IsPCRel << 30) |
macho::RF_Scattered);
MRE.Word1 = Value2;
Writer->addRelocation(Fragment->getParent(), MRE);
}
macho::RelocationEntry MRE;
MRE.Word0 = ((FixupOffset << 0) |
(Type << 24) |
(Log2Size << 28) |
(IsPCRel << 30) |
macho::RF_Scattered);
MRE.Word1 = Value;
Writer->addRelocation(Fragment->getParent(), MRE);
}
void ARMMachObjectWriter::RecordRelocation(MachObjectWriter *Writer,
const MCAssembler &Asm,
const MCAsmLayout &Layout,
const MCFragment *Fragment,
const MCFixup &Fixup,
MCValue Target,
uint64_t &FixedValue) {
unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());
unsigned Log2Size;
unsigned RelocType = macho::RIT_Vanilla;
if (!getARMFixupKindMachOInfo(Fixup.getKind(), RelocType, Log2Size)) {
report_fatal_error("unknown ARM fixup kind!");
return;
}
if (Target.getSymB()) {
if (RelocType == macho::RIT_ARM_Half ||
RelocType == macho::RIT_ARM_HalfDifference)
return RecordARMMovwMovtRelocation(Writer, Asm, Layout, Fragment, Fixup,
Target, FixedValue);
return RecordARMScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup,
Target, Log2Size, FixedValue);
}
MCSymbolData *SD = 0;
if (Target.getSymA())
SD = &Asm.getSymbolData(Target.getSymA()->getSymbol());
uint32_t Offset = Target.getConstant();
if (IsPCRel && RelocType == macho::RIT_Vanilla)
Offset += 1 << Log2Size;
if (Offset && SD && !Writer->doesSymbolRequireExternRelocation(SD))
return RecordARMScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup,
Target, Log2Size, FixedValue);
uint32_t FixupOffset = Layout.getFragmentOffset(Fragment)+Fixup.getOffset();
unsigned Index = 0;
unsigned IsExtern = 0;
unsigned Type = 0;
if (Target.isAbsolute()) { report_fatal_error("FIXME: relocations to absolute targets "
"not yet implemented");
} else {
if (SD->getSymbol().isVariable()) {
int64_t Res;
if (SD->getSymbol().getVariableValue()->EvaluateAsAbsolute(
Res, Layout, Writer->getSectionAddressMap())) {
FixedValue = Res;
return;
}
}
if (Writer->doesSymbolRequireExternRelocation(SD)) {
IsExtern = 1;
Index = SD->getIndex();
if (!SD->Symbol->isUndefined())
FixedValue -= Layout.getSymbolOffset(SD);
} else {
const MCSectionData &SymSD = Asm.getSectionData(
SD->getSymbol().getSection());
Index = SymSD.getOrdinal() + 1;
FixedValue += Writer->getSectionAddress(&SymSD);
}
if (IsPCRel)
FixedValue -= Writer->getSectionAddress(Fragment->getParent());
Type = RelocType;
}
macho::RelocationEntry MRE;
MRE.Word0 = FixupOffset;
MRE.Word1 = ((Index << 0) |
(IsPCRel << 24) |
(Log2Size << 25) |
(IsExtern << 27) |
(Type << 28));
Writer->addRelocation(Fragment->getParent(), MRE);
}
MCObjectWriter *llvm::createARMMachObjectWriter(raw_ostream &OS,
bool Is64Bit,
uint32_t CPUType,
uint32_t CPUSubtype) {
return createMachObjectWriter(new ARMMachObjectWriter(Is64Bit,
CPUType,
CPUSubtype),
OS, true);
}