#include "llvm/BinaryFormat/XCOFF.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAsmLayout.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCFixup.h"
#include "llvm/MC/MCFixupKindInfo.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCSectionXCOFF.h"
#include "llvm/MC/MCSymbolXCOFF.h"
#include "llvm/MC/MCValue.h"
#include "llvm/MC/MCXCOFFObjectWriter.h"
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/EndianStream.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MathExtras.h"
#include <deque>
using namespace llvm;
namespace {
constexpr unsigned DefaultSectionAlign = 4;
constexpr int16_t MaxSectionIndex = INT16_MAX;
uint8_t getEncodedType(const MCSectionXCOFF *);
struct XCOFFRelocation {
uint32_t SymbolTableIndex;
uint32_t FixupOffsetInCsect;
uint8_t SignAndSize;
uint8_t Type;
};
struct Symbol {
const MCSymbolXCOFF *const MCSym;
uint32_t SymbolTableIndex;
XCOFF::VisibilityType getVisibilityType() const {
return MCSym->getVisibilityType();
}
XCOFF::StorageClass getStorageClass() const {
return MCSym->getStorageClass();
}
StringRef getSymbolTableName() const { return MCSym->getSymbolTableName(); }
Symbol(const MCSymbolXCOFF *MCSym) : MCSym(MCSym), SymbolTableIndex(-1) {}
};
struct XCOFFSection {
const MCSectionXCOFF *const MCSec;
uint32_t SymbolTableIndex;
uint64_t Address;
uint64_t Size;
SmallVector<Symbol, 1> Syms;
SmallVector<XCOFFRelocation, 1> Relocations;
StringRef getSymbolTableName() const { return MCSec->getSymbolTableName(); }
XCOFF::VisibilityType getVisibilityType() const {
return MCSec->getVisibilityType();
}
XCOFFSection(const MCSectionXCOFF *MCSec)
: MCSec(MCSec), SymbolTableIndex(-1), Address(-1), Size(0) {}
};
using CsectGroup = std::deque<XCOFFSection>;
using CsectGroups = std::deque<CsectGroup *>;
struct SectionEntry {
char Name[XCOFF::NameSize];
uint64_t Address;
uint64_t Size;
uint64_t FileOffsetToData;
uint64_t FileOffsetToRelocations;
uint32_t RelocationCount;
int32_t Flags;
int16_t Index;
static constexpr int16_t UninitializedIndex =
XCOFF::ReservedSectionNum::N_DEBUG - 1;
SectionEntry(StringRef N, int32_t Flags)
: Name(), Address(0), Size(0), FileOffsetToData(0),
FileOffsetToRelocations(0), RelocationCount(0), Flags(Flags),
Index(UninitializedIndex) {
assert(N.size() <= XCOFF::NameSize && "section name too long");
memcpy(Name, N.data(), N.size());
}
virtual void reset() {
Address = 0;
Size = 0;
FileOffsetToData = 0;
FileOffsetToRelocations = 0;
RelocationCount = 0;
Index = UninitializedIndex;
}
virtual ~SectionEntry() = default;
};
struct CsectSectionEntry : public SectionEntry {
const bool IsVirtual;
CsectGroups Groups;
CsectSectionEntry(StringRef N, XCOFF::SectionTypeFlags Flags, bool IsVirtual,
CsectGroups Groups)
: SectionEntry(N, Flags), IsVirtual(IsVirtual), Groups(Groups) {
assert(N.size() <= XCOFF::NameSize && "section name too long");
memcpy(Name, N.data(), N.size());
}
void reset() override {
SectionEntry::reset();
for (auto *Group : Groups)
Group->clear();
}
virtual ~CsectSectionEntry() = default;
};
struct DwarfSectionEntry : public SectionEntry {
std::unique_ptr<XCOFFSection> DwarfSect;
uint32_t MemorySize;
DwarfSectionEntry(StringRef N, int32_t Flags,
std::unique_ptr<XCOFFSection> Sect)
: SectionEntry(N, Flags | XCOFF::STYP_DWARF), DwarfSect(std::move(Sect)),
MemorySize(0) {
assert(DwarfSect->MCSec->isDwarfSect() &&
"This should be a DWARF section!");
assert(N.size() <= XCOFF::NameSize && "section name too long");
memcpy(Name, N.data(), N.size());
}
DwarfSectionEntry(DwarfSectionEntry &&s) = default;
virtual ~DwarfSectionEntry() = default;
};
class XCOFFObjectWriter : public MCObjectWriter {
uint32_t SymbolTableEntryCount = 0;
uint64_t SymbolTableOffset = 0;
uint16_t SectionCount = 0;
uint64_t RelocationEntryOffset = 0;
std::vector<std::pair<std::string, size_t>> FileNames;
bool HasVisibility = false;
support::endian::Writer W;
std::unique_ptr<MCXCOFFObjectTargetWriter> TargetObjectWriter;
StringTableBuilder Strings;
const uint64_t MaxRawDataSize =
TargetObjectWriter->is64Bit() ? UINT64_MAX : UINT32_MAX;
DenseMap<const MCSectionXCOFF *, XCOFFSection *> SectionMap;
DenseMap<const MCSymbol *, uint32_t> SymbolIndexMap;
CsectGroup UndefinedCsects;
CsectGroup ProgramCodeCsects;
CsectGroup ReadOnlyCsects;
CsectGroup DataCsects;
CsectGroup FuncDSCsects;
CsectGroup TOCCsects;
CsectGroup BSSCsects;
CsectGroup TDataCsects;
CsectGroup TBSSCsects;
CsectSectionEntry Text;
CsectSectionEntry Data;
CsectSectionEntry BSS;
CsectSectionEntry TData;
CsectSectionEntry TBSS;
std::array<CsectSectionEntry *const, 5> Sections{
{&Text, &Data, &BSS, &TData, &TBSS}};
std::vector<DwarfSectionEntry> DwarfSections;
CsectGroup &getCsectGroup(const MCSectionXCOFF *MCSec);
void reset() override;
void executePostLayoutBinding(MCAssembler &, const MCAsmLayout &) override;
void recordRelocation(MCAssembler &, const MCAsmLayout &, const MCFragment *,
const MCFixup &, MCValue, uint64_t &) override;
uint64_t writeObject(MCAssembler &, const MCAsmLayout &) override;
bool is64Bit() const { return TargetObjectWriter->is64Bit(); }
bool nameShouldBeInStringTable(const StringRef &);
void writeSymbolName(const StringRef &);
void writeSymbolEntryForCsectMemberLabel(const Symbol &SymbolRef,
const XCOFFSection &CSectionRef,
int16_t SectionIndex,
uint64_t SymbolOffset);
void writeSymbolEntryForControlSection(const XCOFFSection &CSectionRef,
int16_t SectionIndex,
XCOFF::StorageClass StorageClass);
void writeSymbolEntryForDwarfSection(const XCOFFSection &DwarfSectionRef,
int16_t SectionIndex);
void writeFileHeader();
void writeAuxFileHeader();
void writeSectionHeaderTable();
void writeSections(const MCAssembler &Asm, const MCAsmLayout &Layout);
void writeSectionForControlSectionEntry(const MCAssembler &Asm,
const MCAsmLayout &Layout,
const CsectSectionEntry &CsectEntry,
uint64_t &CurrentAddressLocation);
void writeSectionForDwarfSectionEntry(const MCAssembler &Asm,
const MCAsmLayout &Layout,
const DwarfSectionEntry &DwarfEntry,
uint64_t &CurrentAddressLocation);
void writeSymbolTable(const MCAsmLayout &Layout);
void writeSymbolAuxDwarfEntry(uint64_t LengthOfSectionPortion,
uint64_t NumberOfRelocEnt = 0);
void writeSymbolAuxCsectEntry(uint64_t SectionOrLength,
uint8_t SymbolAlignmentAndType,
uint8_t StorageMappingClass);
void writeSymbolEntry(StringRef SymbolName, uint64_t Value,
int16_t SectionNumber, uint16_t SymbolType,
uint8_t StorageClass, uint8_t NumberOfAuxEntries = 1);
void writeRelocations();
void writeRelocation(XCOFFRelocation Reloc, const XCOFFSection &Section);
void assignAddressesAndIndices(const MCAsmLayout &);
void finalizeSectionInfo();
size_t auxiliaryHeaderSize() const {
return HasVisibility && !is64Bit() ? XCOFF::AuxFileHeaderSizeShort : 0;
}
public:
XCOFFObjectWriter(std::unique_ptr<MCXCOFFObjectTargetWriter> MOTW,
raw_pwrite_stream &OS);
void writeWord(uint64_t Word) {
is64Bit() ? W.write<uint64_t>(Word) : W.write<uint32_t>(Word);
}
};
XCOFFObjectWriter::XCOFFObjectWriter(
std::unique_ptr<MCXCOFFObjectTargetWriter> MOTW, raw_pwrite_stream &OS)
: W(OS, support::big), TargetObjectWriter(std::move(MOTW)),
Strings(StringTableBuilder::XCOFF),
Text(".text", XCOFF::STYP_TEXT, false,
CsectGroups{&ProgramCodeCsects, &ReadOnlyCsects}),
Data(".data", XCOFF::STYP_DATA, false,
CsectGroups{&DataCsects, &FuncDSCsects, &TOCCsects}),
BSS(".bss", XCOFF::STYP_BSS, true,
CsectGroups{&BSSCsects}),
TData(".tdata", XCOFF::STYP_TDATA, false,
CsectGroups{&TDataCsects}),
TBSS(".tbss", XCOFF::STYP_TBSS, true,
CsectGroups{&TBSSCsects}) {}
void XCOFFObjectWriter::reset() {
SymbolIndexMap.clear();
SectionMap.clear();
UndefinedCsects.clear();
for (auto *Sec : Sections)
Sec->reset();
for (auto &DwarfSec : DwarfSections)
DwarfSec.reset();
SymbolTableEntryCount = 0;
SymbolTableOffset = 0;
SectionCount = 0;
RelocationEntryOffset = 0;
Strings.clear();
MCObjectWriter::reset();
}
CsectGroup &XCOFFObjectWriter::getCsectGroup(const MCSectionXCOFF *MCSec) {
switch (MCSec->getMappingClass()) {
case XCOFF::XMC_PR:
assert(XCOFF::XTY_SD == MCSec->getCSectType() &&
"Only an initialized csect can contain program code.");
return ProgramCodeCsects;
case XCOFF::XMC_RO:
assert(XCOFF::XTY_SD == MCSec->getCSectType() &&
"Only an initialized csect can contain read only data.");
return ReadOnlyCsects;
case XCOFF::XMC_RW:
if (XCOFF::XTY_CM == MCSec->getCSectType())
return BSSCsects;
if (XCOFF::XTY_SD == MCSec->getCSectType())
return DataCsects;
report_fatal_error("Unhandled mapping of read-write csect to section.");
case XCOFF::XMC_DS:
return FuncDSCsects;
case XCOFF::XMC_BS:
assert(XCOFF::XTY_CM == MCSec->getCSectType() &&
"Mapping invalid csect. CSECT with bss storage class must be "
"common type.");
return BSSCsects;
case XCOFF::XMC_TL:
assert(XCOFF::XTY_SD == MCSec->getCSectType() &&
"Mapping invalid csect. CSECT with tdata storage class must be "
"an initialized csect.");
return TDataCsects;
case XCOFF::XMC_UL:
assert(XCOFF::XTY_CM == MCSec->getCSectType() &&
"Mapping invalid csect. CSECT with tbss storage class must be "
"an uninitialized csect.");
return TBSSCsects;
case XCOFF::XMC_TC0:
assert(XCOFF::XTY_SD == MCSec->getCSectType() &&
"Only an initialized csect can contain TOC-base.");
assert(TOCCsects.empty() &&
"We should have only one TOC-base, and it should be the first csect "
"in this CsectGroup.");
return TOCCsects;
case XCOFF::XMC_TC:
case XCOFF::XMC_TE:
assert(XCOFF::XTY_SD == MCSec->getCSectType() &&
"Only an initialized csect can contain TC entry.");
assert(!TOCCsects.empty() &&
"We should at least have a TOC-base in this CsectGroup.");
return TOCCsects;
case XCOFF::XMC_TD:
report_fatal_error("toc-data not yet supported when writing object files.");
default:
report_fatal_error("Unhandled mapping of csect to section.");
}
}
static MCSectionXCOFF *getContainingCsect(const MCSymbolXCOFF *XSym) {
if (XSym->isDefined())
return cast<MCSectionXCOFF>(XSym->getFragment()->getParent());
return XSym->getRepresentedCsect();
}
void XCOFFObjectWriter::executePostLayoutBinding(MCAssembler &Asm,
const MCAsmLayout &Layout) {
for (const auto &S : Asm) {
const auto *MCSec = cast<const MCSectionXCOFF>(&S);
assert(SectionMap.find(MCSec) == SectionMap.end() &&
"Cannot add a section twice.");
if (nameShouldBeInStringTable(MCSec->getSymbolTableName()))
Strings.add(MCSec->getSymbolTableName());
if (MCSec->isCsect()) {
assert(XCOFF::XTY_ER != MCSec->getCSectType() &&
"An undefined csect should not get registered.");
CsectGroup &Group = getCsectGroup(MCSec);
Group.emplace_back(MCSec);
SectionMap[MCSec] = &Group.back();
} else if (MCSec->isDwarfSect()) {
std::unique_ptr<XCOFFSection> DwarfSec =
std::make_unique<XCOFFSection>(MCSec);
SectionMap[MCSec] = DwarfSec.get();
DwarfSectionEntry SecEntry(MCSec->getName(),
*MCSec->getDwarfSubtypeFlags(),
std::move(DwarfSec));
DwarfSections.push_back(std::move(SecEntry));
} else
llvm_unreachable("unsupport section type!");
}
for (const MCSymbol &S : Asm.symbols()) {
if (S.isTemporary())
continue;
const MCSymbolXCOFF *XSym = cast<MCSymbolXCOFF>(&S);
const MCSectionXCOFF *ContainingCsect = getContainingCsect(XSym);
if (XSym->getVisibilityType() != XCOFF::SYM_V_UNSPECIFIED)
HasVisibility = true;
if (ContainingCsect->getCSectType() == XCOFF::XTY_ER) {
UndefinedCsects.emplace_back(ContainingCsect);
SectionMap[ContainingCsect] = &UndefinedCsects.back();
if (nameShouldBeInStringTable(ContainingCsect->getSymbolTableName()))
Strings.add(ContainingCsect->getSymbolTableName());
continue;
}
if (XSym == ContainingCsect->getQualNameSymbol())
continue;
if (!XSym->isExternal())
continue;
assert(SectionMap.find(ContainingCsect) != SectionMap.end() &&
"Expected containing csect to exist in map");
XCOFFSection *Csect = SectionMap[ContainingCsect];
assert(Csect->MCSec->isCsect() && "only csect is supported now!");
Csect->Syms.emplace_back(XSym);
if (nameShouldBeInStringTable(XSym->getSymbolTableName()))
Strings.add(XSym->getSymbolTableName());
}
FileNames = Asm.getFileNames();
if (FileNames.empty())
FileNames.emplace_back(".file", 0);
for (const std::pair<std::string, size_t> &F : FileNames) {
if (nameShouldBeInStringTable(F.first))
Strings.add(F.first);
}
Strings.finalize();
assignAddressesAndIndices(Layout);
}
void XCOFFObjectWriter::recordRelocation(MCAssembler &Asm,
const MCAsmLayout &Layout,
const MCFragment *Fragment,
const MCFixup &Fixup, MCValue Target,
uint64_t &FixedValue) {
auto getIndex = [this](const MCSymbol *Sym,
const MCSectionXCOFF *ContainingCsect) {
return SymbolIndexMap.find(Sym) != SymbolIndexMap.end()
? SymbolIndexMap[Sym]
: SymbolIndexMap[ContainingCsect->getQualNameSymbol()];
};
auto getVirtualAddress =
[this, &Layout](const MCSymbol *Sym,
const MCSectionXCOFF *ContainingSect) -> uint64_t {
if (ContainingSect->isDwarfSect())
return Layout.getSymbolOffset(*Sym);
if (!Sym->isDefined())
return SectionMap[ContainingSect]->Address;
assert(Sym->isDefined() && "not a valid object that has address!");
return SectionMap[ContainingSect]->Address + Layout.getSymbolOffset(*Sym);
};
const MCSymbol *const SymA = &Target.getSymA()->getSymbol();
MCAsmBackend &Backend = Asm.getBackend();
bool IsPCRel = Backend.getFixupKindInfo(Fixup.getKind()).Flags &
MCFixupKindInfo::FKF_IsPCRel;
uint8_t Type;
uint8_t SignAndSize;
std::tie(Type, SignAndSize) =
TargetObjectWriter->getRelocTypeAndSignSize(Target, Fixup, IsPCRel);
const MCSectionXCOFF *SymASec = getContainingCsect(cast<MCSymbolXCOFF>(SymA));
if (SymASec->isCsect() && SymASec->getMappingClass() == XCOFF::XMC_TD)
report_fatal_error("toc-data not yet supported when writing object files.");
assert(SectionMap.find(SymASec) != SectionMap.end() &&
"Expected containing csect to exist in map.");
const uint32_t Index = getIndex(SymA, SymASec);
if (Type == XCOFF::RelocationType::R_POS ||
Type == XCOFF::RelocationType::R_TLS)
FixedValue = getVirtualAddress(SymA, SymASec) + Target.getConstant();
else if (Type == XCOFF::RelocationType::R_TLSM)
FixedValue = 0;
else if (Type == XCOFF::RelocationType::R_TOC ||
Type == XCOFF::RelocationType::R_TOCL) {
const int64_t TOCEntryOffset = SectionMap[SymASec]->Address -
TOCCsects.front().Address +
Target.getConstant();
if (Type == XCOFF::RelocationType::R_TOC && !isInt<16>(TOCEntryOffset))
report_fatal_error("TOCEntryOffset overflows in small code model mode");
FixedValue = TOCEntryOffset;
}
assert((Fixup.getOffset() <=
MaxRawDataSize - Layout.getFragmentOffset(Fragment)) &&
"Fragment offset + fixup offset is overflowed.");
uint32_t FixupOffsetInCsect =
Layout.getFragmentOffset(Fragment) + Fixup.getOffset();
XCOFFRelocation Reloc = {Index, FixupOffsetInCsect, SignAndSize, Type};
MCSectionXCOFF *RelocationSec = cast<MCSectionXCOFF>(Fragment->getParent());
assert(SectionMap.find(RelocationSec) != SectionMap.end() &&
"Expected containing csect to exist in map.");
SectionMap[RelocationSec]->Relocations.push_back(Reloc);
if (!Target.getSymB())
return;
const MCSymbol *const SymB = &Target.getSymB()->getSymbol();
if (SymA == SymB)
report_fatal_error("relocation for opposite term is not yet supported");
const MCSectionXCOFF *SymBSec = getContainingCsect(cast<MCSymbolXCOFF>(SymB));
assert(SectionMap.find(SymBSec) != SectionMap.end() &&
"Expected containing csect to exist in map.");
if (SymASec == SymBSec)
report_fatal_error(
"relocation for paired relocatable term is not yet supported");
assert(Type == XCOFF::RelocationType::R_POS &&
"SymA must be R_POS here if it's not opposite term or paired "
"relocatable term.");
const uint32_t IndexB = getIndex(SymB, SymBSec);
const uint8_t TypeB = XCOFF::RelocationType::R_NEG;
XCOFFRelocation RelocB = {IndexB, FixupOffsetInCsect, SignAndSize, TypeB};
SectionMap[RelocationSec]->Relocations.push_back(RelocB);
FixedValue -= getVirtualAddress(SymB, SymBSec);
}
void XCOFFObjectWriter::writeSections(const MCAssembler &Asm,
const MCAsmLayout &Layout) {
uint64_t CurrentAddressLocation = 0;
for (const auto *Section : Sections)
writeSectionForControlSectionEntry(Asm, Layout, *Section,
CurrentAddressLocation);
for (const auto &DwarfSection : DwarfSections)
writeSectionForDwarfSectionEntry(Asm, Layout, DwarfSection,
CurrentAddressLocation);
}
uint64_t XCOFFObjectWriter::writeObject(MCAssembler &Asm,
const MCAsmLayout &Layout) {
if (Asm.isIncrementalLinkerCompatible())
report_fatal_error("Incremental linking not supported for XCOFF.");
finalizeSectionInfo();
uint64_t StartOffset = W.OS.tell();
writeFileHeader();
writeAuxFileHeader();
writeSectionHeaderTable();
writeSections(Asm, Layout);
writeRelocations();
writeSymbolTable(Layout);
Strings.write(W.OS);
return W.OS.tell() - StartOffset;
}
bool XCOFFObjectWriter::nameShouldBeInStringTable(const StringRef &SymbolName) {
return SymbolName.size() > XCOFF::NameSize || is64Bit();
}
void XCOFFObjectWriter::writeSymbolName(const StringRef &SymbolName) {
if (nameShouldBeInStringTable(SymbolName)) {
W.write<int32_t>(0);
W.write<uint32_t>(Strings.getOffset(SymbolName));
} else {
char Name[XCOFF::NameSize + 1];
std::strncpy(Name, SymbolName.data(), XCOFF::NameSize);
ArrayRef<char> NameRef(Name, XCOFF::NameSize);
W.write(NameRef);
}
}
void XCOFFObjectWriter::writeSymbolEntry(StringRef SymbolName, uint64_t Value,
int16_t SectionNumber,
uint16_t SymbolType,
uint8_t StorageClass,
uint8_t NumberOfAuxEntries) {
if (is64Bit()) {
W.write<uint64_t>(Value);
W.write<uint32_t>(Strings.getOffset(SymbolName));
} else {
writeSymbolName(SymbolName);
W.write<uint32_t>(Value);
}
W.write<int16_t>(SectionNumber);
W.write<uint16_t>(SymbolType);
W.write<uint8_t>(StorageClass);
W.write<uint8_t>(NumberOfAuxEntries);
}
void XCOFFObjectWriter::writeSymbolAuxCsectEntry(uint64_t SectionOrLength,
uint8_t SymbolAlignmentAndType,
uint8_t StorageMappingClass) {
W.write<uint32_t>(is64Bit() ? Lo_32(SectionOrLength) : SectionOrLength);
W.write<uint32_t>(0); W.write<uint16_t>(0); W.write<uint8_t>(SymbolAlignmentAndType);
W.write<uint8_t>(StorageMappingClass);
if (is64Bit()) {
W.write<uint32_t>(Hi_32(SectionOrLength));
W.OS.write_zeros(1); W.write<uint8_t>(XCOFF::AUX_CSECT);
} else {
W.write<uint32_t>(0); W.write<uint16_t>(0); }
}
void XCOFFObjectWriter::writeSymbolAuxDwarfEntry(
uint64_t LengthOfSectionPortion, uint64_t NumberOfRelocEnt) {
writeWord(LengthOfSectionPortion);
if (!is64Bit())
W.OS.write_zeros(4); writeWord(NumberOfRelocEnt);
if (is64Bit()) {
W.OS.write_zeros(1); W.write<uint8_t>(XCOFF::AUX_SECT);
} else {
W.OS.write_zeros(6); }
}
void XCOFFObjectWriter::writeSymbolEntryForCsectMemberLabel(
const Symbol &SymbolRef, const XCOFFSection &CSectionRef,
int16_t SectionIndex, uint64_t SymbolOffset) {
assert(SymbolOffset <= MaxRawDataSize - CSectionRef.Address &&
"Symbol address overflowed.");
writeSymbolEntry(SymbolRef.getSymbolTableName(),
CSectionRef.Address + SymbolOffset, SectionIndex,
SymbolRef.getVisibilityType(), SymbolRef.getStorageClass());
writeSymbolAuxCsectEntry(CSectionRef.SymbolTableIndex, XCOFF::XTY_LD,
CSectionRef.MCSec->getMappingClass());
}
void XCOFFObjectWriter::writeSymbolEntryForDwarfSection(
const XCOFFSection &DwarfSectionRef, int16_t SectionIndex) {
assert(DwarfSectionRef.MCSec->isDwarfSect() && "Not a DWARF section!");
writeSymbolEntry(DwarfSectionRef.getSymbolTableName(), 0,
SectionIndex, 0, XCOFF::C_DWARF);
writeSymbolAuxDwarfEntry(DwarfSectionRef.Size);
}
void XCOFFObjectWriter::writeSymbolEntryForControlSection(
const XCOFFSection &CSectionRef, int16_t SectionIndex,
XCOFF::StorageClass StorageClass) {
writeSymbolEntry(CSectionRef.getSymbolTableName(), CSectionRef.Address,
SectionIndex, CSectionRef.getVisibilityType(), StorageClass);
writeSymbolAuxCsectEntry(CSectionRef.Size, getEncodedType(CSectionRef.MCSec),
CSectionRef.MCSec->getMappingClass());
}
void XCOFFObjectWriter::writeFileHeader() {
W.write<uint16_t>(is64Bit() ? XCOFF::XCOFF64 : XCOFF::XCOFF32);
W.write<uint16_t>(SectionCount);
W.write<int32_t>(0); writeWord(SymbolTableOffset);
if (is64Bit()) {
W.write<uint16_t>(auxiliaryHeaderSize());
W.write<uint16_t>(0); W.write<int32_t>(SymbolTableEntryCount);
} else {
W.write<int32_t>(SymbolTableEntryCount);
W.write<uint16_t>(auxiliaryHeaderSize());
W.write<uint16_t>(0); }
}
void XCOFFObjectWriter::writeAuxFileHeader() {
if (!auxiliaryHeaderSize())
return;
W.write<uint16_t>(0); W.write<uint16_t>(
XCOFF::NEW_XCOFF_INTERPRET); W.write<uint32_t>(Sections[0]->Size); W.write<uint32_t>(Sections[1]->Size); W.write<uint32_t>(Sections[2]->Size); W.write<uint32_t>(0); W.write<uint32_t>(Sections[0]->Address); W.write<uint32_t>(Sections[1]->Address); }
void XCOFFObjectWriter::writeSectionHeaderTable() {
auto writeSectionHeader = [&](const SectionEntry *Sec, bool IsDwarf) {
if (Sec->Index == SectionEntry::UninitializedIndex)
return false;
ArrayRef<char> NameRef(Sec->Name, XCOFF::NameSize);
W.write(NameRef);
writeWord(IsDwarf ? 0 : Sec->Address);
writeWord(IsDwarf ? 0 : Sec->Address);
writeWord(Sec->Size);
writeWord(Sec->FileOffsetToData);
writeWord(Sec->FileOffsetToRelocations);
writeWord(0);
if (is64Bit()) {
W.write<uint32_t>(Sec->RelocationCount);
W.write<uint32_t>(0); W.write<int32_t>(Sec->Flags);
W.OS.write_zeros(4);
} else {
W.write<uint16_t>(Sec->RelocationCount);
W.write<uint16_t>(0); W.write<int32_t>(Sec->Flags);
}
return true;
};
for (const auto *CsectSec : Sections)
writeSectionHeader(CsectSec, false);
for (const auto &DwarfSec : DwarfSections)
writeSectionHeader(&DwarfSec, true);
}
void XCOFFObjectWriter::writeRelocation(XCOFFRelocation Reloc,
const XCOFFSection &Section) {
if (Section.MCSec->isCsect())
writeWord(Section.Address + Reloc.FixupOffsetInCsect);
else {
assert(Section.MCSec->isDwarfSect() && "unsupport section type!");
writeWord(Reloc.FixupOffsetInCsect);
}
W.write<uint32_t>(Reloc.SymbolTableIndex);
W.write<uint8_t>(Reloc.SignAndSize);
W.write<uint8_t>(Reloc.Type);
}
void XCOFFObjectWriter::writeRelocations() {
for (const auto *Section : Sections) {
if (Section->Index == SectionEntry::UninitializedIndex)
continue;
for (const auto *Group : Section->Groups) {
if (Group->empty())
continue;
for (const auto &Csect : *Group) {
for (const auto Reloc : Csect.Relocations)
writeRelocation(Reloc, Csect);
}
}
}
for (const auto &DwarfSection : DwarfSections)
for (const auto &Reloc : DwarfSection.DwarfSect->Relocations)
writeRelocation(Reloc, *DwarfSection.DwarfSect);
}
void XCOFFObjectWriter::writeSymbolTable(const MCAsmLayout &Layout) {
for (const std::pair<std::string, size_t> &F : FileNames) {
writeSymbolEntry(F.first, 0, XCOFF::ReservedSectionNum::N_DEBUG,
0, XCOFF::C_FILE,
0);
}
for (const auto &Csect : UndefinedCsects) {
writeSymbolEntryForControlSection(Csect, XCOFF::ReservedSectionNum::N_UNDEF,
Csect.MCSec->getStorageClass());
}
for (const auto *Section : Sections) {
if (Section->Index == SectionEntry::UninitializedIndex)
continue;
for (const auto *Group : Section->Groups) {
if (Group->empty())
continue;
const int16_t SectionIndex = Section->Index;
for (const auto &Csect : *Group) {
writeSymbolEntryForControlSection(Csect, SectionIndex,
Csect.MCSec->getStorageClass());
for (const auto &Sym : Csect.Syms)
writeSymbolEntryForCsectMemberLabel(
Sym, Csect, SectionIndex, Layout.getSymbolOffset(*(Sym.MCSym)));
}
}
}
for (const auto &DwarfSection : DwarfSections)
writeSymbolEntryForDwarfSection(*DwarfSection.DwarfSect,
DwarfSection.Index);
}
void XCOFFObjectWriter::finalizeSectionInfo() {
for (auto *Section : Sections) {
if (Section->Index == SectionEntry::UninitializedIndex)
continue;
for (const auto *Group : Section->Groups) {
if (Group->empty())
continue;
for (auto &Csect : *Group) {
const size_t CsectRelocCount = Csect.Relocations.size();
if (!is64Bit() && (CsectRelocCount >= XCOFF::RelocOverflow ||
Section->RelocationCount >=
XCOFF::RelocOverflow - CsectRelocCount))
report_fatal_error(
"relocation entries overflowed; overflow section is "
"not implemented yet");
Section->RelocationCount += CsectRelocCount;
}
}
}
for (auto &DwarfSection : DwarfSections)
DwarfSection.RelocationCount = DwarfSection.DwarfSect->Relocations.size();
uint64_t RawPointer = RelocationEntryOffset;
auto calcOffsetToRelocations = [&](SectionEntry *Sec, bool IsDwarf) {
if (!IsDwarf && Sec->Index == SectionEntry::UninitializedIndex)
return false;
if (!Sec->RelocationCount)
return false;
Sec->FileOffsetToRelocations = RawPointer;
const uint64_t RelocationSizeInSec =
Sec->RelocationCount * (is64Bit()
? XCOFF::RelocationSerializationSize64
: XCOFF::RelocationSerializationSize32);
RawPointer += RelocationSizeInSec;
if (RawPointer > MaxRawDataSize)
report_fatal_error("Relocation data overflowed this object file.");
return true;
};
for (auto *Sec : Sections)
calcOffsetToRelocations(Sec, false);
for (auto &DwarfSec : DwarfSections)
calcOffsetToRelocations(&DwarfSec, true);
if (SymbolTableEntryCount)
SymbolTableOffset = RawPointer;
}
void XCOFFObjectWriter::assignAddressesAndIndices(const MCAsmLayout &Layout) {
uint32_t SymbolTableIndex = FileNames.size();
for (auto &Csect : UndefinedCsects) {
Csect.Size = 0;
Csect.Address = 0;
Csect.SymbolTableIndex = SymbolTableIndex;
SymbolIndexMap[Csect.MCSec->getQualNameSymbol()] = Csect.SymbolTableIndex;
SymbolTableIndex += 2;
}
uint64_t Address = 0;
int32_t SectionIndex = 1;
bool HasTDataSection = false;
uint32_t PaddingsBeforeDwarf = 0;
for (auto *Section : Sections) {
const bool IsEmpty =
llvm::all_of(Section->Groups,
[](const CsectGroup *Group) { return Group->empty(); });
if (IsEmpty)
continue;
if (SectionIndex > MaxSectionIndex)
report_fatal_error("Section index overflow!");
Section->Index = SectionIndex++;
SectionCount++;
bool SectionAddressSet = false;
if (Section->Flags == XCOFF::STYP_TDATA) {
Address = 0;
HasTDataSection = true;
}
if ((Section->Flags == XCOFF::STYP_TBSS) && !HasTDataSection)
Address = 0;
for (auto *Group : Section->Groups) {
if (Group->empty())
continue;
for (auto &Csect : *Group) {
const MCSectionXCOFF *MCSec = Csect.MCSec;
Csect.Address = alignTo(Address, MCSec->getAlignment());
Csect.Size = Layout.getSectionAddressSize(MCSec);
Address = Csect.Address + Csect.Size;
Csect.SymbolTableIndex = SymbolTableIndex;
SymbolIndexMap[MCSec->getQualNameSymbol()] = Csect.SymbolTableIndex;
SymbolTableIndex += 2;
for (auto &Sym : Csect.Syms) {
Sym.SymbolTableIndex = SymbolTableIndex;
SymbolIndexMap[Sym.MCSym] = Sym.SymbolTableIndex;
SymbolTableIndex += 2;
}
}
if (!SectionAddressSet) {
Section->Address = Group->front().Address;
SectionAddressSet = true;
}
}
Address = alignTo(Address, DefaultSectionAlign);
Section->Size = Address - Section->Address;
}
if (!DwarfSections.empty())
PaddingsBeforeDwarf =
alignTo(Address,
(*DwarfSections.begin()).DwarfSect->MCSec->getAlignment()) -
Address;
DwarfSectionEntry *LastDwarfSection = nullptr;
for (auto &DwarfSection : DwarfSections) {
assert((SectionIndex <= MaxSectionIndex) && "Section index overflow!");
XCOFFSection &DwarfSect = *DwarfSection.DwarfSect;
const MCSectionXCOFF *MCSec = DwarfSect.MCSec;
DwarfSection.Index = SectionIndex++;
SectionCount++;
DwarfSect.SymbolTableIndex = SymbolTableIndex;
SymbolIndexMap[MCSec->getQualNameSymbol()] = DwarfSect.SymbolTableIndex;
SymbolTableIndex += 2;
DwarfSection.Address = DwarfSect.Address =
alignTo(Address, MCSec->getAlignment());
DwarfSection.Size = DwarfSect.Size = Layout.getSectionAddressSize(MCSec);
Address = DwarfSection.Address + DwarfSection.Size;
if (LastDwarfSection)
LastDwarfSection->MemorySize =
DwarfSection.Address - LastDwarfSection->Address;
LastDwarfSection = &DwarfSection;
}
if (LastDwarfSection) {
Address = alignTo(LastDwarfSection->Address + LastDwarfSection->Size,
DefaultSectionAlign);
LastDwarfSection->MemorySize = Address - LastDwarfSection->Address;
}
SymbolTableEntryCount = SymbolTableIndex;
uint64_t RawPointer =
(is64Bit() ? (XCOFF::FileHeaderSize64 +
SectionCount * XCOFF::SectionHeaderSize64)
: (XCOFF::FileHeaderSize32 +
SectionCount * XCOFF::SectionHeaderSize32)) +
auxiliaryHeaderSize();
for (auto *Sec : Sections) {
if (Sec->Index == SectionEntry::UninitializedIndex || Sec->IsVirtual)
continue;
Sec->FileOffsetToData = RawPointer;
RawPointer += Sec->Size;
if (RawPointer > MaxRawDataSize)
report_fatal_error("Section raw data overflowed this object file.");
}
if (!DwarfSections.empty())
RawPointer += PaddingsBeforeDwarf;
for (auto &DwarfSection : DwarfSections) {
DwarfSection.FileOffsetToData = RawPointer;
RawPointer += DwarfSection.MemorySize;
assert(RawPointer <= MaxRawDataSize &&
"Section raw data overflowed this object file.");
}
RelocationEntryOffset = RawPointer;
}
void XCOFFObjectWriter::writeSectionForControlSectionEntry(
const MCAssembler &Asm, const MCAsmLayout &Layout,
const CsectSectionEntry &CsectEntry, uint64_t &CurrentAddressLocation) {
if (CsectEntry.Index == SectionEntry::UninitializedIndex)
return;
assert(((CurrentAddressLocation <= CsectEntry.Address) ||
(CsectEntry.Flags == XCOFF::STYP_TDATA) ||
(CsectEntry.Flags == XCOFF::STYP_TBSS)) &&
"CurrentAddressLocation should be less than or equal to section "
"address if the section is not TData or TBSS.");
CurrentAddressLocation = CsectEntry.Address;
if (CsectEntry.IsVirtual) {
CurrentAddressLocation += CsectEntry.Size;
return;
}
for (const auto &Group : CsectEntry.Groups) {
for (const auto &Csect : *Group) {
if (uint32_t PaddingSize = Csect.Address - CurrentAddressLocation)
W.OS.write_zeros(PaddingSize);
if (Csect.Size)
Asm.writeSectionData(W.OS, Csect.MCSec, Layout);
CurrentAddressLocation = Csect.Address + Csect.Size;
}
}
if (uint64_t PaddingSize =
CsectEntry.Address + CsectEntry.Size - CurrentAddressLocation) {
W.OS.write_zeros(PaddingSize);
CurrentAddressLocation += PaddingSize;
}
}
void XCOFFObjectWriter::writeSectionForDwarfSectionEntry(
const MCAssembler &Asm, const MCAsmLayout &Layout,
const DwarfSectionEntry &DwarfEntry, uint64_t &CurrentAddressLocation) {
assert(CurrentAddressLocation <= DwarfEntry.Address &&
"CurrentAddressLocation should be less than or equal to section "
"address.");
if (uint64_t PaddingSize = DwarfEntry.Address - CurrentAddressLocation)
W.OS.write_zeros(PaddingSize);
if (DwarfEntry.Size)
Asm.writeSectionData(W.OS, DwarfEntry.DwarfSect->MCSec, Layout);
CurrentAddressLocation = DwarfEntry.Address + DwarfEntry.Size;
uint32_t Mod = CurrentAddressLocation % DefaultSectionAlign;
uint32_t TailPaddingSize = Mod ? DefaultSectionAlign - Mod : 0;
if (TailPaddingSize)
W.OS.write_zeros(TailPaddingSize);
CurrentAddressLocation += TailPaddingSize;
}
uint8_t getEncodedType(const MCSectionXCOFF *Sec) {
unsigned Align = Sec->getAlignment();
assert(isPowerOf2_32(Align) && "Alignment must be a power of 2.");
unsigned Log2Align = Log2_32(Align);
uint8_t EncodedAlign = Log2Align << 3;
return EncodedAlign | Sec->getCSectType();
}
}
std::unique_ptr<MCObjectWriter>
llvm::createXCOFFObjectWriter(std::unique_ptr<MCXCOFFObjectTargetWriter> MOTW,
raw_pwrite_stream &OS) {
return std::make_unique<XCOFFObjectWriter>(std::move(MOTW), OS);
}