mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 18:54:02 +01:00
e4aa7692e8
Reviewed By: jsji Differential Revision: https://reviews.llvm.org/D97049
986 lines
36 KiB
C++
986 lines
36 KiB
C++
//===-- lib/MC/XCOFFObjectWriter.cpp - XCOFF file 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements XCOFF object file writer information.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#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/EndianStream.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "llvm/Support/MathExtras.h"
|
|
|
|
#include <deque>
|
|
|
|
using namespace llvm;
|
|
|
|
// An XCOFF object file has a limited set of predefined sections. The most
|
|
// important ones for us (right now) are:
|
|
// .text --> contains program code and read-only data.
|
|
// .data --> contains initialized data, function descriptors, and the TOC.
|
|
// .bss --> contains uninitialized data.
|
|
// Each of these sections is composed of 'Control Sections'. A Control Section
|
|
// is more commonly referred to as a csect. A csect is an indivisible unit of
|
|
// code or data, and acts as a container for symbols. A csect is mapped
|
|
// into a section based on its storage-mapping class, with the exception of
|
|
// XMC_RW which gets mapped to either .data or .bss based on whether it's
|
|
// explicitly initialized or not.
|
|
//
|
|
// We don't represent the sections in the MC layer as there is nothing
|
|
// interesting about them at at that level: they carry information that is
|
|
// only relevant to the ObjectWriter, so we materialize them in this class.
|
|
namespace {
|
|
|
|
constexpr unsigned DefaultSectionAlign = 4;
|
|
constexpr int16_t MaxSectionIndex = INT16_MAX;
|
|
|
|
// Packs the csect's alignment and type into a byte.
|
|
uint8_t getEncodedType(const MCSectionXCOFF *);
|
|
|
|
struct XCOFFRelocation {
|
|
uint32_t SymbolTableIndex;
|
|
uint32_t FixupOffsetInCsect;
|
|
uint8_t SignAndSize;
|
|
uint8_t Type;
|
|
};
|
|
|
|
// Wrapper around an MCSymbolXCOFF.
|
|
struct Symbol {
|
|
const MCSymbolXCOFF *const MCSym;
|
|
uint32_t SymbolTableIndex;
|
|
|
|
XCOFF::StorageClass getStorageClass() const {
|
|
return MCSym->getStorageClass();
|
|
}
|
|
StringRef getSymbolTableName() const { return MCSym->getSymbolTableName(); }
|
|
Symbol(const MCSymbolXCOFF *MCSym) : MCSym(MCSym), SymbolTableIndex(-1) {}
|
|
};
|
|
|
|
// Wrapper for an MCSectionXCOFF.
|
|
// It can be a Csect or debug section or DWARF section and so on.
|
|
struct XCOFFSection {
|
|
const MCSectionXCOFF *const MCSec;
|
|
uint32_t SymbolTableIndex;
|
|
uint32_t Address;
|
|
uint32_t Size;
|
|
|
|
SmallVector<Symbol, 1> Syms;
|
|
SmallVector<XCOFFRelocation, 1> Relocations;
|
|
StringRef getSymbolTableName() const { return MCSec->getSymbolTableName(); }
|
|
XCOFFSection(const MCSectionXCOFF *MCSec)
|
|
: MCSec(MCSec), SymbolTableIndex(-1), Address(-1), Size(0) {}
|
|
};
|
|
|
|
// Type to be used for a container representing a set of csects with
|
|
// (approximately) the same storage mapping class. For example all the csects
|
|
// with a storage mapping class of `xmc_pr` will get placed into the same
|
|
// container.
|
|
using CsectGroup = std::deque<XCOFFSection>;
|
|
using CsectGroups = std::deque<CsectGroup *>;
|
|
|
|
// The basic section entry defination. This Section represents a section entry
|
|
// in XCOFF section header table.
|
|
struct SectionEntry {
|
|
char Name[XCOFF::NameSize];
|
|
// The physical/virtual address of the section. For an object file
|
|
// these values are equivalent.
|
|
uint32_t Address;
|
|
uint32_t Size;
|
|
uint32_t FileOffsetToData;
|
|
uint32_t FileOffsetToRelocations;
|
|
uint32_t RelocationCount;
|
|
int32_t Flags;
|
|
|
|
int16_t Index;
|
|
|
|
// XCOFF has special section numbers for symbols:
|
|
// -2 Specifies N_DEBUG, a special symbolic debugging symbol.
|
|
// -1 Specifies N_ABS, an absolute symbol. The symbol has a value but is not
|
|
// relocatable.
|
|
// 0 Specifies N_UNDEF, an undefined external symbol.
|
|
// Therefore, we choose -3 (N_DEBUG - 1) to represent a section index that
|
|
// hasn't been initialized.
|
|
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() {}
|
|
};
|
|
|
|
// Represents the data related to a section excluding the csects that make up
|
|
// the raw data of the section. The csects are stored separately as not all
|
|
// sections contain csects, and some sections contain csects which are better
|
|
// stored separately, e.g. the .data section containing read-write, descriptor,
|
|
// TOCBase and TOC-entry csects.
|
|
struct CsectSectionEntry : public SectionEntry {
|
|
// Virtual sections do not need storage allocated in the object file.
|
|
const bool IsVirtual;
|
|
|
|
// This is a section containing csect groups.
|
|
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();
|
|
// Clear any csects we have stored.
|
|
for (auto *Group : Groups)
|
|
Group->clear();
|
|
}
|
|
|
|
virtual ~CsectSectionEntry() {}
|
|
};
|
|
|
|
class XCOFFObjectWriter : public MCObjectWriter {
|
|
|
|
uint32_t SymbolTableEntryCount = 0;
|
|
uint32_t SymbolTableOffset = 0;
|
|
uint16_t SectionCount = 0;
|
|
uint32_t RelocationEntryOffset = 0;
|
|
|
|
support::endian::Writer W;
|
|
std::unique_ptr<MCXCOFFObjectTargetWriter> TargetObjectWriter;
|
|
StringTableBuilder Strings;
|
|
|
|
// Maps the MCSection representation to its corresponding XCOFFSection
|
|
// wrapper. Needed for finding the XCOFFSection to insert an MCSymbol into
|
|
// from its containing MCSectionXCOFF.
|
|
DenseMap<const MCSectionXCOFF *, XCOFFSection *> SectionMap;
|
|
|
|
// Maps the MCSymbol representation to its corrresponding symbol table index.
|
|
// Needed for relocation.
|
|
DenseMap<const MCSymbol *, uint32_t> SymbolIndexMap;
|
|
|
|
// CsectGroups. These store the csects which make up different parts of
|
|
// the sections. Should have one for each set of csects that get mapped into
|
|
// the same section and get handled in a 'similar' way.
|
|
CsectGroup UndefinedCsects;
|
|
CsectGroup ProgramCodeCsects;
|
|
CsectGroup ReadOnlyCsects;
|
|
CsectGroup DataCsects;
|
|
CsectGroup FuncDSCsects;
|
|
CsectGroup TOCCsects;
|
|
CsectGroup BSSCsects;
|
|
CsectGroup TDataCsects;
|
|
CsectGroup TBSSCsects;
|
|
|
|
// The Predefined sections.
|
|
CsectSectionEntry Text;
|
|
CsectSectionEntry Data;
|
|
CsectSectionEntry BSS;
|
|
CsectSectionEntry TData;
|
|
CsectSectionEntry TBSS;
|
|
|
|
// All the XCOFF sections, in the order they will appear in the section header
|
|
// table.
|
|
std::array<CsectSectionEntry *const, 5> Sections{
|
|
{&Text, &Data, &BSS, &TData, &TBSS}};
|
|
|
|
CsectGroup &getCsectGroup(const MCSectionXCOFF *MCSec);
|
|
|
|
virtual 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;
|
|
|
|
static bool nameShouldBeInStringTable(const StringRef &);
|
|
void writeSymbolName(const StringRef &);
|
|
void writeSymbolTableEntryForCsectMemberLabel(const Symbol &,
|
|
const XCOFFSection &, int16_t,
|
|
uint64_t);
|
|
void writeSymbolTableEntryForControlSection(const XCOFFSection &, int16_t,
|
|
XCOFF::StorageClass);
|
|
void writeFileHeader();
|
|
void writeSectionHeaderTable();
|
|
void writeSections(const MCAssembler &Asm, const MCAsmLayout &Layout);
|
|
void writeSymbolTable(const MCAsmLayout &Layout);
|
|
void writeRelocations();
|
|
void writeRelocation(XCOFFRelocation Reloc, const XCOFFSection &CSection);
|
|
|
|
// Called after all the csects and symbols have been processed by
|
|
// `executePostLayoutBinding`, this function handles building up the majority
|
|
// of the structures in the object file representation. Namely:
|
|
// *) Calculates physical/virtual addresses, raw-pointer offsets, and section
|
|
// sizes.
|
|
// *) Assigns symbol table indices.
|
|
// *) Builds up the section header table by adding any non-empty sections to
|
|
// `Sections`.
|
|
void assignAddressesAndIndices(const MCAsmLayout &);
|
|
void finalizeSectionInfo();
|
|
|
|
bool
|
|
needsAuxiliaryHeader() const { /* TODO aux header support not implemented. */
|
|
return false;
|
|
}
|
|
|
|
// Returns the size of the auxiliary header to be written to the object file.
|
|
size_t auxiliaryHeaderSize() const {
|
|
assert(!needsAuxiliaryHeader() &&
|
|
"Auxiliary header support not implemented.");
|
|
return 0;
|
|
}
|
|
|
|
public:
|
|
XCOFFObjectWriter(std::unique_ptr<MCXCOFFObjectTargetWriter> MOTW,
|
|
raw_pwrite_stream &OS);
|
|
};
|
|
|
|
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, /* IsVirtual */ false,
|
|
CsectGroups{&ProgramCodeCsects, &ReadOnlyCsects}),
|
|
Data(".data", XCOFF::STYP_DATA, /* IsVirtual */ false,
|
|
CsectGroups{&DataCsects, &FuncDSCsects, &TOCCsects}),
|
|
BSS(".bss", XCOFF::STYP_BSS, /* IsVirtual */ true,
|
|
CsectGroups{&BSSCsects}),
|
|
TData(".tdata", XCOFF::STYP_TDATA, /* IsVirtual */ false,
|
|
CsectGroups{&TDataCsects}),
|
|
TBSS(".tbss", XCOFF::STYP_TBSS, /* IsVirtual */ true,
|
|
CsectGroups{&TBSSCsects}) {}
|
|
|
|
void XCOFFObjectWriter::reset() {
|
|
// Clear the mappings we created.
|
|
SymbolIndexMap.clear();
|
|
SectionMap.clear();
|
|
|
|
UndefinedCsects.clear();
|
|
// Reset any sections we have written to, and empty the section header table.
|
|
for (auto *Sec : Sections)
|
|
Sec->reset();
|
|
|
|
// Reset states in XCOFFObjectWriter.
|
|
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) {
|
|
if (TargetObjectWriter->is64Bit())
|
|
report_fatal_error("64-bit XCOFF object files are not supported yet.");
|
|
|
|
for (const auto &S : Asm) {
|
|
const auto *MCSec = cast<const MCSectionXCOFF>(&S);
|
|
assert(SectionMap.find(MCSec) == SectionMap.end() &&
|
|
"Cannot add a section twice.");
|
|
assert(XCOFF::XTY_ER != MCSec->getCSectType() &&
|
|
"An undefined csect should not get registered.");
|
|
|
|
// If the name does not fit in the storage provided in the symbol table
|
|
// entry, add it to the string table.
|
|
if (nameShouldBeInStringTable(MCSec->getSymbolTableName()))
|
|
Strings.add(MCSec->getSymbolTableName());
|
|
|
|
CsectGroup &Group = getCsectGroup(MCSec);
|
|
Group.emplace_back(MCSec);
|
|
SectionMap[MCSec] = &Group.back();
|
|
}
|
|
|
|
for (const MCSymbol &S : Asm.symbols()) {
|
|
// Nothing to do for temporary symbols.
|
|
if (S.isTemporary())
|
|
continue;
|
|
|
|
const MCSymbolXCOFF *XSym = cast<MCSymbolXCOFF>(&S);
|
|
const MCSectionXCOFF *ContainingCsect = getContainingCsect(XSym);
|
|
|
|
if (ContainingCsect->getCSectType() == XCOFF::XTY_ER) {
|
|
// Handle undefined symbol.
|
|
UndefinedCsects.emplace_back(ContainingCsect);
|
|
SectionMap[ContainingCsect] = &UndefinedCsects.back();
|
|
if (nameShouldBeInStringTable(ContainingCsect->getSymbolTableName()))
|
|
Strings.add(ContainingCsect->getSymbolTableName());
|
|
continue;
|
|
}
|
|
|
|
// If the symbol is the csect itself, we don't need to put the symbol
|
|
// into csect's Syms.
|
|
if (XSym == ContainingCsect->getQualNameSymbol())
|
|
continue;
|
|
|
|
// Only put a label into the symbol table when it is an external label.
|
|
if (!XSym->isExternal())
|
|
continue;
|
|
|
|
assert(SectionMap.find(ContainingCsect) != SectionMap.end() &&
|
|
"Expected containing csect to exist in map");
|
|
XCOFFSection *Csect = SectionMap[ContainingCsect];
|
|
// Lookup the containing csect and add the symbol to it.
|
|
assert(Csect->MCSec->isCsect() && "only csect is supported now!");
|
|
Csect->Syms.emplace_back(XSym);
|
|
|
|
// If the name does not fit in the storage provided in the symbol table
|
|
// entry, add it to the string table.
|
|
if (nameShouldBeInStringTable(XSym->getSymbolTableName()))
|
|
Strings.add(XSym->getSymbolTableName());
|
|
}
|
|
|
|
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) {
|
|
// If we could not find the symbol directly in SymbolIndexMap, this symbol
|
|
// could either be a temporary symbol or an undefined symbol. In this case,
|
|
// we would need to have the relocation reference its csect instead.
|
|
return SymbolIndexMap.find(Sym) != SymbolIndexMap.end()
|
|
? SymbolIndexMap[Sym]
|
|
: SymbolIndexMap[ContainingCsect->getQualNameSymbol()];
|
|
};
|
|
|
|
auto getVirtualAddress = [this,
|
|
&Layout](const MCSymbol *Sym,
|
|
const MCSectionXCOFF *ContainingCsect) {
|
|
// If Sym is a csect, return csect's address.
|
|
// If Sym is a label, return csect's address + label's offset from the csect.
|
|
return SectionMap[ContainingCsect]->Address +
|
|
(Sym->isDefined() ? Layout.getSymbolOffset(*Sym) : 0);
|
|
};
|
|
|
|
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)
|
|
// The FixedValue should be symbol's virtual address in this object file
|
|
// plus any constant value that we might get.
|
|
FixedValue = getVirtualAddress(SymA, SymASec) + Target.getConstant();
|
|
else if (Type == XCOFF::RelocationType::R_TLSM)
|
|
// The FixedValue should always be zero since the region handle is only
|
|
// known at load time.
|
|
FixedValue = 0;
|
|
else if (Type == XCOFF::RelocationType::R_TOC ||
|
|
Type == XCOFF::RelocationType::R_TOCL) {
|
|
// The FixedValue should be the TOC entry offset from the TOC-base plus any
|
|
// constant offset value.
|
|
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(
|
|
(TargetObjectWriter->is64Bit() ||
|
|
Fixup.getOffset() <= UINT32_MAX - Layout.getFragmentOffset(Fragment)) &&
|
|
"Fragment offset + fixup offset is overflowed in 32-bit mode.");
|
|
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);
|
|
// SymB must be R_NEG here, given the general form of Target(MCValue) is
|
|
// "SymbolA - SymbolB + imm64".
|
|
const uint8_t TypeB = XCOFF::RelocationType::R_NEG;
|
|
XCOFFRelocation RelocB = {IndexB, FixupOffsetInCsect, SignAndSize, TypeB};
|
|
SectionMap[RelocationSec]->Relocations.push_back(RelocB);
|
|
// We already folded "SymbolA + imm64" above when Type is R_POS for SymbolA,
|
|
// now we just need to fold "- SymbolB" here.
|
|
FixedValue -= getVirtualAddress(SymB, SymBSec);
|
|
}
|
|
|
|
void XCOFFObjectWriter::writeSections(const MCAssembler &Asm,
|
|
const MCAsmLayout &Layout) {
|
|
uint32_t CurrentAddressLocation = 0;
|
|
for (const auto *Section : Sections) {
|
|
// Nothing to write for this Section.
|
|
if (Section->Index == SectionEntry::UninitializedIndex ||
|
|
Section->IsVirtual)
|
|
continue;
|
|
|
|
// There could be a gap (without corresponding zero padding) between
|
|
// sections.
|
|
assert(((CurrentAddressLocation <= Section->Address) ||
|
|
(Section->Flags == XCOFF::STYP_TDATA) ||
|
|
(Section->Flags == XCOFF::STYP_TBSS)) &&
|
|
"CurrentAddressLocation should be less than or equal to section "
|
|
"address if the section is not TData or TBSS.");
|
|
|
|
CurrentAddressLocation = Section->Address;
|
|
|
|
for (const auto *Group : Section->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;
|
|
}
|
|
}
|
|
|
|
// The size of the tail padding in a section is the end virtual address of
|
|
// the current section minus the the end virtual address of the last csect
|
|
// in that section.
|
|
if (uint32_t PaddingSize =
|
|
Section->Address + Section->Size - CurrentAddressLocation) {
|
|
W.OS.write_zeros(PaddingSize);
|
|
CurrentAddressLocation += PaddingSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint64_t XCOFFObjectWriter::writeObject(MCAssembler &Asm,
|
|
const MCAsmLayout &Layout) {
|
|
// We always emit a timestamp of 0 for reproducibility, so ensure incremental
|
|
// linking is not enabled, in case, like with Windows COFF, such a timestamp
|
|
// is incompatible with incremental linking of XCOFF.
|
|
if (Asm.isIncrementalLinkerCompatible())
|
|
report_fatal_error("Incremental linking not supported for XCOFF.");
|
|
|
|
if (TargetObjectWriter->is64Bit())
|
|
report_fatal_error("64-bit XCOFF object files are not supported yet.");
|
|
|
|
finalizeSectionInfo();
|
|
uint64_t StartOffset = W.OS.tell();
|
|
|
|
writeFileHeader();
|
|
writeSectionHeaderTable();
|
|
writeSections(Asm, Layout);
|
|
writeRelocations();
|
|
|
|
writeSymbolTable(Layout);
|
|
// Write the string table.
|
|
Strings.write(W.OS);
|
|
|
|
return W.OS.tell() - StartOffset;
|
|
}
|
|
|
|
bool XCOFFObjectWriter::nameShouldBeInStringTable(const StringRef &SymbolName) {
|
|
return SymbolName.size() > XCOFF::NameSize;
|
|
}
|
|
|
|
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::writeSymbolTableEntryForCsectMemberLabel(
|
|
const Symbol &SymbolRef, const XCOFFSection &CSectionRef,
|
|
int16_t SectionIndex, uint64_t SymbolOffset) {
|
|
// Name or Zeros and string table offset
|
|
writeSymbolName(SymbolRef.getSymbolTableName());
|
|
assert(SymbolOffset <= UINT32_MAX - CSectionRef.Address &&
|
|
"Symbol address overflows.");
|
|
W.write<uint32_t>(CSectionRef.Address + SymbolOffset);
|
|
W.write<int16_t>(SectionIndex);
|
|
// Basic/Derived type. See the description of the n_type field for symbol
|
|
// table entries for a detailed description. Since we don't yet support
|
|
// visibility, and all other bits are either optionally set or reserved, this
|
|
// is always zero.
|
|
// TODO FIXME How to assert a symbol's visibilty is default?
|
|
// TODO Set the function indicator (bit 10, 0x0020) for functions
|
|
// when debugging is enabled.
|
|
W.write<uint16_t>(0);
|
|
W.write<uint8_t>(SymbolRef.getStorageClass());
|
|
// Always 1 aux entry for now.
|
|
W.write<uint8_t>(1);
|
|
|
|
// Now output the auxiliary entry.
|
|
W.write<uint32_t>(CSectionRef.SymbolTableIndex);
|
|
// Parameter typecheck hash. Not supported.
|
|
W.write<uint32_t>(0);
|
|
// Typecheck section number. Not supported.
|
|
W.write<uint16_t>(0);
|
|
// Symbol type: Label
|
|
W.write<uint8_t>(XCOFF::XTY_LD);
|
|
// Storage mapping class.
|
|
W.write<uint8_t>(CSectionRef.MCSec->getMappingClass());
|
|
// Reserved (x_stab).
|
|
W.write<uint32_t>(0);
|
|
// Reserved (x_snstab).
|
|
W.write<uint16_t>(0);
|
|
}
|
|
|
|
void XCOFFObjectWriter::writeSymbolTableEntryForControlSection(
|
|
const XCOFFSection &CSectionRef, int16_t SectionIndex,
|
|
XCOFF::StorageClass StorageClass) {
|
|
// n_name, n_zeros, n_offset
|
|
writeSymbolName(CSectionRef.getSymbolTableName());
|
|
// n_value
|
|
W.write<uint32_t>(CSectionRef.Address);
|
|
// n_scnum
|
|
W.write<int16_t>(SectionIndex);
|
|
// Basic/Derived type. See the description of the n_type field for symbol
|
|
// table entries for a detailed description. Since we don't yet support
|
|
// visibility, and all other bits are either optionally set or reserved, this
|
|
// is always zero.
|
|
// TODO FIXME How to assert a symbol's visibilty is default?
|
|
// TODO Set the function indicator (bit 10, 0x0020) for functions
|
|
// when debugging is enabled.
|
|
W.write<uint16_t>(0);
|
|
// n_sclass
|
|
W.write<uint8_t>(StorageClass);
|
|
// Always 1 aux entry for now.
|
|
W.write<uint8_t>(1);
|
|
|
|
// Now output the auxiliary entry.
|
|
W.write<uint32_t>(CSectionRef.Size);
|
|
// Parameter typecheck hash. Not supported.
|
|
W.write<uint32_t>(0);
|
|
// Typecheck section number. Not supported.
|
|
W.write<uint16_t>(0);
|
|
// Symbol type.
|
|
W.write<uint8_t>(getEncodedType(CSectionRef.MCSec));
|
|
// Storage mapping class.
|
|
W.write<uint8_t>(CSectionRef.MCSec->getMappingClass());
|
|
// Reserved (x_stab).
|
|
W.write<uint32_t>(0);
|
|
// Reserved (x_snstab).
|
|
W.write<uint16_t>(0);
|
|
}
|
|
|
|
void XCOFFObjectWriter::writeFileHeader() {
|
|
// Magic.
|
|
W.write<uint16_t>(0x01df);
|
|
// Number of sections.
|
|
W.write<uint16_t>(SectionCount);
|
|
// Timestamp field. For reproducible output we write a 0, which represents no
|
|
// timestamp.
|
|
W.write<int32_t>(0);
|
|
// Byte Offset to the start of the symbol table.
|
|
W.write<uint32_t>(SymbolTableOffset);
|
|
// Number of entries in the symbol table.
|
|
W.write<int32_t>(SymbolTableEntryCount);
|
|
// Size of the optional header.
|
|
W.write<uint16_t>(0);
|
|
// Flags.
|
|
W.write<uint16_t>(0);
|
|
}
|
|
|
|
void XCOFFObjectWriter::writeSectionHeaderTable() {
|
|
for (const auto *Sec : Sections) {
|
|
// Nothing to write for this Section.
|
|
if (Sec->Index == SectionEntry::UninitializedIndex)
|
|
continue;
|
|
|
|
// Write Name.
|
|
ArrayRef<char> NameRef(Sec->Name, XCOFF::NameSize);
|
|
W.write(NameRef);
|
|
|
|
// Write the Physical Address and Virtual Address. In an object file these
|
|
// are the same.
|
|
W.write<uint32_t>(Sec->Address);
|
|
W.write<uint32_t>(Sec->Address);
|
|
|
|
W.write<uint32_t>(Sec->Size);
|
|
W.write<uint32_t>(Sec->FileOffsetToData);
|
|
W.write<uint32_t>(Sec->FileOffsetToRelocations);
|
|
|
|
// Line number pointer. Not supported yet.
|
|
W.write<uint32_t>(0);
|
|
|
|
W.write<uint16_t>(Sec->RelocationCount);
|
|
|
|
// Line number counts. Not supported yet.
|
|
W.write<uint16_t>(0);
|
|
|
|
W.write<int32_t>(Sec->Flags);
|
|
}
|
|
}
|
|
|
|
void XCOFFObjectWriter::writeRelocation(XCOFFRelocation Reloc,
|
|
const XCOFFSection &CSection) {
|
|
W.write<uint32_t>(CSection.Address + 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)
|
|
// Nothing to write for this Section.
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void XCOFFObjectWriter::writeSymbolTable(const MCAsmLayout &Layout) {
|
|
// Write symbol 0 as C_FILE.
|
|
// FIXME: support 64-bit C_FILE symbol.
|
|
//
|
|
// n_name. The n_name of a C_FILE symbol is the source filename when no
|
|
// auxiliary entries are present. The source filename is alternatively
|
|
// provided by an auxiliary entry, in which case the n_name of the C_FILE
|
|
// symbol is `.file`.
|
|
// FIXME: add the real source filename.
|
|
writeSymbolName(".file");
|
|
// n_value. The n_value of a C_FILE symbol is its symbol table index.
|
|
W.write<uint32_t>(0);
|
|
// n_scnum. N_DEBUG is a reserved section number for indicating a special
|
|
// symbolic debugging symbol.
|
|
W.write<int16_t>(XCOFF::ReservedSectionNum::N_DEBUG);
|
|
// n_type. The n_type field of a C_FILE symbol encodes the source language and
|
|
// CPU version info; zero indicates no info.
|
|
W.write<uint16_t>(0);
|
|
// n_sclass. The C_FILE symbol provides source file-name information,
|
|
// source-language ID and CPU-version ID information and some other optional
|
|
// infos.
|
|
W.write<uint8_t>(XCOFF::C_FILE);
|
|
// n_numaux. No aux entry for now.
|
|
W.write<uint8_t>(0);
|
|
|
|
for (const auto &Csect : UndefinedCsects) {
|
|
writeSymbolTableEntryForControlSection(Csect,
|
|
XCOFF::ReservedSectionNum::N_UNDEF,
|
|
Csect.MCSec->getStorageClass());
|
|
}
|
|
|
|
for (const auto *Section : Sections) {
|
|
if (Section->Index == SectionEntry::UninitializedIndex)
|
|
// Nothing to write for this Section.
|
|
continue;
|
|
|
|
for (const auto *Group : Section->Groups) {
|
|
if (Group->empty())
|
|
continue;
|
|
|
|
const int16_t SectionIndex = Section->Index;
|
|
for (const auto &Csect : *Group) {
|
|
// Write out the control section first and then each symbol in it.
|
|
writeSymbolTableEntryForControlSection(Csect, SectionIndex,
|
|
Csect.MCSec->getStorageClass());
|
|
|
|
for (const auto &Sym : Csect.Syms)
|
|
writeSymbolTableEntryForCsectMemberLabel(
|
|
Sym, Csect, SectionIndex, Layout.getSymbolOffset(*(Sym.MCSym)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void XCOFFObjectWriter::finalizeSectionInfo() {
|
|
for (auto *Section : Sections) {
|
|
if (Section->Index == SectionEntry::UninitializedIndex)
|
|
// Nothing to record for this Section.
|
|
continue;
|
|
|
|
for (const auto *Group : Section->Groups) {
|
|
if (Group->empty())
|
|
continue;
|
|
|
|
for (auto &Csect : *Group) {
|
|
const size_t CsectRelocCount = Csect.Relocations.size();
|
|
if (CsectRelocCount >= XCOFF::RelocOverflow ||
|
|
Section->RelocationCount >= XCOFF::RelocOverflow - CsectRelocCount)
|
|
report_fatal_error(
|
|
"relocation entries overflowed; overflow section is "
|
|
"not implemented yet");
|
|
|
|
Section->RelocationCount += CsectRelocCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate the file offset to the relocation entries.
|
|
uint64_t RawPointer = RelocationEntryOffset;
|
|
for (auto Sec : Sections) {
|
|
if (Sec->Index == SectionEntry::UninitializedIndex || !Sec->RelocationCount)
|
|
continue;
|
|
|
|
Sec->FileOffsetToRelocations = RawPointer;
|
|
const uint32_t RelocationSizeInSec =
|
|
Sec->RelocationCount * XCOFF::RelocationSerializationSize32;
|
|
RawPointer += RelocationSizeInSec;
|
|
if (RawPointer > UINT32_MAX)
|
|
report_fatal_error("Relocation data overflowed this object file.");
|
|
}
|
|
|
|
// TODO Error check that the number of symbol table entries fits in 32-bits
|
|
// signed ...
|
|
if (SymbolTableEntryCount)
|
|
SymbolTableOffset = RawPointer;
|
|
}
|
|
|
|
void XCOFFObjectWriter::assignAddressesAndIndices(const MCAsmLayout &Layout) {
|
|
// The first symbol table entry (at index 0) is for the file name.
|
|
uint32_t SymbolTableIndex = 1;
|
|
|
|
// Calculate indices for undefined symbols.
|
|
for (auto &Csect : UndefinedCsects) {
|
|
Csect.Size = 0;
|
|
Csect.Address = 0;
|
|
Csect.SymbolTableIndex = SymbolTableIndex;
|
|
SymbolIndexMap[Csect.MCSec->getQualNameSymbol()] = Csect.SymbolTableIndex;
|
|
// 1 main and 1 auxiliary symbol table entry for each contained symbol.
|
|
SymbolTableIndex += 2;
|
|
}
|
|
|
|
// The address corrresponds to the address of sections and symbols in the
|
|
// object file. We place the shared address 0 immediately after the
|
|
// section header table.
|
|
uint32_t Address = 0;
|
|
// Section indices are 1-based in XCOFF.
|
|
int32_t SectionIndex = 1;
|
|
bool HasTDataSection = false;
|
|
|
|
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;
|
|
// Reset the starting address to 0 for TData section.
|
|
if (Section->Flags == XCOFF::STYP_TDATA) {
|
|
Address = 0;
|
|
HasTDataSection = true;
|
|
}
|
|
// Reset the starting address to 0 for TBSS section if the object file does
|
|
// not contain TData Section.
|
|
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;
|
|
// 1 main and 1 auxiliary symbol table entry for the csect.
|
|
SymbolTableIndex += 2;
|
|
|
|
for (auto &Sym : Csect.Syms) {
|
|
Sym.SymbolTableIndex = SymbolTableIndex;
|
|
SymbolIndexMap[Sym.MCSym] = Sym.SymbolTableIndex;
|
|
// 1 main and 1 auxiliary symbol table entry for each contained
|
|
// symbol.
|
|
SymbolTableIndex += 2;
|
|
}
|
|
}
|
|
|
|
if (!SectionAddressSet) {
|
|
Section->Address = Group->front().Address;
|
|
SectionAddressSet = true;
|
|
}
|
|
}
|
|
|
|
// Make sure the address of the next section aligned to
|
|
// DefaultSectionAlign.
|
|
Address = alignTo(Address, DefaultSectionAlign);
|
|
Section->Size = Address - Section->Address;
|
|
}
|
|
|
|
SymbolTableEntryCount = SymbolTableIndex;
|
|
|
|
// Calculate the RawPointer value for each section.
|
|
uint64_t RawPointer = XCOFF::FileHeaderSize32 + auxiliaryHeaderSize() +
|
|
SectionCount * XCOFF::SectionHeaderSize32;
|
|
for (auto *Sec : Sections) {
|
|
if (Sec->Index == SectionEntry::UninitializedIndex || Sec->IsVirtual)
|
|
continue;
|
|
|
|
Sec->FileOffsetToData = RawPointer;
|
|
RawPointer += Sec->Size;
|
|
if (RawPointer > UINT32_MAX)
|
|
report_fatal_error("Section raw data overflowed this object file.");
|
|
}
|
|
|
|
RelocationEntryOffset = RawPointer;
|
|
}
|
|
|
|
// Takes the log base 2 of the alignment and shifts the result into the 5 most
|
|
// significant bits of a byte, then or's in the csect type into the least
|
|
// significant 3 bits.
|
|
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);
|
|
// Result is a number in the range [0, 31] which fits in the 5 least
|
|
// significant bits. Shift this value into the 5 most significant bits, and
|
|
// bitwise-or in the csect type.
|
|
uint8_t EncodedAlign = Log2Align << 3;
|
|
return EncodedAlign | Sec->getCSectType();
|
|
}
|
|
|
|
} // end anonymous namespace
|
|
|
|
std::unique_ptr<MCObjectWriter>
|
|
llvm::createXCOFFObjectWriter(std::unique_ptr<MCXCOFFObjectTargetWriter> MOTW,
|
|
raw_pwrite_stream &OS) {
|
|
return std::make_unique<XCOFFObjectWriter>(std::move(MOTW), OS);
|
|
}
|