//===- yaml2xcoff - Convert YAML to a xcoff object file -------------------===// // // 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 // //===----------------------------------------------------------------------===// /// /// \file /// The xcoff component of yaml2obj. /// //===----------------------------------------------------------------------===// #include "llvm/ADT/DenseMap.h" #include "llvm/BinaryFormat/XCOFF.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Object/XCOFFObjectFile.h" #include "llvm/ObjectYAML/ObjectYAML.h" #include "llvm/ObjectYAML/yaml2obj.h" #include "llvm/Support/EndianStream.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/LEB128.h" using namespace llvm; namespace { constexpr unsigned DefaultSectionAlign = 4; constexpr int16_t MaxSectionIndex = INT16_MAX; constexpr uint32_t MaxRawDataSize = UINT32_MAX; class XCOFFWriter { public: XCOFFWriter(XCOFFYAML::Object &Obj, raw_ostream &OS, yaml::ErrorHandler EH) : Obj(Obj), W(OS, support::big), ErrHandler(EH), Strings(StringTableBuilder::XCOFF) { Is64Bit = Obj.Header.Magic == (llvm::yaml::Hex16)XCOFF::XCOFF64; } bool writeXCOFF(); private: bool nameShouldBeInStringTable(StringRef SymbolName); bool initFileHeader(uint64_t CurrentOffset); bool initSectionHeader(uint64_t &CurrentOffset); bool initRelocations(uint64_t &CurrentOffset); bool assignAddressesAndIndices(); void writeFileHeader(); void writeSectionHeader(); bool writeSectionData(); bool writeRelocations(); bool writeSymbols(); XCOFFYAML::Object &Obj; bool Is64Bit = false; support::endian::Writer W; yaml::ErrorHandler ErrHandler; StringTableBuilder Strings; uint64_t StartOffset; // Map the section name to its corrresponding section index. DenseMap SectionIndexMap = { {StringRef("N_DEBUG"), XCOFF::N_DEBUG}, {StringRef("N_ABS"), XCOFF::N_ABS}, {StringRef("N_UNDEF"), XCOFF::N_UNDEF}}; XCOFFYAML::FileHeader InitFileHdr = Obj.Header; std::vector InitSections = Obj.Sections; }; static void writeName(StringRef StrName, support::endian::Writer W) { char Name[XCOFF::NameSize]; memset(Name, 0, XCOFF::NameSize); char SrcName[] = ""; memcpy(Name, StrName.size() ? StrName.data() : SrcName, StrName.size()); ArrayRef NameRef(Name, XCOFF::NameSize); W.write(NameRef); } bool XCOFFWriter::nameShouldBeInStringTable(StringRef SymbolName) { return SymbolName.size() > XCOFF::NameSize; } bool XCOFFWriter::initRelocations(uint64_t &CurrentOffset) { for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) { if (!InitSections[I].Relocations.empty()) { InitSections[I].NumberOfRelocations = InitSections[I].Relocations.size(); InitSections[I].FileOffsetToRelocations = CurrentOffset; CurrentOffset += InitSections[I].NumberOfRelocations * XCOFF::RelocationSerializationSize32; if (CurrentOffset > MaxRawDataSize) { ErrHandler("maximum object size of" + Twine(MaxRawDataSize) + "exceeded when writing relocation data"); return false; } } } return true; } bool XCOFFWriter::initSectionHeader(uint64_t &CurrentOffset) { uint64_t CurrentSecAddr = 0; for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) { if (CurrentOffset > MaxRawDataSize) { ErrHandler("maximum object size of" + Twine(MaxRawDataSize) + "exceeded when writing section data"); return false; } // Assign indices for sections. if (InitSections[I].SectionName.size() && !SectionIndexMap[InitSections[I].SectionName]) { // The section index starts from 1. SectionIndexMap[InitSections[I].SectionName] = I + 1; if ((I + 1) > MaxSectionIndex) { ErrHandler("exceeded the maximum permitted section index of " + Twine(MaxSectionIndex)); return false; } } // Calculate the physical/virtual address. This field should contain 0 for // all sections except the text, data and bss sections. if (InitSections[I].Flags != XCOFF::STYP_TEXT && InitSections[I].Flags != XCOFF::STYP_DATA && InitSections[I].Flags != XCOFF::STYP_BSS) InitSections[I].Address = 0; else InitSections[I].Address = CurrentSecAddr; // Calculate the FileOffsetToData and data size for sections. if (InitSections[I].SectionData.binary_size()) { InitSections[I].FileOffsetToData = CurrentOffset; CurrentOffset += InitSections[I].SectionData.binary_size(); // Ensure the offset is aligned to DefaultSectionAlign. CurrentOffset = alignTo(CurrentOffset, DefaultSectionAlign); InitSections[I].Size = CurrentOffset - InitSections[I].FileOffsetToData; CurrentSecAddr += InitSections[I].Size; } } return initRelocations(CurrentOffset); } bool XCOFFWriter::initFileHeader(uint64_t CurrentOffset) { // The default format of the object file is XCOFF32. InitFileHdr.Magic = XCOFF::XCOFF32; InitFileHdr.NumberOfSections = Obj.Sections.size(); InitFileHdr.NumberOfSymTableEntries = Obj.Symbols.size(); for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) { // Add the number of auxiliary symbols to the total number. InitFileHdr.NumberOfSymTableEntries += YamlSym.NumberOfAuxEntries; if (nameShouldBeInStringTable(YamlSym.SymbolName)) Strings.add(YamlSym.SymbolName); } // Finalize the string table. Strings.finalize(); // Calculate SymbolTableOffset for the file header. if (InitFileHdr.NumberOfSymTableEntries) { InitFileHdr.SymbolTableOffset = CurrentOffset; CurrentOffset += InitFileHdr.NumberOfSymTableEntries * XCOFF::SymbolTableEntrySize; if (CurrentOffset > MaxRawDataSize) { ErrHandler("maximum object size of" + Twine(MaxRawDataSize) + "exceeded when writing symbols"); return false; } } // TODO: Calculate FileOffsetToLineNumbers when line number supported. return true; } bool XCOFFWriter::assignAddressesAndIndices() { Strings.clear(); uint64_t CurrentOffset = XCOFF::FileHeaderSize32 /* TODO: + auxiliaryHeaderSize() */ + InitSections.size() * XCOFF::SectionHeaderSize32; // Calculate section header info. if (!initSectionHeader(CurrentOffset)) return false; // Calculate file header info. return initFileHeader(CurrentOffset); } void XCOFFWriter::writeFileHeader() { W.write(Obj.Header.Magic ? Obj.Header.Magic : InitFileHdr.Magic); W.write(Obj.Header.NumberOfSections ? Obj.Header.NumberOfSections : InitFileHdr.NumberOfSections); W.write(Obj.Header.TimeStamp); W.write(Obj.Header.SymbolTableOffset ? Obj.Header.SymbolTableOffset : InitFileHdr.SymbolTableOffset); W.write(Obj.Header.NumberOfSymTableEntries ? Obj.Header.NumberOfSymTableEntries : InitFileHdr.NumberOfSymTableEntries); W.write(Obj.Header.AuxHeaderSize); W.write(Obj.Header.Flags); } void XCOFFWriter::writeSectionHeader() { for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) { XCOFFYAML::Section YamlSec = Obj.Sections[I]; XCOFFYAML::Section DerivedSec = InitSections[I]; writeName(YamlSec.SectionName, W); // Virtual address is the same as physical address. uint32_t SectionAddress = YamlSec.Address ? YamlSec.Address : DerivedSec.Address; W.write(SectionAddress); // Physical address W.write(SectionAddress); // Virtual address W.write(YamlSec.Size ? YamlSec.Size : DerivedSec.Size); W.write(YamlSec.FileOffsetToData ? YamlSec.FileOffsetToData : DerivedSec.FileOffsetToData); W.write(YamlSec.FileOffsetToRelocations ? YamlSec.FileOffsetToRelocations : DerivedSec.FileOffsetToRelocations); W.write(YamlSec.FileOffsetToLineNumbers); W.write(YamlSec.NumberOfRelocations ? YamlSec.NumberOfRelocations : DerivedSec.NumberOfRelocations); W.write(YamlSec.NumberOfLineNumbers); W.write(YamlSec.Flags); } } bool XCOFFWriter::writeSectionData() { for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) { XCOFFYAML::Section YamlSec = Obj.Sections[I]; if (YamlSec.SectionData.binary_size()) { // Fill the padding size with zeros. int64_t PaddingSize = InitSections[I].FileOffsetToData - (W.OS.tell() - StartOffset); if (PaddingSize < 0) { ErrHandler("redundant data was written before section data"); return false; } if (PaddingSize > 0) W.OS.write_zeros(PaddingSize); YamlSec.SectionData.writeAsBinary(W.OS); } } return true; } bool XCOFFWriter::writeRelocations() { for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) { XCOFFYAML::Section YamlSec = Obj.Sections[I]; if (!YamlSec.Relocations.empty()) { int64_t PaddingSize = InitSections[I].FileOffsetToRelocations - (W.OS.tell() - StartOffset); if (PaddingSize < 0) { ErrHandler("redundant data was written before relocations"); return false; } if (PaddingSize > 0) W.OS.write_zeros(PaddingSize); for (const XCOFFYAML::Relocation &YamlRel : YamlSec.Relocations) { W.write(YamlRel.VirtualAddress); W.write(YamlRel.SymbolIndex); W.write(YamlRel.Info); W.write(YamlRel.Type); } } } return true; } bool XCOFFWriter::writeSymbols() { int64_t PaddingSize = (uint64_t)InitFileHdr.SymbolTableOffset - (W.OS.tell() - StartOffset); if (PaddingSize < 0) { ErrHandler("redundant data was written before symbols"); return false; } if (PaddingSize > 0) W.OS.write_zeros(PaddingSize); for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) { if (nameShouldBeInStringTable(YamlSym.SymbolName)) { // For XCOFF32: A value of 0 indicates that the symbol name is in the // string table. W.write(0); W.write(Strings.getOffset(YamlSym.SymbolName)); } else { writeName(YamlSym.SymbolName, W); } W.write(YamlSym.Value); W.write( YamlSym.SectionName.size() ? SectionIndexMap[YamlSym.SectionName] : 0); W.write(YamlSym.Type); W.write(YamlSym.StorageClass); W.write(YamlSym.NumberOfAuxEntries); // Now output the auxiliary entry. for (uint8_t I = 0, E = YamlSym.NumberOfAuxEntries; I < E; ++I) { // TODO: Auxiliary entry is not supported yet. // The auxiliary entries for a symbol follow its symbol table entry. The // length of each auxiliary entry is the same as a symbol table entry (18 // bytes). The format and quantity of auxiliary entries depend on the // storage class (n_sclass) and type (n_type) of the symbol table entry. W.OS.write_zeros(18); } } return true; } bool XCOFFWriter::writeXCOFF() { if (Is64Bit) { ErrHandler("only XCOFF32 is currently supported"); return false; } if (!assignAddressesAndIndices()) return false; StartOffset = W.OS.tell(); writeFileHeader(); if (!Obj.Sections.empty()) { writeSectionHeader(); if (!writeSectionData()) return false; if (!writeRelocations()) return false; } if (!Obj.Symbols.empty() && !writeSymbols()) return false; // Write the string table. if (Strings.getSize() > 4) Strings.write(W.OS); return true; } } // end anonymous namespace namespace llvm { namespace yaml { bool yaml2xcoff(XCOFFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH) { XCOFFWriter Writer(Doc, Out, EH); return Writer.writeXCOFF(); } } // namespace yaml } // namespace llvm