#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Twine.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/BinaryFormat/MachO.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAsmLayout.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCDirectives.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCFixupKindInfo.h"
#include "llvm/MC/MCFragment.h"
#include "llvm/MC/MCMachObjectWriter.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCSection.h"
#include "llvm/MC/MCSectionMachO.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/MC/MCSymbolMachO.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/Alignment.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <string>
#include <utility>
#include <vector>
using namespace llvm;
#define DEBUG_TYPE "mc"
void MachObjectWriter::reset() {
Relocations.clear();
IndirectSymBase.clear();
StringTable.clear();
LocalSymbolData.clear();
ExternalSymbolData.clear();
UndefinedSymbolData.clear();
MCObjectWriter::reset();
}
bool MachObjectWriter::doesSymbolRequireExternRelocation(const MCSymbol &S) {
if (S.isUndefined())
return true;
if (cast<MCSymbolMachO>(S).isWeakDefinition())
return true;
return false;
}
bool MachObjectWriter::
MachSymbolData::operator<(const MachSymbolData &RHS) const {
return Symbol->getName() < RHS.Symbol->getName();
}
bool MachObjectWriter::isFixupKindPCRel(const MCAssembler &Asm, unsigned Kind) {
const MCFixupKindInfo &FKI = Asm.getBackend().getFixupKindInfo(
(MCFixupKind) Kind);
return FKI.Flags & MCFixupKindInfo::FKF_IsPCRel;
}
uint64_t MachObjectWriter::getFragmentAddress(const MCFragment *Fragment,
const MCAsmLayout &Layout) const {
return getSectionAddress(Fragment->getParent()) +
Layout.getFragmentOffset(Fragment);
}
uint64_t MachObjectWriter::getSymbolAddress(const MCSymbol &S,
const MCAsmLayout &Layout) const {
if (S.isVariable()) {
if (const MCConstantExpr *C =
dyn_cast<const MCConstantExpr>(S.getVariableValue()))
return C->getValue();
MCValue Target;
if (!S.getVariableValue()->evaluateAsRelocatable(Target, &Layout, nullptr))
report_fatal_error("unable to evaluate offset for variable '" +
S.getName() + "'");
if (Target.getSymA() && Target.getSymA()->getSymbol().isUndefined())
report_fatal_error("unable to evaluate offset to undefined symbol '" +
Target.getSymA()->getSymbol().getName() + "'");
if (Target.getSymB() && Target.getSymB()->getSymbol().isUndefined())
report_fatal_error("unable to evaluate offset to undefined symbol '" +
Target.getSymB()->getSymbol().getName() + "'");
uint64_t Address = Target.getConstant();
if (Target.getSymA())
Address += getSymbolAddress(Target.getSymA()->getSymbol(), Layout);
if (Target.getSymB())
Address += getSymbolAddress(Target.getSymB()->getSymbol(), Layout);
return Address;
}
return getSectionAddress(S.getFragment()->getParent()) +
Layout.getSymbolOffset(S);
}
uint64_t MachObjectWriter::getPaddingSize(const MCSection *Sec,
const MCAsmLayout &Layout) const {
uint64_t EndAddr = getSectionAddress(Sec) + Layout.getSectionAddressSize(Sec);
unsigned Next = Sec->getLayoutOrder() + 1;
if (Next >= Layout.getSectionOrder().size())
return 0;
const MCSection &NextSec = *Layout.getSectionOrder()[Next];
if (NextSec.isVirtualSection())
return 0;
return offsetToAlignment(EndAddr, Align(NextSec.getAlignment()));
}
void MachObjectWriter::writeHeader(MachO::HeaderFileType Type,
unsigned NumLoadCommands,
unsigned LoadCommandsSize,
bool SubsectionsViaSymbols) {
uint32_t Flags = 0;
if (SubsectionsViaSymbols)
Flags |= MachO::MH_SUBSECTIONS_VIA_SYMBOLS;
uint64_t Start = W.OS.tell();
(void) Start;
W.write<uint32_t>(is64Bit() ? MachO::MH_MAGIC_64 : MachO::MH_MAGIC);
W.write<uint32_t>(TargetObjectWriter->getCPUType());
W.write<uint32_t>(TargetObjectWriter->getCPUSubtype());
W.write<uint32_t>(Type);
W.write<uint32_t>(NumLoadCommands);
W.write<uint32_t>(LoadCommandsSize);
W.write<uint32_t>(Flags);
if (is64Bit())
W.write<uint32_t>(0);
assert(W.OS.tell() - Start == (is64Bit() ? sizeof(MachO::mach_header_64)
: sizeof(MachO::mach_header)));
}
void MachObjectWriter::writeWithPadding(StringRef Str, uint64_t Size) {
assert(Size >= Str.size());
W.OS << Str;
W.OS.write_zeros(Size - Str.size());
}
void MachObjectWriter::writeSegmentLoadCommand(
StringRef Name, unsigned NumSections, uint64_t VMAddr, uint64_t VMSize,
uint64_t SectionDataStartOffset, uint64_t SectionDataSize, uint32_t MaxProt,
uint32_t InitProt) {
uint64_t Start = W.OS.tell();
(void) Start;
unsigned SegmentLoadCommandSize =
is64Bit() ? sizeof(MachO::segment_command_64):
sizeof(MachO::segment_command);
W.write<uint32_t>(is64Bit() ? MachO::LC_SEGMENT_64 : MachO::LC_SEGMENT);
W.write<uint32_t>(SegmentLoadCommandSize +
NumSections * (is64Bit() ? sizeof(MachO::section_64) :
sizeof(MachO::section)));
writeWithPadding(Name, 16);
if (is64Bit()) {
W.write<uint64_t>(VMAddr); W.write<uint64_t>(VMSize); W.write<uint64_t>(SectionDataStartOffset); W.write<uint64_t>(SectionDataSize); } else {
W.write<uint32_t>(VMAddr); W.write<uint32_t>(VMSize); W.write<uint32_t>(SectionDataStartOffset); W.write<uint32_t>(SectionDataSize); }
W.write<uint32_t>(MaxProt);
W.write<uint32_t>(InitProt);
W.write<uint32_t>(NumSections);
W.write<uint32_t>(0);
assert(W.OS.tell() - Start == SegmentLoadCommandSize);
}
void MachObjectWriter::writeSection(const MCAsmLayout &Layout,
const MCSection &Sec, uint64_t VMAddr,
uint64_t FileOffset, unsigned Flags,
uint64_t RelocationsStart,
unsigned NumRelocations) {
uint64_t SectionSize = Layout.getSectionAddressSize(&Sec);
const MCSectionMachO &Section = cast<MCSectionMachO>(Sec);
if (Section.isVirtualSection()) {
assert(Layout.getSectionFileSize(&Sec) == 0 && "Invalid file size!");
FileOffset = 0;
}
uint64_t Start = W.OS.tell();
(void) Start;
writeWithPadding(Section.getName(), 16);
writeWithPadding(Section.getSegmentName(), 16);
if (is64Bit()) {
W.write<uint64_t>(VMAddr); W.write<uint64_t>(SectionSize); } else {
W.write<uint32_t>(VMAddr); W.write<uint32_t>(SectionSize); }
W.write<uint32_t>(FileOffset);
assert(isPowerOf2_32(Section.getAlignment()) && "Invalid alignment!");
W.write<uint32_t>(Log2_32(Section.getAlignment()));
W.write<uint32_t>(NumRelocations ? RelocationsStart : 0);
W.write<uint32_t>(NumRelocations);
W.write<uint32_t>(Flags);
W.write<uint32_t>(IndirectSymBase.lookup(&Sec)); W.write<uint32_t>(Section.getStubSize()); if (is64Bit())
W.write<uint32_t>(0);
assert(W.OS.tell() - Start ==
(is64Bit() ? sizeof(MachO::section_64) : sizeof(MachO::section)));
}
void MachObjectWriter::writeSymtabLoadCommand(uint32_t SymbolOffset,
uint32_t NumSymbols,
uint32_t StringTableOffset,
uint32_t StringTableSize) {
uint64_t Start = W.OS.tell();
(void) Start;
W.write<uint32_t>(MachO::LC_SYMTAB);
W.write<uint32_t>(sizeof(MachO::symtab_command));
W.write<uint32_t>(SymbolOffset);
W.write<uint32_t>(NumSymbols);
W.write<uint32_t>(StringTableOffset);
W.write<uint32_t>(StringTableSize);
assert(W.OS.tell() - Start == sizeof(MachO::symtab_command));
}
void MachObjectWriter::writeDysymtabLoadCommand(uint32_t FirstLocalSymbol,
uint32_t NumLocalSymbols,
uint32_t FirstExternalSymbol,
uint32_t NumExternalSymbols,
uint32_t FirstUndefinedSymbol,
uint32_t NumUndefinedSymbols,
uint32_t IndirectSymbolOffset,
uint32_t NumIndirectSymbols) {
uint64_t Start = W.OS.tell();
(void) Start;
W.write<uint32_t>(MachO::LC_DYSYMTAB);
W.write<uint32_t>(sizeof(MachO::dysymtab_command));
W.write<uint32_t>(FirstLocalSymbol);
W.write<uint32_t>(NumLocalSymbols);
W.write<uint32_t>(FirstExternalSymbol);
W.write<uint32_t>(NumExternalSymbols);
W.write<uint32_t>(FirstUndefinedSymbol);
W.write<uint32_t>(NumUndefinedSymbols);
W.write<uint32_t>(0); W.write<uint32_t>(0); W.write<uint32_t>(0); W.write<uint32_t>(0); W.write<uint32_t>(0); W.write<uint32_t>(0); W.write<uint32_t>(IndirectSymbolOffset);
W.write<uint32_t>(NumIndirectSymbols);
W.write<uint32_t>(0); W.write<uint32_t>(0); W.write<uint32_t>(0); W.write<uint32_t>(0);
assert(W.OS.tell() - Start == sizeof(MachO::dysymtab_command));
}
MachObjectWriter::MachSymbolData *
MachObjectWriter::findSymbolData(const MCSymbol &Sym) {
for (auto *SymbolData :
{&LocalSymbolData, &ExternalSymbolData, &UndefinedSymbolData})
for (MachSymbolData &Entry : *SymbolData)
if (Entry.Symbol == &Sym)
return &Entry;
return nullptr;
}
const MCSymbol &MachObjectWriter::findAliasedSymbol(const MCSymbol &Sym) const {
const MCSymbol *S = &Sym;
while (S->isVariable()) {
const MCExpr *Value = S->getVariableValue();
const auto *Ref = dyn_cast<MCSymbolRefExpr>(Value);
if (!Ref)
return *S;
S = &Ref->getSymbol();
}
return *S;
}
void MachObjectWriter::writeNlist(MachSymbolData &MSD,
const MCAsmLayout &Layout) {
const MCSymbol *Symbol = MSD.Symbol;
const MCSymbol &Data = *Symbol;
const MCSymbol *AliasedSymbol = &findAliasedSymbol(*Symbol);
uint8_t SectionIndex = MSD.SectionIndex;
uint8_t Type = 0;
uint64_t Address = 0;
bool IsAlias = Symbol != AliasedSymbol;
const MCSymbol &OrigSymbol = *Symbol;
MachSymbolData *AliaseeInfo;
if (IsAlias) {
AliaseeInfo = findSymbolData(*AliasedSymbol);
if (AliaseeInfo)
SectionIndex = AliaseeInfo->SectionIndex;
Symbol = AliasedSymbol;
}
if (IsAlias && Symbol->isUndefined())
Type = MachO::N_INDR;
else if (Symbol->isUndefined())
Type = MachO::N_UNDF;
else if (Symbol->isAbsolute())
Type = MachO::N_ABS;
else
Type = MachO::N_SECT;
if (Data.isPrivateExtern())
Type |= MachO::N_PEXT;
if (Data.isExternal() || (!IsAlias && Symbol->isUndefined()))
Type |= MachO::N_EXT;
if (IsAlias && Symbol->isUndefined())
Address = AliaseeInfo->StringIndex;
else if (Symbol->isDefined())
Address = getSymbolAddress(OrigSymbol, Layout);
else if (Symbol->isCommon()) {
Address = Symbol->getCommonSize();
}
W.write<uint32_t>(MSD.StringIndex);
W.OS << char(Type);
W.OS << char(SectionIndex);
bool EncodeAsAltEntry =
IsAlias && cast<MCSymbolMachO>(OrigSymbol).isAltEntry();
W.write<uint16_t>(cast<MCSymbolMachO>(Symbol)->getEncodedFlags(EncodeAsAltEntry));
if (is64Bit())
W.write<uint64_t>(Address);
else
W.write<uint32_t>(Address);
}
void MachObjectWriter::writeLinkeditLoadCommand(uint32_t Type,
uint32_t DataOffset,
uint32_t DataSize) {
uint64_t Start = W.OS.tell();
(void) Start;
W.write<uint32_t>(Type);
W.write<uint32_t>(sizeof(MachO::linkedit_data_command));
W.write<uint32_t>(DataOffset);
W.write<uint32_t>(DataSize);
assert(W.OS.tell() - Start == sizeof(MachO::linkedit_data_command));
}
static unsigned ComputeLinkerOptionsLoadCommandSize(
const std::vector<std::string> &Options, bool is64Bit)
{
unsigned Size = sizeof(MachO::linker_option_command);
for (const std::string &Option : Options)
Size += Option.size() + 1;
return alignTo(Size, is64Bit ? 8 : 4);
}
void MachObjectWriter::writeLinkerOptionsLoadCommand(
const std::vector<std::string> &Options)
{
unsigned Size = ComputeLinkerOptionsLoadCommandSize(Options, is64Bit());
uint64_t Start = W.OS.tell();
(void) Start;
W.write<uint32_t>(MachO::LC_LINKER_OPTION);
W.write<uint32_t>(Size);
W.write<uint32_t>(Options.size());
uint64_t BytesWritten = sizeof(MachO::linker_option_command);
for (const std::string &Option : Options) {
W.OS << Option << '\0';
BytesWritten += Option.size() + 1;
}
W.OS.write_zeros(
offsetToAlignment(BytesWritten, is64Bit() ? Align(8) : Align(4)));
assert(W.OS.tell() - Start == Size);
}
static bool isFixupTargetValid(const MCValue &Target) {
if (!Target.getSymA() && Target.getSymB())
return false;
return true;
}
void MachObjectWriter::recordRelocation(MCAssembler &Asm,
const MCAsmLayout &Layout,
const MCFragment *Fragment,
const MCFixup &Fixup, MCValue Target,
uint64_t &FixedValue) {
if (!isFixupTargetValid(Target)) {
Asm.getContext().reportError(Fixup.getLoc(),
"unsupported relocation expression");
return;
}
TargetObjectWriter->recordRelocation(this, Asm, Layout, Fragment, Fixup,
Target, FixedValue);
}
void MachObjectWriter::bindIndirectSymbols(MCAssembler &Asm) {
for (IndirectSymbolData &ISD : llvm::make_range(Asm.indirect_symbol_begin(),
Asm.indirect_symbol_end())) {
const MCSectionMachO &Section = cast<MCSectionMachO>(*ISD.Section);
if (Section.getType() != MachO::S_NON_LAZY_SYMBOL_POINTERS &&
Section.getType() != MachO::S_LAZY_SYMBOL_POINTERS &&
Section.getType() != MachO::S_THREAD_LOCAL_VARIABLE_POINTERS &&
Section.getType() != MachO::S_SYMBOL_STUBS) {
MCSymbol &Symbol = *ISD.Symbol;
report_fatal_error("indirect symbol '" + Symbol.getName() +
"' not in a symbol pointer or stub section");
}
}
unsigned IndirectIndex = 0;
for (MCAssembler::indirect_symbol_iterator it = Asm.indirect_symbol_begin(),
ie = Asm.indirect_symbol_end(); it != ie; ++it, ++IndirectIndex) {
const MCSectionMachO &Section = cast<MCSectionMachO>(*it->Section);
if (Section.getType() != MachO::S_NON_LAZY_SYMBOL_POINTERS &&
Section.getType() != MachO::S_THREAD_LOCAL_VARIABLE_POINTERS)
continue;
IndirectSymBase.insert(std::make_pair(it->Section, IndirectIndex));
Asm.registerSymbol(*it->Symbol);
}
IndirectIndex = 0;
for (MCAssembler::indirect_symbol_iterator it = Asm.indirect_symbol_begin(),
ie = Asm.indirect_symbol_end(); it != ie; ++it, ++IndirectIndex) {
const MCSectionMachO &Section = cast<MCSectionMachO>(*it->Section);
if (Section.getType() != MachO::S_LAZY_SYMBOL_POINTERS &&
Section.getType() != MachO::S_SYMBOL_STUBS)
continue;
IndirectSymBase.insert(std::make_pair(it->Section, IndirectIndex));
bool Created;
Asm.registerSymbol(*it->Symbol, &Created);
if (Created)
cast<MCSymbolMachO>(it->Symbol)->setReferenceTypeUndefinedLazy(true);
}
}
void MachObjectWriter::computeSymbolTable(
MCAssembler &Asm, std::vector<MachSymbolData> &LocalSymbolData,
std::vector<MachSymbolData> &ExternalSymbolData,
std::vector<MachSymbolData> &UndefinedSymbolData) {
DenseMap<const MCSection*, uint8_t> SectionIndexMap;
unsigned Index = 1;
for (MCAssembler::iterator it = Asm.begin(),
ie = Asm.end(); it != ie; ++it, ++Index)
SectionIndexMap[&*it] = Index;
assert(Index <= 256 && "Too many sections!");
for (const MCSymbol &Symbol : Asm.symbols()) {
if (!Asm.isSymbolLinkerVisible(Symbol))
continue;
StringTable.add(Symbol.getName());
}
StringTable.finalize();
for (const MCSymbol &Symbol : Asm.symbols()) {
if (!Asm.isSymbolLinkerVisible(Symbol))
continue;
if (!Symbol.isExternal() && !Symbol.isUndefined())
continue;
MachSymbolData MSD;
MSD.Symbol = &Symbol;
MSD.StringIndex = StringTable.getOffset(Symbol.getName());
if (Symbol.isUndefined()) {
MSD.SectionIndex = 0;
UndefinedSymbolData.push_back(MSD);
} else if (Symbol.isAbsolute()) {
MSD.SectionIndex = 0;
ExternalSymbolData.push_back(MSD);
} else {
MSD.SectionIndex = SectionIndexMap.lookup(&Symbol.getSection());
assert(MSD.SectionIndex && "Invalid section index!");
ExternalSymbolData.push_back(MSD);
}
}
for (const MCSymbol &Symbol : Asm.symbols()) {
if (!Asm.isSymbolLinkerVisible(Symbol))
continue;
if (Symbol.isExternal() || Symbol.isUndefined())
continue;
MachSymbolData MSD;
MSD.Symbol = &Symbol;
MSD.StringIndex = StringTable.getOffset(Symbol.getName());
if (Symbol.isAbsolute()) {
MSD.SectionIndex = 0;
LocalSymbolData.push_back(MSD);
} else {
MSD.SectionIndex = SectionIndexMap.lookup(&Symbol.getSection());
assert(MSD.SectionIndex && "Invalid section index!");
LocalSymbolData.push_back(MSD);
}
}
llvm::sort(ExternalSymbolData);
llvm::sort(UndefinedSymbolData);
Index = 0;
for (auto *SymbolData :
{&LocalSymbolData, &ExternalSymbolData, &UndefinedSymbolData})
for (MachSymbolData &Entry : *SymbolData)
Entry.Symbol->setIndex(Index++);
for (const MCSection &Section : Asm) {
for (RelAndSymbol &Rel : Relocations[&Section]) {
if (!Rel.Sym)
continue;
unsigned Index = Rel.Sym->getIndex();
assert(isInt<24>(Index));
if (W.Endian == support::little)
Rel.MRE.r_word1 = (Rel.MRE.r_word1 & (~0U << 24)) | Index | (1 << 27);
else
Rel.MRE.r_word1 = (Rel.MRE.r_word1 & 0xff) | Index << 8 | (1 << 4);
}
}
}
void MachObjectWriter::computeSectionAddresses(const MCAssembler &Asm,
const MCAsmLayout &Layout) {
uint64_t StartAddress = 0;
for (const MCSection *Sec : Layout.getSectionOrder()) {
StartAddress = alignTo(StartAddress, Sec->getAlignment());
SectionAddress[Sec] = StartAddress;
StartAddress += Layout.getSectionAddressSize(Sec);
StartAddress += getPaddingSize(Sec, Layout);
}
}
void MachObjectWriter::executePostLayoutBinding(MCAssembler &Asm,
const MCAsmLayout &Layout) {
computeSectionAddresses(Asm, Layout);
bindIndirectSymbols(Asm);
}
bool MachObjectWriter::isSymbolRefDifferenceFullyResolvedImpl(
const MCAssembler &Asm, const MCSymbol &A, const MCSymbol &B,
bool InSet) const {
if (A.isVariable() || B.isVariable())
return false;
return MCObjectWriter::isSymbolRefDifferenceFullyResolvedImpl(Asm, A, B,
InSet);
}
bool MachObjectWriter::isSymbolRefDifferenceFullyResolvedImpl(
const MCAssembler &Asm, const MCSymbol &SymA, const MCFragment &FB,
bool InSet, bool IsPCRel) const {
if (InSet)
return true;
const MCSymbol &SA = findAliasedSymbol(SymA);
const MCSection &SecA = SA.getSection();
const MCSection &SecB = *FB.getParent();
if (IsPCRel) {
bool hasReliableSymbolDifference = isX86_64();
if (!hasReliableSymbolDifference) {
if (!SA.isInSection() || &SecA != &SecB ||
(!SA.isTemporary() && FB.getAtom() != SA.getFragment()->getAtom() &&
Asm.getSubsectionsViaSymbols()))
return false;
return true;
}
else if(!FB.getAtom() &&
SA.isTemporary() && SA.isInSection() && &SecA == &SecB){
return true;
}
}
if (&SecA != &SecB)
return false;
const MCFragment *FA = SA.getFragment();
if (!FA)
return false;
if (FA->getAtom() == FB.getAtom())
return true;
return false;
}
static MachO::LoadCommandType getLCFromMCVM(MCVersionMinType Type) {
switch (Type) {
case MCVM_OSXVersionMin: return MachO::LC_VERSION_MIN_MACOSX;
case MCVM_IOSVersionMin: return MachO::LC_VERSION_MIN_IPHONEOS;
case MCVM_TvOSVersionMin: return MachO::LC_VERSION_MIN_TVOS;
case MCVM_WatchOSVersionMin: return MachO::LC_VERSION_MIN_WATCHOS;
}
llvm_unreachable("Invalid mc version min type");
}
void MachObjectWriter::populateAddrSigSection(MCAssembler &Asm) {
MCSection *AddrSigSection =
Asm.getContext().getObjectFileInfo()->getAddrSigSection();
unsigned Log2Size = is64Bit() ? 3 : 2;
for (const MCSymbol *S : getAddrsigSyms()) {
MachO::any_relocation_info MRE;
MRE.r_word0 = 0;
MRE.r_word1 = (Log2Size << 25) | (MachO::GENERIC_RELOC_VANILLA << 28);
addRelocation(S, AddrSigSection, MRE);
}
}
uint64_t MachObjectWriter::writeObject(MCAssembler &Asm,
const MCAsmLayout &Layout) {
uint64_t StartOffset = W.OS.tell();
populateAddrSigSection(Asm);
computeSymbolTable(Asm, LocalSymbolData, ExternalSymbolData,
UndefinedSymbolData);
if (!Asm.CGProfile.empty()) {
MCSection *CGProfileSection = Asm.getContext().getMachOSection(
"__LLVM", "__cg_profile", 0, SectionKind::getMetadata());
MCDataFragment *Frag = dyn_cast_or_null<MCDataFragment>(
&*CGProfileSection->getFragmentList().begin());
assert(Frag && "call graph profile section not reserved");
Frag->getContents().clear();
raw_svector_ostream OS(Frag->getContents());
for (const MCAssembler::CGProfileEntry &CGPE : Asm.CGProfile) {
uint32_t FromIndex = CGPE.From->getSymbol().getIndex();
uint32_t ToIndex = CGPE.To->getSymbol().getIndex();
support::endian::write(OS, FromIndex, W.Endian);
support::endian::write(OS, ToIndex, W.Endian);
support::endian::write(OS, CGPE.Count, W.Endian);
}
}
unsigned NumSections = Asm.size();
const MCAssembler::VersionInfoType &VersionInfo =
Layout.getAssembler().getVersionInfo();
unsigned NumLoadCommands = 1;
uint64_t LoadCommandsSize = is64Bit() ?
sizeof(MachO::segment_command_64) + NumSections * sizeof(MachO::section_64):
sizeof(MachO::segment_command) + NumSections * sizeof(MachO::section);
if (VersionInfo.Major != 0) {
++NumLoadCommands;
if (VersionInfo.EmitBuildVersion)
LoadCommandsSize += sizeof(MachO::build_version_command);
else
LoadCommandsSize += sizeof(MachO::version_min_command);
}
const MCAssembler::VersionInfoType &TargetVariantVersionInfo =
Layout.getAssembler().getDarwinTargetVariantVersionInfo();
if (TargetVariantVersionInfo.Major != 0) {
++NumLoadCommands;
assert(TargetVariantVersionInfo.EmitBuildVersion &&
"target variant should use build version");
LoadCommandsSize += sizeof(MachO::build_version_command);
}
unsigned NumDataRegions = Asm.getDataRegions().size();
if (NumDataRegions) {
++NumLoadCommands;
LoadCommandsSize += sizeof(MachO::linkedit_data_command);
}
uint64_t LOHRawSize = Asm.getLOHContainer().getEmitSize(*this, Layout);
uint64_t LOHSize = alignTo(LOHRawSize, is64Bit() ? 8 : 4);
if (LOHSize) {
++NumLoadCommands;
LoadCommandsSize += sizeof(MachO::linkedit_data_command);
}
unsigned NumSymbols = LocalSymbolData.size() + ExternalSymbolData.size() +
UndefinedSymbolData.size();
if (NumSymbols) {
NumLoadCommands += 2;
LoadCommandsSize += (sizeof(MachO::symtab_command) +
sizeof(MachO::dysymtab_command));
}
for (const auto &Option : Asm.getLinkerOptions()) {
++NumLoadCommands;
LoadCommandsSize += ComputeLinkerOptionsLoadCommandSize(Option, is64Bit());
}
uint64_t SectionDataStart = (is64Bit() ? sizeof(MachO::mach_header_64) :
sizeof(MachO::mach_header)) + LoadCommandsSize;
uint64_t SectionDataSize = 0;
uint64_t SectionDataFileSize = 0;
uint64_t VMSize = 0;
for (const MCSection &Sec : Asm) {
uint64_t Address = getSectionAddress(&Sec);
uint64_t Size = Layout.getSectionAddressSize(&Sec);
uint64_t FileSize = Layout.getSectionFileSize(&Sec);
FileSize += getPaddingSize(&Sec, Layout);
VMSize = std::max(VMSize, Address + Size);
if (Sec.isVirtualSection())
continue;
SectionDataSize = std::max(SectionDataSize, Address + Size);
SectionDataFileSize = std::max(SectionDataFileSize, Address + FileSize);
}
unsigned SectionDataPadding =
offsetToAlignment(SectionDataFileSize, is64Bit() ? Align(8) : Align(4));
SectionDataFileSize += SectionDataPadding;
writeHeader(MachO::MH_OBJECT, NumLoadCommands, LoadCommandsSize,
Asm.getSubsectionsViaSymbols());
uint32_t Prot =
MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE;
writeSegmentLoadCommand("", NumSections, 0, VMSize, SectionDataStart,
SectionDataSize, Prot, Prot);
uint64_t RelocTableEnd = SectionDataStart + SectionDataFileSize;
for (const MCSection &Section : Asm) {
const auto &Sec = cast<MCSectionMachO>(Section);
std::vector<RelAndSymbol> &Relocs = Relocations[&Sec];
unsigned NumRelocs = Relocs.size();
uint64_t SectionStart = SectionDataStart + getSectionAddress(&Sec);
unsigned Flags = Sec.getTypeAndAttributes();
if (Sec.hasInstructions())
Flags |= MachO::S_ATTR_SOME_INSTRUCTIONS;
writeSection(Layout, Sec, getSectionAddress(&Sec), SectionStart, Flags,
RelocTableEnd, NumRelocs);
RelocTableEnd += NumRelocs * sizeof(MachO::any_relocation_info);
}
auto EmitDeploymentTargetVersion =
[&](const MCAssembler::VersionInfoType &VersionInfo) {
auto EncodeVersion = [](VersionTuple V) -> uint32_t {
assert(!V.empty() && "empty version");
unsigned Update = V.getSubminor().value_or(0);
unsigned Minor = V.getMinor().value_or(0);
assert(Update < 256 && "unencodable update target version");
assert(Minor < 256 && "unencodable minor target version");
assert(V.getMajor() < 65536 && "unencodable major target version");
return Update | (Minor << 8) | (V.getMajor() << 16);
};
uint32_t EncodedVersion = EncodeVersion(VersionTuple(
VersionInfo.Major, VersionInfo.Minor, VersionInfo.Update));
uint32_t SDKVersion = !VersionInfo.SDKVersion.empty()
? EncodeVersion(VersionInfo.SDKVersion)
: 0;
if (VersionInfo.EmitBuildVersion) {
W.write<uint32_t>(MachO::LC_BUILD_VERSION);
W.write<uint32_t>(sizeof(MachO::build_version_command));
W.write<uint32_t>(VersionInfo.TypeOrPlatform.Platform);
W.write<uint32_t>(EncodedVersion);
W.write<uint32_t>(SDKVersion);
W.write<uint32_t>(0); } else {
MachO::LoadCommandType LCType =
getLCFromMCVM(VersionInfo.TypeOrPlatform.Type);
W.write<uint32_t>(LCType);
W.write<uint32_t>(sizeof(MachO::version_min_command));
W.write<uint32_t>(EncodedVersion);
W.write<uint32_t>(SDKVersion);
}
};
if (VersionInfo.Major != 0)
EmitDeploymentTargetVersion(VersionInfo);
if (TargetVariantVersionInfo.Major != 0)
EmitDeploymentTargetVersion(TargetVariantVersionInfo);
uint64_t DataInCodeTableEnd = RelocTableEnd + NumDataRegions * 8;
if (NumDataRegions) {
uint64_t DataRegionsOffset = RelocTableEnd;
uint64_t DataRegionsSize = NumDataRegions * 8;
writeLinkeditLoadCommand(MachO::LC_DATA_IN_CODE, DataRegionsOffset,
DataRegionsSize);
}
uint64_t LOHTableEnd = DataInCodeTableEnd + LOHSize;
if (LOHSize)
writeLinkeditLoadCommand(MachO::LC_LINKER_OPTIMIZATION_HINT,
DataInCodeTableEnd, LOHSize);
if (NumSymbols) {
unsigned FirstLocalSymbol = 0;
unsigned NumLocalSymbols = LocalSymbolData.size();
unsigned FirstExternalSymbol = FirstLocalSymbol + NumLocalSymbols;
unsigned NumExternalSymbols = ExternalSymbolData.size();
unsigned FirstUndefinedSymbol = FirstExternalSymbol + NumExternalSymbols;
unsigned NumUndefinedSymbols = UndefinedSymbolData.size();
unsigned NumIndirectSymbols = Asm.indirect_symbol_size();
unsigned NumSymTabSymbols =
NumLocalSymbols + NumExternalSymbols + NumUndefinedSymbols;
uint64_t IndirectSymbolSize = NumIndirectSymbols * 4;
uint64_t IndirectSymbolOffset = 0;
if (NumIndirectSymbols)
IndirectSymbolOffset = LOHTableEnd;
uint64_t SymbolTableOffset = LOHTableEnd + IndirectSymbolSize;
uint64_t StringTableOffset =
SymbolTableOffset + NumSymTabSymbols * (is64Bit() ?
sizeof(MachO::nlist_64) :
sizeof(MachO::nlist));
writeSymtabLoadCommand(SymbolTableOffset, NumSymTabSymbols,
StringTableOffset, StringTable.getSize());
writeDysymtabLoadCommand(FirstLocalSymbol, NumLocalSymbols,
FirstExternalSymbol, NumExternalSymbols,
FirstUndefinedSymbol, NumUndefinedSymbols,
IndirectSymbolOffset, NumIndirectSymbols);
}
for (const auto &Option : Asm.getLinkerOptions())
writeLinkerOptionsLoadCommand(Option);
for (const MCSection &Sec : Asm) {
Asm.writeSectionData(W.OS, &Sec, Layout);
uint64_t Pad = getPaddingSize(&Sec, Layout);
W.OS.write_zeros(Pad);
}
W.OS.write_zeros(SectionDataPadding);
for (const MCSection &Sec : Asm) {
std::vector<RelAndSymbol> &Relocs = Relocations[&Sec];
for (const RelAndSymbol &Rel : llvm::reverse(Relocs)) {
W.write<uint32_t>(Rel.MRE.r_word0);
W.write<uint32_t>(Rel.MRE.r_word1);
}
}
for (MCAssembler::const_data_region_iterator
it = Asm.data_region_begin(), ie = Asm.data_region_end();
it != ie; ++it) {
const DataRegionData *Data = &(*it);
uint64_t Start = getSymbolAddress(*Data->Start, Layout);
uint64_t End;
if (Data->End)
End = getSymbolAddress(*Data->End, Layout);
else
report_fatal_error("Data region not terminated");
LLVM_DEBUG(dbgs() << "data in code region-- kind: " << Data->Kind
<< " start: " << Start << "(" << Data->Start->getName()
<< ")"
<< " end: " << End << "(" << Data->End->getName() << ")"
<< " size: " << End - Start << "\n");
W.write<uint32_t>(Start);
W.write<uint16_t>(End - Start);
W.write<uint16_t>(Data->Kind);
}
if (LOHSize) {
#ifndef NDEBUG
unsigned Start = W.OS.tell();
#endif
Asm.getLOHContainer().emit(*this, Layout);
W.OS.write_zeros(
offsetToAlignment(LOHRawSize, is64Bit() ? Align(8) : Align(4)));
assert(W.OS.tell() - Start == LOHSize);
}
if (NumSymbols) {
for (MCAssembler::const_indirect_symbol_iterator
it = Asm.indirect_symbol_begin(),
ie = Asm.indirect_symbol_end(); it != ie; ++it) {
const MCSectionMachO &Section =
static_cast<const MCSectionMachO &>(*it->Section);
if (Section.getType() == MachO::S_NON_LAZY_SYMBOL_POINTERS) {
if (it->Symbol->isDefined() && !it->Symbol->isExternal()) {
uint32_t Flags = MachO::INDIRECT_SYMBOL_LOCAL;
if (it->Symbol->isAbsolute())
Flags |= MachO::INDIRECT_SYMBOL_ABS;
W.write<uint32_t>(Flags);
continue;
}
}
W.write<uint32_t>(it->Symbol->getIndex());
}
for (auto *SymbolData :
{&LocalSymbolData, &ExternalSymbolData, &UndefinedSymbolData})
for (MachSymbolData &Entry : *SymbolData)
writeNlist(Entry, Layout);
StringTable.write(W.OS);
}
return W.OS.tell() - StartOffset;
}
std::unique_ptr<MCObjectWriter>
llvm::createMachObjectWriter(std::unique_ptr<MCMachObjectTargetWriter> MOTW,
raw_pwrite_stream &OS, bool IsLittleEndian) {
return std::make_unique<MachObjectWriter>(std::move(MOTW), OS,
IsLittleEndian);
}