//===-- 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/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 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 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; support::endian::Writer W; std::unique_ptr TargetObjectWriter; StringTableBuilder Strings; // The non-empty sections, in the order they will appear in the section header // table. std::vector
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; 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 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 MOTW, raw_pwrite_stream &OS); }; XCOFFObjectWriter::XCOFFObjectWriter( std::unique_ptr MOTW, raw_pwrite_stream &OS) : 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 WrapperMap; for (const auto &S : Asm) { const auto *MCSec = cast(&S); assert(WrapperMap.find(MCSec) == WrapperMap.end() && "Cannot add a csect 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->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(&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 &, const MCFragment *, const MCFixup &, MCValue, uint64_t &) { report_fatal_error("XCOFF relocations not supported."); } 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. 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."); uint64_t StartOffset = W.OS.tell(); writeFileHeader(); writeSectionHeaderTable(); writeSections(Asm, Layout); // TODO 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(0); W.write(Strings.getOffset(SymbolName)); } else { char Name[XCOFF::NameSize]; std::strncpy(Name, SymbolName.data(), XCOFF::NameSize); ArrayRef 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(CSectionRef.Address + SymbolOffset); W.write(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(0); W.write(SymbolRef.getStorageClass()); // Always 1 aux entry for now. W.write(1); // Now output the auxiliary entry. W.write(CSectionRef.SymbolTableIndex); // Parameter typecheck hash. Not supported. W.write(0); // Typecheck section number. Not supported. W.write(0); // Symbol type: Label W.write(XCOFF::XTY_LD); // Storage mapping class. W.write(CSectionRef.MCCsect->getMappingClass()); // Reserved (x_stab). W.write(0); // Reserved (x_snstab). W.write(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(CSectionRef.Address); // n_scnum W.write(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(0); // n_sclass W.write(StorageClass); // Always 1 aux entry for now. W.write(1); // Now output the auxiliary entry. W.write(CSectionRef.Size); // Parameter typecheck hash. Not supported. W.write(0); // Typecheck section number. Not supported. W.write(0); // Symbol type. W.write(getEncodedType(CSectionRef.MCCsect)); // Storage mapping class. W.write(CSectionRef.MCCsect->getMappingClass()); // Reserved (x_stab). W.write(0); // Reserved (x_snstab). W.write(0); } void XCOFFObjectWriter::writeFileHeader() { // Magic. W.write(0x01df); // Number of sections. W.write(Sections.size()); // Timestamp field. For reproducible output we write a 0, which represents no // timestamp. W.write(0); // Byte Offset to the start of the symbol table. W.write(SymbolTableOffset); // Number of entries in the symbol table. W.write(SymbolTableEntryCount); // Size of the optional header. W.write(0); // Flags. W.write(0); } void XCOFFObjectWriter::writeSectionHeaderTable() { for (const auto *Sec : Sections) { // Write Name. ArrayRef 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(Sec->Address); W.write(Sec->Address); W.write(Sec->Size); W.write(Sec->FileOffsetToData); // Relocation pointer and Lineno pointer. Not supported yet. W.write(0); W.write(0); // Relocation and line-number counts. Not supported yet. W.write(0); W.write(0); W.write(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 std::unique_ptr llvm::createXCOFFObjectWriter(std::unique_ptr MOTW, raw_pwrite_stream &OS) { return std::make_unique(std::move(MOTW), OS); }