mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 15:26:07 +00:00
1734 lines
65 KiB
C++
1734 lines
65 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/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>
|
|
#include <map>
|
|
|
|
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::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) {}
|
|
};
|
|
|
|
// 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;
|
|
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) {}
|
|
};
|
|
|
|
// 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, except for in the overflow section header, where
|
|
// the physical address specifies the number of relocation entries and the
|
|
// virtual address specifies the number of line number entries.
|
|
// TODO: Divide Address into PhysicalAddress and VirtualAddress when line
|
|
// number entries are supported.
|
|
uint64_t Address;
|
|
uint64_t Size;
|
|
uint64_t FileOffsetToData;
|
|
uint64_t FileOffsetToRelocations;
|
|
uint32_t RelocationCount;
|
|
int32_t Flags;
|
|
|
|
int16_t Index;
|
|
|
|
virtual uint64_t advanceFileOffset(const uint64_t MaxRawDataSize,
|
|
const uint64_t RawPointer) {
|
|
FileOffsetToData = RawPointer;
|
|
uint64_t NewPointer = RawPointer + Size;
|
|
if (NewPointer > MaxRawDataSize)
|
|
report_fatal_error("Section raw data overflowed this object file.");
|
|
return NewPointer;
|
|
}
|
|
|
|
// 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() = default;
|
|
};
|
|
|
|
// 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() = default;
|
|
};
|
|
|
|
struct DwarfSectionEntry : public SectionEntry {
|
|
// For DWARF section entry.
|
|
std::unique_ptr<XCOFFSection> DwarfSect;
|
|
|
|
// For DWARF section, we must use real size in the section header. MemorySize
|
|
// is for the size the DWARF section occupies including paddings.
|
|
uint32_t MemorySize;
|
|
|
|
// TODO: Remove this override. Loadable sections (e.g., .text, .data) may need
|
|
// to be aligned. Other sections generally don't need any alignment, but if
|
|
// they're aligned, the RawPointer should be adjusted before writing the
|
|
// section. Then a dwarf-specific function wouldn't be needed.
|
|
uint64_t advanceFileOffset(const uint64_t MaxRawDataSize,
|
|
const uint64_t RawPointer) override {
|
|
FileOffsetToData = RawPointer;
|
|
uint64_t NewPointer = RawPointer + MemorySize;
|
|
assert(NewPointer <= MaxRawDataSize &&
|
|
"Section raw data overflowed this object file.");
|
|
return NewPointer;
|
|
}
|
|
|
|
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;
|
|
};
|
|
|
|
struct ExceptionTableEntry {
|
|
const MCSymbol *Trap;
|
|
uint64_t TrapAddress = ~0ul;
|
|
unsigned Lang;
|
|
unsigned Reason;
|
|
|
|
ExceptionTableEntry(const MCSymbol *Trap, unsigned Lang, unsigned Reason)
|
|
: Trap(Trap), Lang(Lang), Reason(Reason) {}
|
|
};
|
|
|
|
struct ExceptionInfo {
|
|
const MCSymbol *FunctionSymbol;
|
|
unsigned FunctionSize;
|
|
std::vector<ExceptionTableEntry> Entries;
|
|
};
|
|
|
|
struct ExceptionSectionEntry : public SectionEntry {
|
|
std::map<const StringRef, ExceptionInfo> ExceptionTable;
|
|
bool isDebugEnabled = false;
|
|
|
|
ExceptionSectionEntry(StringRef N, int32_t Flags)
|
|
: SectionEntry(N, Flags | XCOFF::STYP_EXCEPT) {
|
|
assert(N.size() <= XCOFF::NameSize && "Section too long.");
|
|
memcpy(Name, N.data(), N.size());
|
|
}
|
|
|
|
virtual ~ExceptionSectionEntry() = default;
|
|
};
|
|
|
|
struct CInfoSymInfo {
|
|
// Name of the C_INFO symbol associated with the section
|
|
std::string Name;
|
|
std::string Metadata;
|
|
// Offset into the start of the metadata in the section
|
|
uint64_t Offset;
|
|
|
|
CInfoSymInfo(std::string Name, std::string Metadata)
|
|
: Name(Name), Metadata(Metadata) {}
|
|
// Metadata needs to be padded out to an even word size.
|
|
uint32_t paddingSize() const {
|
|
return alignTo(Metadata.size(), sizeof(uint32_t)) - Metadata.size();
|
|
};
|
|
|
|
// Total size of the entry, including the 4 byte length
|
|
uint32_t size() const {
|
|
return Metadata.size() + paddingSize() + sizeof(uint32_t);
|
|
};
|
|
};
|
|
|
|
struct CInfoSymSectionEntry : public SectionEntry {
|
|
std::unique_ptr<CInfoSymInfo> Entry;
|
|
|
|
CInfoSymSectionEntry(StringRef N, int32_t Flags) : SectionEntry(N, Flags) {}
|
|
virtual ~CInfoSymSectionEntry() = default;
|
|
void addEntry(std::unique_ptr<CInfoSymInfo> NewEntry) {
|
|
Entry = std::move(NewEntry);
|
|
Entry->Offset = sizeof(uint32_t);
|
|
Size += Entry->size();
|
|
}
|
|
void reset() override {
|
|
SectionEntry::reset();
|
|
Entry.reset();
|
|
}
|
|
};
|
|
|
|
class XCOFFWriter final : public XCOFFObjectWriter {
|
|
uint32_t SymbolTableEntryCount = 0;
|
|
uint64_t SymbolTableOffset = 0;
|
|
uint16_t SectionCount = 0;
|
|
uint32_t PaddingsBeforeDwarf = 0;
|
|
bool HasVisibility = false;
|
|
|
|
support::endian::Writer W;
|
|
std::unique_ptr<MCXCOFFObjectTargetWriter> TargetObjectWriter;
|
|
StringTableBuilder Strings;
|
|
|
|
const uint64_t MaxRawDataSize =
|
|
TargetObjectWriter->is64Bit() ? UINT64_MAX : UINT32_MAX;
|
|
|
|
// 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}};
|
|
|
|
std::vector<DwarfSectionEntry> DwarfSections;
|
|
std::vector<SectionEntry> OverflowSections;
|
|
|
|
ExceptionSectionEntry ExceptionSection;
|
|
CInfoSymSectionEntry CInfoSymSection;
|
|
|
|
CsectGroup &getCsectGroup(const MCSectionXCOFF *MCSec);
|
|
|
|
void reset() override;
|
|
|
|
void executePostLayoutBinding(MCAssembler &) override;
|
|
|
|
void recordRelocation(MCAssembler &, const MCFragment *, const MCFixup &,
|
|
MCValue, uint64_t &) override;
|
|
|
|
uint64_t writeObject(MCAssembler &) override;
|
|
|
|
bool is64Bit() const { return TargetObjectWriter->is64Bit(); }
|
|
bool nameShouldBeInStringTable(const StringRef &);
|
|
void writeSymbolName(const StringRef &);
|
|
bool auxFileSymNameShouldBeInStringTable(const StringRef &);
|
|
void writeAuxFileSymName(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 writeSectionHeader(const SectionEntry *Sec);
|
|
void writeSectionHeaderTable();
|
|
void writeSections(const MCAssembler &Asm);
|
|
void writeSectionForControlSectionEntry(const MCAssembler &Asm,
|
|
const CsectSectionEntry &CsectEntry,
|
|
uint64_t &CurrentAddressLocation);
|
|
void writeSectionForDwarfSectionEntry(const MCAssembler &Asm,
|
|
const DwarfSectionEntry &DwarfEntry,
|
|
uint64_t &CurrentAddressLocation);
|
|
void
|
|
writeSectionForExceptionSectionEntry(const MCAssembler &Asm,
|
|
ExceptionSectionEntry &ExceptionEntry,
|
|
uint64_t &CurrentAddressLocation);
|
|
void writeSectionForCInfoSymSectionEntry(const MCAssembler &Asm,
|
|
CInfoSymSectionEntry &CInfoSymEntry,
|
|
uint64_t &CurrentAddressLocation);
|
|
void writeSymbolTable(MCAssembler &Asm);
|
|
void writeSymbolAuxFileEntry(StringRef &Name, uint8_t ftype);
|
|
void writeSymbolAuxDwarfEntry(uint64_t LengthOfSectionPortion,
|
|
uint64_t NumberOfRelocEnt = 0);
|
|
void writeSymbolAuxCsectEntry(uint64_t SectionOrLength,
|
|
uint8_t SymbolAlignmentAndType,
|
|
uint8_t StorageMappingClass);
|
|
void writeSymbolAuxFunctionEntry(uint32_t EntryOffset, uint32_t FunctionSize,
|
|
uint64_t LineNumberPointer,
|
|
uint32_t EndIndex);
|
|
void writeSymbolAuxExceptionEntry(uint64_t EntryOffset, uint32_t FunctionSize,
|
|
uint32_t EndIndex);
|
|
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);
|
|
|
|
// 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(MCAssembler &Asm);
|
|
// Called after relocations are recorded.
|
|
void finalizeSectionInfo();
|
|
void finalizeRelocationInfo(SectionEntry *Sec, uint64_t RelCount);
|
|
void calcOffsetToRelocations(SectionEntry *Sec, uint64_t &RawPointer);
|
|
|
|
bool hasExceptionSection() {
|
|
return !ExceptionSection.ExceptionTable.empty();
|
|
}
|
|
unsigned getExceptionSectionSize();
|
|
unsigned getExceptionOffset(const MCSymbol *Symbol);
|
|
|
|
size_t auxiliaryHeaderSize() const {
|
|
// 64-bit object files have no auxiliary header.
|
|
return HasVisibility && !is64Bit() ? XCOFF::AuxFileHeaderSizeShort : 0;
|
|
}
|
|
|
|
public:
|
|
XCOFFWriter(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);
|
|
}
|
|
|
|
void addExceptionEntry(const MCSymbol *Symbol, const MCSymbol *Trap,
|
|
unsigned LanguageCode, unsigned ReasonCode,
|
|
unsigned FunctionSize, bool hasDebug) override;
|
|
void addCInfoSymEntry(StringRef Name, StringRef Metadata) override;
|
|
};
|
|
|
|
XCOFFWriter::XCOFFWriter(std::unique_ptr<MCXCOFFObjectTargetWriter> MOTW,
|
|
raw_pwrite_stream &OS)
|
|
: W(OS, llvm::endianness::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}),
|
|
ExceptionSection(".except", XCOFF::STYP_EXCEPT),
|
|
CInfoSymSection(".info", XCOFF::STYP_INFO) {}
|
|
|
|
void XCOFFWriter::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();
|
|
for (auto &DwarfSec : DwarfSections)
|
|
DwarfSec.reset();
|
|
for (auto &OverflowSec : OverflowSections)
|
|
OverflowSec.reset();
|
|
ExceptionSection.reset();
|
|
CInfoSymSection.reset();
|
|
|
|
// Reset states in XCOFFWriter.
|
|
SymbolTableEntryCount = 0;
|
|
SymbolTableOffset = 0;
|
|
SectionCount = 0;
|
|
PaddingsBeforeDwarf = 0;
|
|
Strings.clear();
|
|
|
|
MCObjectWriter::reset();
|
|
}
|
|
|
|
CsectGroup &XCOFFWriter::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() &&
|
|
"A TOC symbol must be an initialized csect.");
|
|
assert(!TOCCsects.empty() &&
|
|
"We should at least have a TOC-base in this CsectGroup.");
|
|
return TOCCsects;
|
|
case XCOFF::XMC_TD:
|
|
assert((XCOFF::XTY_SD == MCSec->getCSectType() ||
|
|
XCOFF::XTY_CM == MCSec->getCSectType()) &&
|
|
"Symbol type incompatible with toc-data.");
|
|
assert(!TOCCsects.empty() &&
|
|
"We should at least have a TOC-base in this CsectGroup.");
|
|
return TOCCsects;
|
|
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 XCOFFWriter::executePostLayoutBinding(MCAssembler &Asm) {
|
|
for (const auto &S : Asm) {
|
|
const auto *MCSec = cast<const MCSectionXCOFF>(&S);
|
|
assert(!SectionMap.contains(MCSec) && "Cannot add a section twice.");
|
|
|
|
// 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());
|
|
if (MCSec->isCsect()) {
|
|
// A new control section. Its CsectSectionEntry should already be staticly
|
|
// generated as Text/Data/BSS/TDATA/TBSS. Add this section to the group of
|
|
// the CsectSectionEntry.
|
|
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()) {
|
|
// A new DwarfSectionEntry.
|
|
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()) {
|
|
// Nothing to do for temporary symbols.
|
|
if (S.isTemporary())
|
|
continue;
|
|
|
|
const MCSymbolXCOFF *XSym = cast<MCSymbolXCOFF>(&S);
|
|
const MCSectionXCOFF *ContainingCsect = getContainingCsect(XSym);
|
|
|
|
if (ContainingCsect->isDwarfSect())
|
|
continue;
|
|
|
|
if (XSym->getVisibilityType() != XCOFF::SYM_V_UNSPECIFIED)
|
|
HasVisibility = true;
|
|
|
|
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.contains(ContainingCsect) &&
|
|
"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());
|
|
}
|
|
|
|
std::unique_ptr<CInfoSymInfo> &CISI = CInfoSymSection.Entry;
|
|
if (CISI && nameShouldBeInStringTable(CISI->Name))
|
|
Strings.add(CISI->Name);
|
|
|
|
// Emit ".file" as the source file name when there is no file name.
|
|
if (FileNames.empty())
|
|
FileNames.emplace_back(".file", 0);
|
|
for (const std::pair<std::string, size_t> &F : FileNames) {
|
|
if (auxFileSymNameShouldBeInStringTable(F.first))
|
|
Strings.add(F.first);
|
|
}
|
|
|
|
// Always add ".file" to the symbol table. The actual file name will be in
|
|
// the AUX_FILE auxiliary entry.
|
|
if (nameShouldBeInStringTable(".file"))
|
|
Strings.add(".file");
|
|
StringRef Vers = CompilerVersion;
|
|
if (auxFileSymNameShouldBeInStringTable(Vers))
|
|
Strings.add(Vers);
|
|
|
|
Strings.finalize();
|
|
assignAddressesAndIndices(Asm);
|
|
}
|
|
|
|
void XCOFFWriter::recordRelocation(MCAssembler &Asm, 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.
|
|
auto It = SymbolIndexMap.find(Sym);
|
|
return It != SymbolIndexMap.end()
|
|
? It->second
|
|
: SymbolIndexMap[ContainingCsect->getQualNameSymbol()];
|
|
};
|
|
|
|
auto getVirtualAddress =
|
|
[this, &Asm](const MCSymbol *Sym,
|
|
const MCSectionXCOFF *ContainingSect) -> uint64_t {
|
|
// A DWARF section.
|
|
if (ContainingSect->isDwarfSect())
|
|
return Asm.getSymbolOffset(*Sym);
|
|
|
|
// A csect.
|
|
if (!Sym->isDefined())
|
|
return SectionMap[ContainingSect]->Address;
|
|
|
|
// A label.
|
|
assert(Sym->isDefined() && "not a valid object that has address!");
|
|
return SectionMap[ContainingSect]->Address + Asm.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));
|
|
assert(SectionMap.contains(SymASec) &&
|
|
"Expected containing csect to exist in map.");
|
|
|
|
assert((Fixup.getOffset() <=
|
|
MaxRawDataSize - Asm.getFragmentOffset(*Fragment)) &&
|
|
"Fragment offset + fixup offset is overflowed.");
|
|
uint32_t FixupOffsetInCsect =
|
|
Asm.getFragmentOffset(*Fragment) + Fixup.getOffset();
|
|
|
|
const uint32_t Index = getIndex(SymA, SymASec);
|
|
if (Type == XCOFF::RelocationType::R_POS ||
|
|
Type == XCOFF::RelocationType::R_TLS ||
|
|
Type == XCOFF::RelocationType::R_TLS_LE ||
|
|
Type == XCOFF::RelocationType::R_TLS_IE ||
|
|
Type == XCOFF::RelocationType::R_TLS_LD)
|
|
// 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) {
|
|
// For non toc-data external symbols, R_TOC type relocation will relocate to
|
|
// data symbols that have XCOFF::XTY_SD type csect. For toc-data external
|
|
// symbols, R_TOC type relocation will relocate to data symbols that have
|
|
// XCOFF_ER type csect. For XCOFF_ER kind symbols, there will be no TOC
|
|
// entry for them, so the FixedValue should always be 0.
|
|
if (SymASec->getCSectType() == XCOFF::XTY_ER) {
|
|
FixedValue = 0;
|
|
} else {
|
|
// The FixedValue should be the TOC entry offset from the TOC-base plus
|
|
// any constant offset value.
|
|
int64_t TOCEntryOffset = SectionMap[SymASec]->Address -
|
|
TOCCsects.front().Address + Target.getConstant();
|
|
// For small code model, if the TOCEntryOffset overflows the 16-bit value,
|
|
// we truncate it back down to 16 bits. The linker will be able to insert
|
|
// fix-up code when needed.
|
|
// For non toc-data symbols, we already did the truncation in
|
|
// PPCAsmPrinter.cpp through setting Target.getConstant() in the
|
|
// expression above by calling getTOCEntryLoadingExprForXCOFF for the
|
|
// various TOC PseudoOps.
|
|
// For toc-data symbols, we were not able to calculate the offset from
|
|
// the TOC in PPCAsmPrinter.cpp since the TOC has not been finalized at
|
|
// that point, so we are adjusting it here though
|
|
// llvm::SignExtend64<16>(TOCEntryOffset);
|
|
// TODO: Since the time that the handling for offsets over 16-bits was
|
|
// added in PPCAsmPrinter.cpp using getTOCEntryLoadingExprForXCOFF, the
|
|
// system assembler and linker have been updated to be able to handle the
|
|
// overflowing offsets, so we no longer need to keep
|
|
// getTOCEntryLoadingExprForXCOFF.
|
|
if (Type == XCOFF::RelocationType::R_TOC && !isInt<16>(TOCEntryOffset))
|
|
TOCEntryOffset = llvm::SignExtend64<16>(TOCEntryOffset);
|
|
|
|
FixedValue = TOCEntryOffset;
|
|
}
|
|
} else if (Type == XCOFF::RelocationType::R_RBR) {
|
|
MCSectionXCOFF *ParentSec = cast<MCSectionXCOFF>(Fragment->getParent());
|
|
assert((SymASec->getMappingClass() == XCOFF::XMC_PR &&
|
|
ParentSec->getMappingClass() == XCOFF::XMC_PR) &&
|
|
"Only XMC_PR csect may have the R_RBR relocation.");
|
|
|
|
// The address of the branch instruction should be the sum of section
|
|
// address, fragment offset and Fixup offset.
|
|
uint64_t BRInstrAddress =
|
|
SectionMap[ParentSec]->Address + FixupOffsetInCsect;
|
|
// The FixedValue should be the difference between symbol's virtual address
|
|
// and BR instr address plus any constant value.
|
|
FixedValue = getVirtualAddress(SymA, SymASec) - BRInstrAddress +
|
|
Target.getConstant();
|
|
} else if (Type == XCOFF::RelocationType::R_REF) {
|
|
// The FixedValue and FixupOffsetInCsect should always be 0 since it
|
|
// specifies a nonrelocating reference.
|
|
FixedValue = 0;
|
|
FixupOffsetInCsect = 0;
|
|
}
|
|
|
|
XCOFFRelocation Reloc = {Index, FixupOffsetInCsect, SignAndSize, Type};
|
|
MCSectionXCOFF *RelocationSec = cast<MCSectionXCOFF>(Fragment->getParent());
|
|
assert(SectionMap.contains(RelocationSec) &&
|
|
"Expected containing csect to exist in map.");
|
|
SectionMap[RelocationSec]->Relocations.push_back(Reloc);
|
|
|
|
const MCSymbol *const SymB = Target.getSubSym();
|
|
if (!SymB)
|
|
return;
|
|
if (SymA == SymB)
|
|
report_fatal_error("relocation for opposite term is not yet supported");
|
|
|
|
const MCSectionXCOFF *SymBSec = getContainingCsect(cast<MCSymbolXCOFF>(SymB));
|
|
assert(SectionMap.contains(SymBSec) &&
|
|
"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 XCOFFWriter::writeSections(const MCAssembler &Asm) {
|
|
uint64_t CurrentAddressLocation = 0;
|
|
for (const auto *Section : Sections)
|
|
writeSectionForControlSectionEntry(Asm, *Section, CurrentAddressLocation);
|
|
for (const auto &DwarfSection : DwarfSections)
|
|
writeSectionForDwarfSectionEntry(Asm, DwarfSection, CurrentAddressLocation);
|
|
writeSectionForExceptionSectionEntry(Asm, ExceptionSection,
|
|
CurrentAddressLocation);
|
|
writeSectionForCInfoSymSectionEntry(Asm, CInfoSymSection,
|
|
CurrentAddressLocation);
|
|
}
|
|
|
|
uint64_t XCOFFWriter::writeObject(MCAssembler &Asm) {
|
|
// 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.
|
|
|
|
finalizeSectionInfo();
|
|
uint64_t StartOffset = W.OS.tell();
|
|
|
|
writeFileHeader();
|
|
writeAuxFileHeader();
|
|
writeSectionHeaderTable();
|
|
writeSections(Asm);
|
|
writeRelocations();
|
|
writeSymbolTable(Asm);
|
|
// Write the string table.
|
|
Strings.write(W.OS);
|
|
|
|
return W.OS.tell() - StartOffset;
|
|
}
|
|
|
|
bool XCOFFWriter::nameShouldBeInStringTable(const StringRef &SymbolName) {
|
|
return SymbolName.size() > XCOFF::NameSize || is64Bit();
|
|
}
|
|
|
|
void XCOFFWriter::writeSymbolName(const StringRef &SymbolName) {
|
|
// Magic, Offset or 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 XCOFFWriter::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 XCOFFWriter::writeSymbolAuxCsectEntry(uint64_t SectionOrLength,
|
|
uint8_t SymbolAlignmentAndType,
|
|
uint8_t StorageMappingClass) {
|
|
W.write<uint32_t>(is64Bit() ? Lo_32(SectionOrLength) : SectionOrLength);
|
|
W.write<uint32_t>(0); // ParameterHashIndex
|
|
W.write<uint16_t>(0); // TypeChkSectNum
|
|
W.write<uint8_t>(SymbolAlignmentAndType);
|
|
W.write<uint8_t>(StorageMappingClass);
|
|
if (is64Bit()) {
|
|
W.write<uint32_t>(Hi_32(SectionOrLength));
|
|
W.OS.write_zeros(1); // Reserved
|
|
W.write<uint8_t>(XCOFF::AUX_CSECT);
|
|
} else {
|
|
W.write<uint32_t>(0); // StabInfoIndex
|
|
W.write<uint16_t>(0); // StabSectNum
|
|
}
|
|
}
|
|
|
|
bool XCOFFWriter::auxFileSymNameShouldBeInStringTable(
|
|
const StringRef &SymbolName) {
|
|
return SymbolName.size() > XCOFF::AuxFileEntNameSize;
|
|
}
|
|
|
|
void XCOFFWriter::writeAuxFileSymName(const StringRef &SymbolName) {
|
|
// Magic, Offset or SymbolName.
|
|
if (auxFileSymNameShouldBeInStringTable(SymbolName)) {
|
|
W.write<int32_t>(0);
|
|
W.write<uint32_t>(Strings.getOffset(SymbolName));
|
|
W.OS.write_zeros(XCOFF::FileNamePadSize);
|
|
} else {
|
|
char Name[XCOFF::AuxFileEntNameSize + 1];
|
|
std::strncpy(Name, SymbolName.data(), XCOFF::AuxFileEntNameSize);
|
|
ArrayRef<char> NameRef(Name, XCOFF::AuxFileEntNameSize);
|
|
W.write(NameRef);
|
|
}
|
|
}
|
|
|
|
void XCOFFWriter::writeSymbolAuxFileEntry(StringRef &Name, uint8_t ftype) {
|
|
writeAuxFileSymName(Name);
|
|
W.write<uint8_t>(ftype);
|
|
W.OS.write_zeros(2);
|
|
if (is64Bit())
|
|
W.write<uint8_t>(XCOFF::AUX_FILE);
|
|
else
|
|
W.OS.write_zeros(1);
|
|
}
|
|
|
|
void XCOFFWriter::writeSymbolAuxDwarfEntry(uint64_t LengthOfSectionPortion,
|
|
uint64_t NumberOfRelocEnt) {
|
|
writeWord(LengthOfSectionPortion);
|
|
if (!is64Bit())
|
|
W.OS.write_zeros(4); // Reserved
|
|
writeWord(NumberOfRelocEnt);
|
|
if (is64Bit()) {
|
|
W.OS.write_zeros(1); // Reserved
|
|
W.write<uint8_t>(XCOFF::AUX_SECT);
|
|
} else {
|
|
W.OS.write_zeros(6); // Reserved
|
|
}
|
|
}
|
|
|
|
void XCOFFWriter::writeSymbolEntryForCsectMemberLabel(
|
|
const Symbol &SymbolRef, const XCOFFSection &CSectionRef,
|
|
int16_t SectionIndex, uint64_t SymbolOffset) {
|
|
assert(SymbolOffset <= MaxRawDataSize - CSectionRef.Address &&
|
|
"Symbol address overflowed.");
|
|
|
|
auto Entry = ExceptionSection.ExceptionTable.find(SymbolRef.MCSym->getName());
|
|
if (Entry != ExceptionSection.ExceptionTable.end()) {
|
|
writeSymbolEntry(SymbolRef.getSymbolTableName(),
|
|
CSectionRef.Address + SymbolOffset, SectionIndex,
|
|
// In the old version of the 32-bit XCOFF interpretation,
|
|
// symbols may require bit 10 (0x0020) to be set if the
|
|
// symbol is a function, otherwise the bit should be 0.
|
|
is64Bit() ? SymbolRef.getVisibilityType()
|
|
: SymbolRef.getVisibilityType() | 0x0020,
|
|
SymbolRef.getStorageClass(),
|
|
(is64Bit() && ExceptionSection.isDebugEnabled) ? 3 : 2);
|
|
if (is64Bit() && ExceptionSection.isDebugEnabled) {
|
|
// On 64 bit with debugging enabled, we have a csect, exception, and
|
|
// function auxilliary entries, so we must increment symbol index by 4.
|
|
writeSymbolAuxExceptionEntry(
|
|
ExceptionSection.FileOffsetToData +
|
|
getExceptionOffset(Entry->second.FunctionSymbol),
|
|
Entry->second.FunctionSize,
|
|
SymbolIndexMap[Entry->second.FunctionSymbol] + 4);
|
|
}
|
|
// For exception section entries, csect and function auxilliary entries
|
|
// must exist. On 64-bit there is also an exception auxilliary entry.
|
|
writeSymbolAuxFunctionEntry(
|
|
ExceptionSection.FileOffsetToData +
|
|
getExceptionOffset(Entry->second.FunctionSymbol),
|
|
Entry->second.FunctionSize, 0,
|
|
(is64Bit() && ExceptionSection.isDebugEnabled)
|
|
? SymbolIndexMap[Entry->second.FunctionSymbol] + 4
|
|
: SymbolIndexMap[Entry->second.FunctionSymbol] + 3);
|
|
} else {
|
|
writeSymbolEntry(SymbolRef.getSymbolTableName(),
|
|
CSectionRef.Address + SymbolOffset, SectionIndex,
|
|
SymbolRef.getVisibilityType(),
|
|
SymbolRef.getStorageClass());
|
|
}
|
|
writeSymbolAuxCsectEntry(CSectionRef.SymbolTableIndex, XCOFF::XTY_LD,
|
|
CSectionRef.MCSec->getMappingClass());
|
|
}
|
|
|
|
void XCOFFWriter::writeSymbolEntryForDwarfSection(
|
|
const XCOFFSection &DwarfSectionRef, int16_t SectionIndex) {
|
|
assert(DwarfSectionRef.MCSec->isDwarfSect() && "Not a DWARF section!");
|
|
|
|
writeSymbolEntry(DwarfSectionRef.getSymbolTableName(), /*Value=*/0,
|
|
SectionIndex, /*SymbolType=*/0, XCOFF::C_DWARF);
|
|
|
|
writeSymbolAuxDwarfEntry(DwarfSectionRef.Size);
|
|
}
|
|
|
|
void XCOFFWriter::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 XCOFFWriter::writeSymbolAuxFunctionEntry(uint32_t EntryOffset,
|
|
uint32_t FunctionSize,
|
|
uint64_t LineNumberPointer,
|
|
uint32_t EndIndex) {
|
|
if (is64Bit())
|
|
writeWord(LineNumberPointer);
|
|
else
|
|
W.write<uint32_t>(EntryOffset);
|
|
W.write<uint32_t>(FunctionSize);
|
|
if (!is64Bit())
|
|
writeWord(LineNumberPointer);
|
|
W.write<uint32_t>(EndIndex);
|
|
if (is64Bit()) {
|
|
W.OS.write_zeros(1);
|
|
W.write<uint8_t>(XCOFF::AUX_FCN);
|
|
} else {
|
|
W.OS.write_zeros(2);
|
|
}
|
|
}
|
|
|
|
void XCOFFWriter::writeSymbolAuxExceptionEntry(uint64_t EntryOffset,
|
|
uint32_t FunctionSize,
|
|
uint32_t EndIndex) {
|
|
assert(is64Bit() && "Exception auxilliary entries are 64-bit only.");
|
|
W.write<uint64_t>(EntryOffset);
|
|
W.write<uint32_t>(FunctionSize);
|
|
W.write<uint32_t>(EndIndex);
|
|
W.OS.write_zeros(1); // Pad (unused)
|
|
W.write<uint8_t>(XCOFF::AUX_EXCEPT);
|
|
}
|
|
|
|
void XCOFFWriter::writeFileHeader() {
|
|
W.write<uint16_t>(is64Bit() ? XCOFF::XCOFF64 : XCOFF::XCOFF32);
|
|
W.write<uint16_t>(SectionCount);
|
|
W.write<int32_t>(0); // TimeStamp
|
|
writeWord(SymbolTableOffset);
|
|
if (is64Bit()) {
|
|
W.write<uint16_t>(auxiliaryHeaderSize());
|
|
W.write<uint16_t>(0); // Flags
|
|
W.write<int32_t>(SymbolTableEntryCount);
|
|
} else {
|
|
W.write<int32_t>(SymbolTableEntryCount);
|
|
W.write<uint16_t>(auxiliaryHeaderSize());
|
|
W.write<uint16_t>(0); // Flags
|
|
}
|
|
}
|
|
|
|
void XCOFFWriter::writeAuxFileHeader() {
|
|
if (!auxiliaryHeaderSize())
|
|
return;
|
|
W.write<uint16_t>(0); // Magic
|
|
W.write<uint16_t>(
|
|
XCOFF::NEW_XCOFF_INTERPRET); // Version. The new interpretation of the
|
|
// n_type field in the symbol table entry is
|
|
// used in XCOFF32.
|
|
W.write<uint32_t>(Sections[0]->Size); // TextSize
|
|
W.write<uint32_t>(Sections[1]->Size); // InitDataSize
|
|
W.write<uint32_t>(Sections[2]->Size); // BssDataSize
|
|
W.write<uint32_t>(0); // EntryPointAddr
|
|
W.write<uint32_t>(Sections[0]->Address); // TextStartAddr
|
|
W.write<uint32_t>(Sections[1]->Address); // DataStartAddr
|
|
}
|
|
|
|
void XCOFFWriter::writeSectionHeader(const SectionEntry *Sec) {
|
|
bool IsDwarf = (Sec->Flags & XCOFF::STYP_DWARF) != 0;
|
|
bool IsOvrflo = (Sec->Flags & XCOFF::STYP_OVRFLO) != 0;
|
|
// Nothing to write for this Section.
|
|
if (Sec->Index == SectionEntry::UninitializedIndex)
|
|
return;
|
|
|
|
// Write Name.
|
|
ArrayRef<char> NameRef(Sec->Name, XCOFF::NameSize);
|
|
W.write(NameRef);
|
|
|
|
// Write the Physical Address and Virtual Address.
|
|
// We use 0 for DWARF sections' Physical and Virtual Addresses.
|
|
writeWord(IsDwarf ? 0 : Sec->Address);
|
|
// Since line number is not supported, we set it to 0 for overflow sections.
|
|
writeWord((IsDwarf || IsOvrflo) ? 0 : Sec->Address);
|
|
|
|
writeWord(Sec->Size);
|
|
writeWord(Sec->FileOffsetToData);
|
|
writeWord(Sec->FileOffsetToRelocations);
|
|
writeWord(0); // FileOffsetToLineNumberInfo. Not supported yet.
|
|
|
|
if (is64Bit()) {
|
|
W.write<uint32_t>(Sec->RelocationCount);
|
|
W.write<uint32_t>(0); // NumberOfLineNumbers. Not supported yet.
|
|
W.write<int32_t>(Sec->Flags);
|
|
W.OS.write_zeros(4);
|
|
} else {
|
|
// For the overflow section header, s_nreloc provides a reference to the
|
|
// primary section header and s_nlnno must have the same value.
|
|
// For common section headers, if either of s_nreloc or s_nlnno are set to
|
|
// 65535, the other one must also be set to 65535.
|
|
W.write<uint16_t>(Sec->RelocationCount);
|
|
W.write<uint16_t>((IsOvrflo || Sec->RelocationCount == XCOFF::RelocOverflow)
|
|
? Sec->RelocationCount
|
|
: 0); // NumberOfLineNumbers. Not supported yet.
|
|
W.write<int32_t>(Sec->Flags);
|
|
}
|
|
}
|
|
|
|
void XCOFFWriter::writeSectionHeaderTable() {
|
|
for (const auto *CsectSec : Sections)
|
|
writeSectionHeader(CsectSec);
|
|
for (const auto &DwarfSec : DwarfSections)
|
|
writeSectionHeader(&DwarfSec);
|
|
for (const auto &OverflowSec : OverflowSections)
|
|
writeSectionHeader(&OverflowSec);
|
|
if (hasExceptionSection())
|
|
writeSectionHeader(&ExceptionSection);
|
|
if (CInfoSymSection.Entry)
|
|
writeSectionHeader(&CInfoSymSection);
|
|
}
|
|
|
|
void XCOFFWriter::writeRelocation(XCOFFRelocation Reloc,
|
|
const XCOFFSection &Section) {
|
|
if (Section.MCSec->isCsect())
|
|
writeWord(Section.Address + Reloc.FixupOffsetInCsect);
|
|
else {
|
|
// DWARF sections' address is set to 0.
|
|
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 XCOFFWriter::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);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const auto &DwarfSection : DwarfSections)
|
|
for (const auto &Reloc : DwarfSection.DwarfSect->Relocations)
|
|
writeRelocation(Reloc, *DwarfSection.DwarfSect);
|
|
}
|
|
|
|
void XCOFFWriter::writeSymbolTable(MCAssembler &Asm) {
|
|
// Write C_FILE symbols.
|
|
StringRef Vers = CompilerVersion;
|
|
|
|
for (const std::pair<std::string, size_t> &F : FileNames) {
|
|
// The n_name of a C_FILE symbol is the source file's name when no auxiliary
|
|
// entries are present.
|
|
StringRef FileName = F.first;
|
|
|
|
// For C_FILE symbols, the Source Language ID overlays the high-order byte
|
|
// of the SymbolType field, and the CPU Version ID is defined as the
|
|
// low-order byte.
|
|
// AIX's system assembler determines the source language ID based on the
|
|
// source file's name suffix, and the behavior here is consistent with it.
|
|
uint8_t LangID;
|
|
if (FileName.ends_with(".c"))
|
|
LangID = XCOFF::TB_C;
|
|
else if (FileName.ends_with_insensitive(".f") ||
|
|
FileName.ends_with_insensitive(".f77") ||
|
|
FileName.ends_with_insensitive(".f90") ||
|
|
FileName.ends_with_insensitive(".f95") ||
|
|
FileName.ends_with_insensitive(".f03") ||
|
|
FileName.ends_with_insensitive(".f08"))
|
|
LangID = XCOFF::TB_Fortran;
|
|
else
|
|
LangID = XCOFF::TB_CPLUSPLUS;
|
|
|
|
uint8_t CpuID = XCOFF::getCpuID(getCPUType());
|
|
|
|
int NumberOfFileAuxEntries = 1;
|
|
if (!Vers.empty())
|
|
++NumberOfFileAuxEntries;
|
|
writeSymbolEntry(".file", /*Value=*/0, XCOFF::ReservedSectionNum::N_DEBUG,
|
|
/*SymbolType=*/(LangID << 8) | CpuID, XCOFF::C_FILE,
|
|
NumberOfFileAuxEntries);
|
|
writeSymbolAuxFileEntry(FileName, XCOFF::XFT_FN);
|
|
if (!Vers.empty())
|
|
writeSymbolAuxFileEntry(Vers, XCOFF::XFT_CV);
|
|
}
|
|
|
|
if (CInfoSymSection.Entry)
|
|
writeSymbolEntry(CInfoSymSection.Entry->Name, CInfoSymSection.Entry->Offset,
|
|
CInfoSymSection.Index,
|
|
/*SymbolType=*/0, XCOFF::C_INFO,
|
|
/*NumberOfAuxEntries=*/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)
|
|
// 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.
|
|
writeSymbolEntryForControlSection(Csect, SectionIndex,
|
|
Csect.MCSec->getStorageClass());
|
|
|
|
for (const auto &Sym : Csect.Syms)
|
|
writeSymbolEntryForCsectMemberLabel(
|
|
Sym, Csect, SectionIndex, Asm.getSymbolOffset(*(Sym.MCSym)));
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const auto &DwarfSection : DwarfSections)
|
|
writeSymbolEntryForDwarfSection(*DwarfSection.DwarfSect,
|
|
DwarfSection.Index);
|
|
}
|
|
|
|
void XCOFFWriter::finalizeRelocationInfo(SectionEntry *Sec, uint64_t RelCount) {
|
|
// Handles relocation field overflows in an XCOFF32 file. An XCOFF64 file
|
|
// may not contain an overflow section header.
|
|
if (!is64Bit() && (RelCount >= static_cast<uint32_t>(XCOFF::RelocOverflow))) {
|
|
// Generate an overflow section header.
|
|
SectionEntry SecEntry(".ovrflo", XCOFF::STYP_OVRFLO);
|
|
|
|
// This field specifies the file section number of the section header that
|
|
// overflowed.
|
|
SecEntry.RelocationCount = Sec->Index;
|
|
|
|
// This field specifies the number of relocation entries actually
|
|
// required.
|
|
SecEntry.Address = RelCount;
|
|
SecEntry.Index = ++SectionCount;
|
|
OverflowSections.push_back(std::move(SecEntry));
|
|
|
|
// The field in the primary section header is always 65535
|
|
// (XCOFF::RelocOverflow).
|
|
Sec->RelocationCount = XCOFF::RelocOverflow;
|
|
} else {
|
|
Sec->RelocationCount = RelCount;
|
|
}
|
|
}
|
|
|
|
void XCOFFWriter::calcOffsetToRelocations(SectionEntry *Sec,
|
|
uint64_t &RawPointer) {
|
|
if (!Sec->RelocationCount)
|
|
return;
|
|
|
|
Sec->FileOffsetToRelocations = RawPointer;
|
|
uint64_t RelocationSizeInSec = 0;
|
|
if (!is64Bit() &&
|
|
Sec->RelocationCount == static_cast<uint32_t>(XCOFF::RelocOverflow)) {
|
|
// Find its corresponding overflow section.
|
|
for (auto &OverflowSec : OverflowSections) {
|
|
if (OverflowSec.RelocationCount == static_cast<uint32_t>(Sec->Index)) {
|
|
RelocationSizeInSec =
|
|
OverflowSec.Address * XCOFF::RelocationSerializationSize32;
|
|
|
|
// This field must have the same values as in the corresponding
|
|
// primary section header.
|
|
OverflowSec.FileOffsetToRelocations = Sec->FileOffsetToRelocations;
|
|
}
|
|
}
|
|
assert(RelocationSizeInSec && "Overflow section header doesn't exist.");
|
|
} else {
|
|
RelocationSizeInSec = Sec->RelocationCount *
|
|
(is64Bit() ? XCOFF::RelocationSerializationSize64
|
|
: XCOFF::RelocationSerializationSize32);
|
|
}
|
|
|
|
RawPointer += RelocationSizeInSec;
|
|
if (RawPointer > MaxRawDataSize)
|
|
report_fatal_error("Relocation data overflowed this object file.");
|
|
}
|
|
|
|
void XCOFFWriter::finalizeSectionInfo() {
|
|
for (auto *Section : Sections) {
|
|
if (Section->Index == SectionEntry::UninitializedIndex)
|
|
// Nothing to record for this Section.
|
|
continue;
|
|
|
|
uint64_t RelCount = 0;
|
|
for (const auto *Group : Section->Groups) {
|
|
if (Group->empty())
|
|
continue;
|
|
|
|
for (auto &Csect : *Group)
|
|
RelCount += Csect.Relocations.size();
|
|
}
|
|
finalizeRelocationInfo(Section, RelCount);
|
|
}
|
|
|
|
for (auto &DwarfSection : DwarfSections)
|
|
finalizeRelocationInfo(&DwarfSection,
|
|
DwarfSection.DwarfSect->Relocations.size());
|
|
|
|
// Calculate the RawPointer value for all headers.
|
|
uint64_t RawPointer =
|
|
(is64Bit() ? (XCOFF::FileHeaderSize64 +
|
|
SectionCount * XCOFF::SectionHeaderSize64)
|
|
: (XCOFF::FileHeaderSize32 +
|
|
SectionCount * XCOFF::SectionHeaderSize32)) +
|
|
auxiliaryHeaderSize();
|
|
|
|
// Calculate the file offset to the section data.
|
|
for (auto *Sec : Sections) {
|
|
if (Sec->Index == SectionEntry::UninitializedIndex || Sec->IsVirtual)
|
|
continue;
|
|
|
|
RawPointer = Sec->advanceFileOffset(MaxRawDataSize, RawPointer);
|
|
}
|
|
|
|
if (!DwarfSections.empty()) {
|
|
RawPointer += PaddingsBeforeDwarf;
|
|
for (auto &DwarfSection : DwarfSections) {
|
|
RawPointer = DwarfSection.advanceFileOffset(MaxRawDataSize, RawPointer);
|
|
}
|
|
}
|
|
|
|
if (hasExceptionSection())
|
|
RawPointer = ExceptionSection.advanceFileOffset(MaxRawDataSize, RawPointer);
|
|
|
|
if (CInfoSymSection.Entry)
|
|
RawPointer = CInfoSymSection.advanceFileOffset(MaxRawDataSize, RawPointer);
|
|
|
|
for (auto *Sec : Sections) {
|
|
if (Sec->Index != SectionEntry::UninitializedIndex)
|
|
calcOffsetToRelocations(Sec, RawPointer);
|
|
}
|
|
|
|
for (auto &DwarfSec : DwarfSections)
|
|
calcOffsetToRelocations(&DwarfSec, RawPointer);
|
|
|
|
// TODO Error check that the number of symbol table entries fits in 32-bits
|
|
// signed ...
|
|
if (SymbolTableEntryCount)
|
|
SymbolTableOffset = RawPointer;
|
|
}
|
|
|
|
void XCOFFWriter::addExceptionEntry(const MCSymbol *Symbol,
|
|
const MCSymbol *Trap, unsigned LanguageCode,
|
|
unsigned ReasonCode, unsigned FunctionSize,
|
|
bool hasDebug) {
|
|
// If a module had debug info, debugging is enabled and XCOFF emits the
|
|
// exception auxilliary entry.
|
|
if (hasDebug)
|
|
ExceptionSection.isDebugEnabled = true;
|
|
auto Entry = ExceptionSection.ExceptionTable.find(Symbol->getName());
|
|
if (Entry != ExceptionSection.ExceptionTable.end()) {
|
|
Entry->second.Entries.push_back(
|
|
ExceptionTableEntry(Trap, LanguageCode, ReasonCode));
|
|
return;
|
|
}
|
|
ExceptionInfo NewEntry;
|
|
NewEntry.FunctionSymbol = Symbol;
|
|
NewEntry.FunctionSize = FunctionSize;
|
|
NewEntry.Entries.push_back(
|
|
ExceptionTableEntry(Trap, LanguageCode, ReasonCode));
|
|
ExceptionSection.ExceptionTable.insert(
|
|
std::pair<const StringRef, ExceptionInfo>(Symbol->getName(), NewEntry));
|
|
}
|
|
|
|
unsigned XCOFFWriter::getExceptionSectionSize() {
|
|
unsigned EntryNum = 0;
|
|
|
|
for (const auto &TableEntry : ExceptionSection.ExceptionTable)
|
|
// The size() gets +1 to account for the initial entry containing the
|
|
// symbol table index.
|
|
EntryNum += TableEntry.second.Entries.size() + 1;
|
|
|
|
return EntryNum * (is64Bit() ? XCOFF::ExceptionSectionEntrySize64
|
|
: XCOFF::ExceptionSectionEntrySize32);
|
|
}
|
|
|
|
unsigned XCOFFWriter::getExceptionOffset(const MCSymbol *Symbol) {
|
|
unsigned EntryNum = 0;
|
|
for (const auto &TableEntry : ExceptionSection.ExceptionTable) {
|
|
if (Symbol == TableEntry.second.FunctionSymbol)
|
|
break;
|
|
EntryNum += TableEntry.second.Entries.size() + 1;
|
|
}
|
|
return EntryNum * (is64Bit() ? XCOFF::ExceptionSectionEntrySize64
|
|
: XCOFF::ExceptionSectionEntrySize32);
|
|
}
|
|
|
|
void XCOFFWriter::addCInfoSymEntry(StringRef Name, StringRef Metadata) {
|
|
assert(!CInfoSymSection.Entry && "Multiple entries are not supported");
|
|
CInfoSymSection.addEntry(
|
|
std::make_unique<CInfoSymInfo>(Name.str(), Metadata.str()));
|
|
}
|
|
|
|
void XCOFFWriter::assignAddressesAndIndices(MCAssembler &Asm) {
|
|
// The symbol table starts with all the C_FILE symbols. Each C_FILE symbol
|
|
// requires 1 or 2 auxiliary entries.
|
|
uint32_t SymbolTableIndex =
|
|
(2 + (CompilerVersion.empty() ? 0 : 1)) * FileNames.size();
|
|
|
|
if (CInfoSymSection.Entry)
|
|
SymbolTableIndex++;
|
|
|
|
// 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.
|
|
uint64_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->getAlign());
|
|
Csect.Size = Asm.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) {
|
|
bool hasExceptEntry = false;
|
|
auto Entry =
|
|
ExceptionSection.ExceptionTable.find(Sym.MCSym->getName());
|
|
if (Entry != ExceptionSection.ExceptionTable.end()) {
|
|
hasExceptEntry = true;
|
|
for (auto &TrapEntry : Entry->second.Entries) {
|
|
TrapEntry.TrapAddress = Asm.getSymbolOffset(*(Sym.MCSym)) +
|
|
TrapEntry.Trap->getOffset();
|
|
}
|
|
}
|
|
Sym.SymbolTableIndex = SymbolTableIndex;
|
|
SymbolIndexMap[Sym.MCSym] = Sym.SymbolTableIndex;
|
|
// 1 main and 1 auxiliary symbol table entry for each contained
|
|
// symbol. For symbols with exception section entries, a function
|
|
// auxilliary entry is needed, and on 64-bit XCOFF with debugging
|
|
// enabled, an additional exception auxilliary entry is needed.
|
|
SymbolTableIndex += 2;
|
|
if (hasExceptionSection() && hasExceptEntry) {
|
|
if (is64Bit() && ExceptionSection.isDebugEnabled)
|
|
SymbolTableIndex += 2;
|
|
else
|
|
SymbolTableIndex += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// Start to generate DWARF sections. Sections other than DWARF section use
|
|
// DefaultSectionAlign as the default alignment, while DWARF sections have
|
|
// their own alignments. If these two alignments are not the same, we need
|
|
// some paddings here and record the paddings bytes for FileOffsetToData
|
|
// calculation.
|
|
if (!DwarfSections.empty())
|
|
PaddingsBeforeDwarf =
|
|
alignTo(Address,
|
|
(*DwarfSections.begin()).DwarfSect->MCSec->getAlign()) -
|
|
Address;
|
|
|
|
DwarfSectionEntry *LastDwarfSection = nullptr;
|
|
for (auto &DwarfSection : DwarfSections) {
|
|
assert((SectionIndex <= MaxSectionIndex) && "Section index overflow!");
|
|
|
|
XCOFFSection &DwarfSect = *DwarfSection.DwarfSect;
|
|
const MCSectionXCOFF *MCSec = DwarfSect.MCSec;
|
|
|
|
// Section index.
|
|
DwarfSection.Index = SectionIndex++;
|
|
SectionCount++;
|
|
|
|
// Symbol index.
|
|
DwarfSect.SymbolTableIndex = SymbolTableIndex;
|
|
SymbolIndexMap[MCSec->getQualNameSymbol()] = DwarfSect.SymbolTableIndex;
|
|
// 1 main and 1 auxiliary symbol table entry for the csect.
|
|
SymbolTableIndex += 2;
|
|
|
|
// Section address. Make it align to section alignment.
|
|
// We use address 0 for DWARF sections' Physical and Virtual Addresses.
|
|
// This address is used to tell where is the section in the final object.
|
|
// See writeSectionForDwarfSectionEntry().
|
|
DwarfSection.Address = DwarfSect.Address =
|
|
alignTo(Address, MCSec->getAlign());
|
|
|
|
// Section size.
|
|
// For DWARF section, we must use the real size which may be not aligned.
|
|
DwarfSection.Size = DwarfSect.Size = Asm.getSectionAddressSize(*MCSec);
|
|
|
|
Address = DwarfSection.Address + DwarfSection.Size;
|
|
|
|
if (LastDwarfSection)
|
|
LastDwarfSection->MemorySize =
|
|
DwarfSection.Address - LastDwarfSection->Address;
|
|
LastDwarfSection = &DwarfSection;
|
|
}
|
|
if (LastDwarfSection) {
|
|
// Make the final DWARF section address align to the default section
|
|
// alignment for follow contents.
|
|
Address = alignTo(LastDwarfSection->Address + LastDwarfSection->Size,
|
|
DefaultSectionAlign);
|
|
LastDwarfSection->MemorySize = Address - LastDwarfSection->Address;
|
|
}
|
|
if (hasExceptionSection()) {
|
|
ExceptionSection.Index = SectionIndex++;
|
|
SectionCount++;
|
|
ExceptionSection.Address = 0;
|
|
ExceptionSection.Size = getExceptionSectionSize();
|
|
Address += ExceptionSection.Size;
|
|
Address = alignTo(Address, DefaultSectionAlign);
|
|
}
|
|
|
|
if (CInfoSymSection.Entry) {
|
|
CInfoSymSection.Index = SectionIndex++;
|
|
SectionCount++;
|
|
CInfoSymSection.Address = 0;
|
|
Address += CInfoSymSection.Size;
|
|
Address = alignTo(Address, DefaultSectionAlign);
|
|
}
|
|
|
|
SymbolTableEntryCount = SymbolTableIndex;
|
|
}
|
|
|
|
void XCOFFWriter::writeSectionForControlSectionEntry(
|
|
const MCAssembler &Asm, const CsectSectionEntry &CsectEntry,
|
|
uint64_t &CurrentAddressLocation) {
|
|
// Nothing to write for this Section.
|
|
if (CsectEntry.Index == SectionEntry::UninitializedIndex)
|
|
return;
|
|
|
|
// There could be a gap (without corresponding zero padding) between
|
|
// sections.
|
|
// There could be a gap (without corresponding zero padding) between
|
|
// sections.
|
|
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;
|
|
|
|
// For virtual sections, nothing to write. But need to increase
|
|
// CurrentAddressLocation for later sections like DWARF section has a correct
|
|
// writing location.
|
|
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);
|
|
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 end virtual address of the last csect
|
|
// in that section.
|
|
if (uint64_t PaddingSize =
|
|
CsectEntry.Address + CsectEntry.Size - CurrentAddressLocation) {
|
|
W.OS.write_zeros(PaddingSize);
|
|
CurrentAddressLocation += PaddingSize;
|
|
}
|
|
}
|
|
|
|
void XCOFFWriter::writeSectionForDwarfSectionEntry(
|
|
const MCAssembler &Asm, const DwarfSectionEntry &DwarfEntry,
|
|
uint64_t &CurrentAddressLocation) {
|
|
// There could be a gap (without corresponding zero padding) between
|
|
// sections. For example DWARF section alignment is bigger than
|
|
// DefaultSectionAlign.
|
|
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);
|
|
|
|
CurrentAddressLocation = DwarfEntry.Address + DwarfEntry.Size;
|
|
|
|
// DWARF section size is not aligned to DefaultSectionAlign.
|
|
// Make sure CurrentAddressLocation is aligned to DefaultSectionAlign.
|
|
uint32_t Mod = CurrentAddressLocation % DefaultSectionAlign;
|
|
uint32_t TailPaddingSize = Mod ? DefaultSectionAlign - Mod : 0;
|
|
if (TailPaddingSize)
|
|
W.OS.write_zeros(TailPaddingSize);
|
|
|
|
CurrentAddressLocation += TailPaddingSize;
|
|
}
|
|
|
|
void XCOFFWriter::writeSectionForExceptionSectionEntry(
|
|
const MCAssembler &Asm, ExceptionSectionEntry &ExceptionEntry,
|
|
uint64_t &CurrentAddressLocation) {
|
|
for (const auto &TableEntry : ExceptionEntry.ExceptionTable) {
|
|
// For every symbol that has exception entries, you must start the entries
|
|
// with an initial symbol table index entry
|
|
W.write<uint32_t>(SymbolIndexMap[TableEntry.second.FunctionSymbol]);
|
|
if (is64Bit()) {
|
|
// 4-byte padding on 64-bit.
|
|
W.OS.write_zeros(4);
|
|
}
|
|
W.OS.write_zeros(2);
|
|
for (auto &TrapEntry : TableEntry.second.Entries) {
|
|
writeWord(TrapEntry.TrapAddress);
|
|
W.write<uint8_t>(TrapEntry.Lang);
|
|
W.write<uint8_t>(TrapEntry.Reason);
|
|
}
|
|
}
|
|
|
|
CurrentAddressLocation += getExceptionSectionSize();
|
|
}
|
|
|
|
void XCOFFWriter::writeSectionForCInfoSymSectionEntry(
|
|
const MCAssembler &Asm, CInfoSymSectionEntry &CInfoSymEntry,
|
|
uint64_t &CurrentAddressLocation) {
|
|
if (!CInfoSymSection.Entry)
|
|
return;
|
|
|
|
constexpr int WordSize = sizeof(uint32_t);
|
|
std::unique_ptr<CInfoSymInfo> &CISI = CInfoSymEntry.Entry;
|
|
const std::string &Metadata = CISI->Metadata;
|
|
|
|
// Emit the 4-byte length of the metadata.
|
|
W.write<uint32_t>(Metadata.size());
|
|
|
|
if (Metadata.size() == 0)
|
|
return;
|
|
|
|
// Write out the payload one word at a time.
|
|
size_t Index = 0;
|
|
while (Index + WordSize <= Metadata.size()) {
|
|
uint32_t NextWord =
|
|
llvm::support::endian::read32be(Metadata.data() + Index);
|
|
W.write<uint32_t>(NextWord);
|
|
Index += WordSize;
|
|
}
|
|
|
|
// If there is padding, we have at least one byte of payload left to emit.
|
|
if (CISI->paddingSize()) {
|
|
std::array<uint8_t, WordSize> LastWord = {0};
|
|
::memcpy(LastWord.data(), Metadata.data() + Index, Metadata.size() - Index);
|
|
W.write<uint32_t>(llvm::support::endian::read32be(LastWord.data()));
|
|
}
|
|
|
|
CurrentAddressLocation += CISI->size();
|
|
}
|
|
|
|
// 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 Log2Align = Log2(Sec->getAlign());
|
|
// 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<XCOFFWriter>(std::move(MOTW), OS);
|
|
}
|