diff options
Diffstat (limited to 'lib/MC/XCOFFObjectWriter.cpp')
-rw-r--r-- | lib/MC/XCOFFObjectWriter.cpp | 533 |
1 files changed, 521 insertions, 12 deletions
diff --git a/lib/MC/XCOFFObjectWriter.cpp b/lib/MC/XCOFFObjectWriter.cpp index 9b9a7b6c118c..353c21068735 100644 --- a/lib/MC/XCOFFObjectWriter.cpp +++ b/lib/MC/XCOFFObjectWriter.cpp @@ -10,18 +10,135 @@ // //===----------------------------------------------------------------------===// +#include "llvm/BinaryFormat/XCOFF.h" +#include "llvm/MC/MCAsmLayout.h" #include "llvm/MC/MCAssembler.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/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; + +// Packs the csect's alignment and type into a byte. +uint8_t getEncodedType(const MCSectionXCOFF *); + +// Wrapper around an MCSymbolXCOFF. +struct Symbol { + const MCSymbolXCOFF *const MCSym; + uint32_t SymbolTableIndex; + + XCOFF::StorageClass getStorageClass() const { + return MCSym->getStorageClass(); + } + StringRef getName() const { return MCSym->getName(); } + Symbol(const MCSymbolXCOFF *MCSym) : MCSym(MCSym), SymbolTableIndex(-1) {} +}; + +// Wrapper for an MCSectionXCOFF. +struct ControlSection { + const MCSectionXCOFF *const MCCsect; + uint32_t SymbolTableIndex; + uint32_t Address; + uint32_t Size; + + SmallVector<Symbol, 1> Syms; + StringRef getName() const { return MCCsect->getSectionName(); } + ControlSection(const MCSectionXCOFF *MCSec) + : MCCsect(MCSec), SymbolTableIndex(-1), Address(-1), Size(0) {} +}; + +// 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 Section { + 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; + + // Virtual sections do not need storage allocated in the object file. + const bool IsVirtual; + + void reset() { + Address = 0; + Size = 0; + FileOffsetToData = 0; + FileOffsetToRelocations = 0; + RelocationCount = 0; + Index = -1; + } + + Section(const char *N, XCOFF::SectionTypeFlags Flags, bool IsVirtual) + : Address(0), Size(0), FileOffsetToData(0), FileOffsetToRelocations(0), + RelocationCount(0), Flags(Flags), Index(-1), IsVirtual(IsVirtual) { + strncpy(Name, N, XCOFF::NameSize); + } +}; + class XCOFFObjectWriter : public MCObjectWriter { + // 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<ControlSection>; + support::endian::Writer W; std::unique_ptr<MCXCOFFObjectTargetWriter> TargetObjectWriter; + StringTableBuilder Strings; + + // The non-empty sections, in the order they will appear in the section header + // table. + std::vector<Section *> Sections; + + // The Predefined sections. + Section Text; + Section BSS; + + // 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 ProgramCodeCsects; + CsectGroup BSSCsects; + + uint32_t SymbolTableEntryCount = 0; + uint32_t SymbolTableOffset = 0; + + virtual void reset() override; void executePostLayoutBinding(MCAssembler &, const MCAsmLayout &) override; @@ -30,6 +147,40 @@ class XCOFFObjectWriter : public MCObjectWriter { uint64_t writeObject(MCAssembler &, const MCAsmLayout &) override; + static bool nameShouldBeInStringTable(const StringRef &); + void writeSymbolName(const StringRef &); + void writeSymbolTableEntryForCsectMemberLabel(const Symbol &, + const ControlSection &, int16_t, + uint64_t); + void writeSymbolTableEntryForControlSection(const ControlSection &, int16_t, + XCOFF::StorageClass); + void writeFileHeader(); + void writeSectionHeaderTable(); + void writeSections(const MCAssembler &Asm, const MCAsmLayout &Layout); + void writeSymbolTable(const MCAsmLayout &Layout); + + // 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 &); + + 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); @@ -37,11 +188,100 @@ public: XCOFFObjectWriter::XCOFFObjectWriter( std::unique_ptr<MCXCOFFObjectTargetWriter> MOTW, raw_pwrite_stream &OS) - : W(OS, support::big), TargetObjectWriter(std::move(MOTW)) {} + : W(OS, support::big), TargetObjectWriter(std::move(MOTW)), + Strings(StringTableBuilder::XCOFF), + Text(".text", XCOFF::STYP_TEXT, /* IsVirtual */ false), + BSS(".bss", XCOFF::STYP_BSS, /* IsVirtual */ true) {} + +void XCOFFObjectWriter::reset() { + // Reset any sections we have written to, and empty the section header table. + for (auto *Sec : Sections) + Sec->reset(); + Sections.clear(); + + // Clear any csects we have stored. + ProgramCodeCsects.clear(); + BSSCsects.clear(); + + // Reset the symbol table and string table. + SymbolTableEntryCount = 0; + SymbolTableOffset = 0; + Strings.clear(); + + MCObjectWriter::reset(); +} + +void XCOFFObjectWriter::executePostLayoutBinding(MCAssembler &Asm, + const MCAsmLayout &Layout) { + if (TargetObjectWriter->is64Bit()) + report_fatal_error("64-bit XCOFF object files are not supported yet."); + + // Maps the MC Section representation to its corresponding ControlSection + // wrapper. Needed for finding the ControlSection to insert an MCSymbol into + // from its containing MCSectionXCOFF. + DenseMap<const MCSectionXCOFF *, ControlSection *> WrapperMap; + + for (const auto &S : Asm) { + const auto *MCSec = cast<const MCSectionXCOFF>(&S); + assert(WrapperMap.find(MCSec) == WrapperMap.end() && + "Cannot add a csect twice."); -void XCOFFObjectWriter::executePostLayoutBinding(MCAssembler &, - const MCAsmLayout &) { - // TODO Implement once we have sections and symbols to handle. + // If the name does not fit in the storage provided in the symbol table + // entry, add it to the string table. + if (nameShouldBeInStringTable(MCSec->getSectionName())) + Strings.add(MCSec->getSectionName()); + + switch (MCSec->getMappingClass()) { + case XCOFF::XMC_PR: + assert(XCOFF::XTY_SD == MCSec->getCSectType() && + "Only an initialized csect can contain program code."); + ProgramCodeCsects.emplace_back(MCSec); + WrapperMap[MCSec] = &ProgramCodeCsects.back(); + break; + case XCOFF::XMC_RW: + if (XCOFF::XTY_CM == MCSec->getCSectType()) { + BSSCsects.emplace_back(MCSec); + WrapperMap[MCSec] = &BSSCsects.back(); + break; + } + report_fatal_error("Unhandled mapping of read-write csect to section."); + case XCOFF::XMC_TC0: + // TODO FIXME Handle emiting the TOC base. + break; + case XCOFF::XMC_BS: + assert(XCOFF::XTY_CM == MCSec->getCSectType() && + "Mapping invalid csect. CSECT with bss storage class must be " + "common type."); + BSSCsects.emplace_back(MCSec); + WrapperMap[MCSec] = &BSSCsects.back(); + break; + default: + report_fatal_error("Unhandled mapping of csect to section."); + } + } + + for (const MCSymbol &S : Asm.symbols()) { + // Nothing to do for temporary symbols. + if (S.isTemporary()) + continue; + const MCSymbolXCOFF *XSym = cast<MCSymbolXCOFF>(&S); + + // Map the symbol into its containing csect. + const MCSectionXCOFF *ContainingCsect = XSym->getContainingCsect(); + assert(WrapperMap.find(ContainingCsect) != WrapperMap.end() && + "Expected containing csect to exist in map"); + + // Lookup the containing csect and add the symbol to it. + WrapperMap[ContainingCsect]->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->getName())) + Strings.add(XSym->getName()); + } + + Strings.finalize(); + assignAddressesAndIndices(Layout); } void XCOFFObjectWriter::recordRelocation(MCAssembler &, const MCAsmLayout &, @@ -50,7 +290,29 @@ void XCOFFObjectWriter::recordRelocation(MCAssembler &, const MCAsmLayout &, report_fatal_error("XCOFF relocations not supported."); } -uint64_t XCOFFObjectWriter::writeObject(MCAssembler &Asm, const MCAsmLayout &) { +void XCOFFObjectWriter::writeSections(const MCAssembler &Asm, + const MCAsmLayout &Layout) { + // Write the program code control sections one at a time. + uint32_t CurrentAddressLocation = Text.Address; + for (const auto &Csect : ProgramCodeCsects) { + if (uint32_t PaddingSize = Csect.Address - CurrentAddressLocation) + W.OS.write_zeros(PaddingSize); + Asm.writeSectionData(W.OS, Csect.MCCsect, Layout); + CurrentAddressLocation = Csect.Address + Csect.Size; + } + + if (Text.Index != -1) { + // 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 = + Text.Address + Text.Size - CurrentAddressLocation) + W.OS.write_zeros(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. @@ -62,27 +324,274 @@ uint64_t XCOFFObjectWriter::writeObject(MCAssembler &Asm, const MCAsmLayout &) { uint64_t StartOffset = W.OS.tell(); - // TODO FIXME Assign section numbers/finalize sections. + writeFileHeader(); + writeSectionHeaderTable(); + writeSections(Asm, Layout); + // TODO writeRelocations(); - // TODO FIXME Finalize symbols. + 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]; + std::strncpy(Name, SymbolName.data(), XCOFF::NameSize); + ArrayRef<char> NameRef(Name, XCOFF::NameSize); + W.write(NameRef); + } +} + +void XCOFFObjectWriter::writeSymbolTableEntryForCsectMemberLabel( + const Symbol &SymbolRef, const ControlSection &CSectionRef, + int16_t SectionIndex, uint64_t SymbolOffset) { + // Name or Zeros and string table offset + writeSymbolName(SymbolRef.getName()); + 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.MCCsect->getMappingClass()); + // Reserved (x_stab). + W.write<uint32_t>(0); + // Reserved (x_snstab). + W.write<uint16_t>(0); +} + +void XCOFFObjectWriter::writeSymbolTableEntryForControlSection( + const ControlSection &CSectionRef, int16_t SectionIndex, + XCOFF::StorageClass StorageClass) { + // n_name, n_zeros, n_offset + writeSymbolName(CSectionRef.getName()); + // 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.MCCsect)); + // Storage mapping class. + W.write<uint8_t>(CSectionRef.MCCsect->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>(0); + W.write<uint16_t>(Sections.size()); // 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>(0); + W.write<uint32_t>(SymbolTableOffset); // Number of entries in the symbol table. - W.write<int32_t>(0); + W.write<int32_t>(SymbolTableEntryCount); // Size of the optional header. W.write<uint16_t>(0); // Flags. W.write<uint16_t>(0); +} - return W.OS.tell() - StartOffset; +void XCOFFObjectWriter::writeSectionHeaderTable() { + for (const auto *Sec : Sections) { + // 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); + + // Relocation pointer and Lineno pointer. Not supported yet. + W.write<uint32_t>(0); + W.write<uint32_t>(0); + + // Relocation and line-number counts. Not supported yet. + W.write<uint16_t>(0); + W.write<uint16_t>(0); + + W.write<int32_t>(Sec->Flags); + } +} + +void XCOFFObjectWriter::writeSymbolTable(const MCAsmLayout &Layout) { + // Print out symbol table for the program code. + for (const auto &Csect : ProgramCodeCsects) { + // Write out the control section first and then each symbol in it. + writeSymbolTableEntryForControlSection(Csect, Text.Index, + Csect.MCCsect->getStorageClass()); + for (const auto &Sym : Csect.Syms) + writeSymbolTableEntryForCsectMemberLabel( + Sym, Csect, Text.Index, Layout.getSymbolOffset(*Sym.MCSym)); + } + + // The BSS Section is special in that the csects must contain a single symbol, + // and the contained symbol cannot be represented in the symbol table as a + // label definition. + for (auto &Csect : BSSCsects) { + assert(Csect.Syms.size() == 1 && + "Uninitialized csect cannot contain more then 1 symbol."); + Symbol &Sym = Csect.Syms.back(); + writeSymbolTableEntryForControlSection(Csect, BSS.Index, + Sym.getStorageClass()); + } +} + +void XCOFFObjectWriter::assignAddressesAndIndices(const MCAsmLayout &Layout) { + // 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. + int16_t SectionIndex = 1; + // The first symbol table entry is for the file name. We are not emitting it + // yet, so start at index 0. + uint32_t SymbolTableIndex = 0; + + // Text section comes first. + if (!ProgramCodeCsects.empty()) { + Sections.push_back(&Text); + Text.Index = SectionIndex++; + for (auto &Csect : ProgramCodeCsects) { + const MCSectionXCOFF *MCSec = Csect.MCCsect; + Csect.Address = alignTo(Address, MCSec->getAlignment()); + Csect.Size = Layout.getSectionAddressSize(MCSec); + Address = Csect.Address + Csect.Size; + Csect.SymbolTableIndex = SymbolTableIndex; + // 1 main and 1 auxiliary symbol table entry for the csect. + SymbolTableIndex += 2; + for (auto &Sym : Csect.Syms) { + Sym.SymbolTableIndex = SymbolTableIndex; + // 1 main and 1 auxiliary symbol table entry for each contained symbol + SymbolTableIndex += 2; + } + } + Address = alignTo(Address, DefaultSectionAlign); + + // The first csect of a section can be aligned by adjusting the virtual + // address of its containing section instead of writing zeroes into the + // object file. + Text.Address = ProgramCodeCsects.front().Address; + + Text.Size = Address - Text.Address; + } + + // Data section Second. TODO + + // BSS Section third. + if (!BSSCsects.empty()) { + Sections.push_back(&BSS); + BSS.Index = SectionIndex++; + for (auto &Csect : BSSCsects) { + const MCSectionXCOFF *MCSec = Csect.MCCsect; + Csect.Address = alignTo(Address, MCSec->getAlignment()); + Csect.Size = Layout.getSectionAddressSize(MCSec); + Address = Csect.Address + Csect.Size; + Csect.SymbolTableIndex = SymbolTableIndex; + // 1 main and 1 auxiliary symbol table entry for the csect. + SymbolTableIndex += 2; + + assert(Csect.Syms.size() == 1 && + "csect in the BSS can only contain a single symbol."); + Csect.Syms[0].SymbolTableIndex = Csect.SymbolTableIndex; + } + // Pad out Address to the default alignment. This is to match how the system + // assembler handles the .bss section. Its size is always a multiple of 4. + Address = alignTo(Address, DefaultSectionAlign); + + BSS.Address = BSSCsects.front().Address; + BSS.Size = Address - BSS.Address; + } + + SymbolTableEntryCount = SymbolTableIndex; + + // Calculate the RawPointer value for each section. + uint64_t RawPointer = sizeof(XCOFF::FileHeader32) + auxiliaryHeaderSize() + + Sections.size() * sizeof(XCOFF::SectionHeader32); + for (auto *Sec : Sections) { + if (!Sec->IsVirtual) { + Sec->FileOffsetToData = RawPointer; + RawPointer += Sec->Size; + } + } + + // TODO Add in Relocation storage to the RawPointer Calculation. + // TODO What to align the SymbolTable to? + // TODO Error check that the number of symbol table entries fits in 32-bits + // signed ... + if (SymbolTableEntryCount) + SymbolTableOffset = 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 @@ -90,5 +599,5 @@ uint64_t XCOFFObjectWriter::writeObject(MCAssembler &Asm, const MCAsmLayout &) { std::unique_ptr<MCObjectWriter> llvm::createXCOFFObjectWriter(std::unique_ptr<MCXCOFFObjectTargetWriter> MOTW, raw_pwrite_stream &OS) { - return llvm::make_unique<XCOFFObjectWriter>(std::move(MOTW), OS); + return std::make_unique<XCOFFObjectWriter>(std::move(MOTW), OS); } |