//===- 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/LEB128.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; using namespace llvm::object; 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, llvm::endianness::big), ErrHandler(EH), StrTblBuilder(StringTableBuilder::XCOFF) { Is64Bit = Obj.Header.Magic == (llvm::yaml::Hex16)XCOFF::XCOFF64; } bool writeXCOFF(); private: void reportOverwrite(uint64_t currentOffset, uint64_t specifiedOffset, const Twine &fieldName); bool nameShouldBeInStringTable(StringRef SymbolName); bool initFileHeader(uint64_t CurrentOffset); void initAuxFileHeader(); bool initSectionHeaders(uint64_t &CurrentOffset); bool initRelocations(uint64_t &CurrentOffset); bool initStringTable(); bool assignAddressesAndIndices(); void writeFileHeader(); void writeAuxFileHeader(); void writeSectionHeaders(); bool writeSectionData(); bool writeRelocations(); bool writeSymbols(); void writeStringTable(); bool writeAuxSymbol(const XCOFFYAML::CsectAuxEnt &AuxSym); bool writeAuxSymbol(const XCOFFYAML::FileAuxEnt &AuxSym); bool writeAuxSymbol(const XCOFFYAML::FunctionAuxEnt &AuxSym); bool writeAuxSymbol(const XCOFFYAML::ExcpetionAuxEnt &AuxSym); bool writeAuxSymbol(const XCOFFYAML::BlockAuxEnt &AuxSym); bool writeAuxSymbol(const XCOFFYAML::SectAuxEntForDWARF &AuxSym); bool writeAuxSymbol(const XCOFFYAML::SectAuxEntForStat &AuxSym); bool writeAuxSymbol(const std::unique_ptr &AuxSym); XCOFFYAML::Object &Obj; bool Is64Bit = false; support::endian::Writer W; yaml::ErrorHandler ErrHandler; StringTableBuilder StrTblBuilder; uint64_t StartOffset = 0u; // 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; XCOFFYAML::AuxiliaryHeader InitAuxFileHdr; 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); } void XCOFFWriter::reportOverwrite(uint64_t CurrentOffset, uint64_t specifiedOffset, const Twine &fieldName) { ErrHandler("current file offset (" + Twine(CurrentOffset) + ") is bigger than the specified " + fieldName + " (" + Twine(specifiedOffset) + ") "); } bool XCOFFWriter::nameShouldBeInStringTable(StringRef SymbolName) { // For XCOFF64: The symbol name is always in the string table. return (SymbolName.size() > XCOFF::NameSize) || Is64Bit; } bool XCOFFWriter::initRelocations(uint64_t &CurrentOffset) { for (XCOFFYAML::Section &InitSection : InitSections) { if (!InitSection.Relocations.empty()) { uint64_t RelSize = Is64Bit ? XCOFF::RelocationSerializationSize64 : XCOFF::RelocationSerializationSize32; uint64_t UsedSize = RelSize * InitSection.Relocations.size(); // If NumberOfRelocations was specified, we use it, even if it's // not consistent with the number of provided relocations. if (!InitSection.NumberOfRelocations) InitSection.NumberOfRelocations = InitSection.Relocations.size(); // If the YAML file specified an offset to relocations, we use it. if (InitSection.FileOffsetToRelocations) { if (CurrentOffset > InitSection.FileOffsetToRelocations) { reportOverwrite(CurrentOffset, InitSection.FileOffsetToRelocations, "FileOffsetToRelocations for the " + InitSection.SectionName + " section"); return false; } CurrentOffset = InitSection.FileOffsetToRelocations; } else InitSection.FileOffsetToRelocations = CurrentOffset; CurrentOffset += UsedSize; if (CurrentOffset > MaxRawDataSize) { ErrHandler("maximum object size (" + Twine(MaxRawDataSize) + ") exceeded when writing relocation data for section " + Twine(InitSection.SectionName)); return false; } } } return true; } bool XCOFFWriter::initSectionHeaders(uint64_t &CurrentOffset) { uint64_t CurrentEndDataAddr = 0; uint64_t CurrentEndTDataAddr = 0; for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) { // 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; } } if (!InitSections[I].Size) InitSections[I].Size = InitSections[I].SectionData.binary_size(); // Section data addresses (physical/virtual) are related to symbol // addresses and alignments. Furthermore, it is possible to specify the // same starting addresses for the .text, .data, and .tdata sections. // Without examining all the symbols and their addreses and alignments, // it is not possible to compute valid section addresses. The only // condition required by XCOFF is that the .bss section immediately // follows the .data section, and the .tbss section immediately follows // the .tdata section. Therefore, we only assign addresses to the .bss // and .tbss sections if they do not already have non-zero addresses. // (If the YAML file is being used to generate a valid object file, we // expect all section addresses to be specified explicitly.) switch (InitSections[I].Flags) { case XCOFF::STYP_DATA: CurrentEndDataAddr = InitSections[I].Address + InitSections[I].Size; break; case XCOFF::STYP_BSS: if (!InitSections[I].Address) InitSections[I].Address = CurrentEndDataAddr; break; case XCOFF::STYP_TDATA: CurrentEndTDataAddr = InitSections[I].Address + InitSections[I].Size; break; case XCOFF::STYP_TBSS: if (!InitSections[I].Address) InitSections[I].Address = CurrentEndTDataAddr; break; } if (InitSections[I].SectionData.binary_size()) { if (InitSections[I].FileOffsetToData) { // Use the providedFileOffsetToData. if (CurrentOffset > InitSections[I].FileOffsetToData) { reportOverwrite(CurrentOffset, InitSections[I].FileOffsetToData, "FileOffsetToData for the " + InitSections[I].SectionName + " section"); return false; } CurrentOffset = InitSections[I].FileOffsetToData; } else { CurrentOffset = alignTo(CurrentOffset, DefaultSectionAlign); InitSections[I].FileOffsetToData = CurrentOffset; } CurrentOffset += InitSections[I].SectionData.binary_size(); if (CurrentOffset > MaxRawDataSize) { ErrHandler("maximum object size (" + Twine(MaxRawDataSize) + ") exceeded when writing data for section " + Twine(I + 1) + " (" + Twine(InitSections[I].SectionName) + ")"); return false; } } if (InitSections[I].SectionSubtype) { uint32_t DWARFSubtype = static_cast(*InitSections[I].SectionSubtype); if (InitSections[I].Flags != XCOFF::STYP_DWARF) { ErrHandler("a DWARFSectionSubtype is only allowed for a DWARF section"); return false; } unsigned Mask = Is64Bit ? XCOFFSectionHeader64::SectionFlagsTypeMask : XCOFFSectionHeader32::SectionFlagsTypeMask; if (DWARFSubtype & Mask) { ErrHandler("the low-order bits of DWARFSectionSubtype must be 0"); return false; } InitSections[I].Flags |= DWARFSubtype; } } return initRelocations(CurrentOffset); } bool XCOFFWriter::initStringTable() { if (Obj.StrTbl.RawContent) { size_t RawSize = Obj.StrTbl.RawContent->binary_size(); if (Obj.StrTbl.Strings || Obj.StrTbl.Length) { ErrHandler( "can't specify Strings or Length when RawContent is specified"); return false; } if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize < RawSize) { ErrHandler("specified ContentSize (" + Twine(*Obj.StrTbl.ContentSize) + ") is less than the RawContent data size (" + Twine(RawSize) + ")"); return false; } return true; } if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize <= 3) { ErrHandler("ContentSize shouldn't be less than 4 without RawContent"); return false; } // Build the string table. StrTblBuilder.clear(); if (Obj.StrTbl.Strings) { // Add all specified strings to the string table. for (StringRef StringEnt : *Obj.StrTbl.Strings) StrTblBuilder.add(StringEnt); size_t StrTblIdx = 0; size_t NumOfStrings = Obj.StrTbl.Strings->size(); for (XCOFFYAML::Symbol &YamlSym : Obj.Symbols) { if (nameShouldBeInStringTable(YamlSym.SymbolName)) { if (StrTblIdx < NumOfStrings) { // Overwrite the symbol name with the specified string. YamlSym.SymbolName = (*Obj.StrTbl.Strings)[StrTblIdx]; ++StrTblIdx; } else // Names that are not overwritten are still stored in the string // table. StrTblBuilder.add(YamlSym.SymbolName); } } } else { for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) { if (nameShouldBeInStringTable(YamlSym.SymbolName)) StrTblBuilder.add(YamlSym.SymbolName); } } // Check if the file name in the File Auxiliary Entry should be added to the // string table. for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) { for (const std::unique_ptr &AuxSym : YamlSym.AuxEntries) { if (auto AS = dyn_cast(AuxSym.get())) if (nameShouldBeInStringTable(AS->FileNameOrString.value_or(""))) StrTblBuilder.add(AS->FileNameOrString.value_or("")); } } StrTblBuilder.finalize(); size_t StrTblSize = StrTblBuilder.getSize(); if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize < StrTblSize) { ErrHandler("specified ContentSize (" + Twine(*Obj.StrTbl.ContentSize) + ") is less than the size of the data that would otherwise be " "written (" + Twine(StrTblSize) + ")"); return false; } return true; } 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 (XCOFFYAML::Symbol &YamlSym : Obj.Symbols) { uint32_t AuxCount = YamlSym.AuxEntries.size(); if (YamlSym.NumberOfAuxEntries && *YamlSym.NumberOfAuxEntries < AuxCount) { ErrHandler("specified NumberOfAuxEntries " + Twine(static_cast(*YamlSym.NumberOfAuxEntries)) + " is less than the actual number " "of auxiliary entries " + Twine(AuxCount)); return false; } YamlSym.NumberOfAuxEntries = YamlSym.NumberOfAuxEntries.value_or(AuxCount); // Add the number of auxiliary symbols to the total number. InitFileHdr.NumberOfSymTableEntries += *YamlSym.NumberOfAuxEntries; } // Calculate SymbolTableOffset for the file header. if (InitFileHdr.NumberOfSymTableEntries) { if (Obj.Header.SymbolTableOffset) { if (CurrentOffset > Obj.Header.SymbolTableOffset) { reportOverwrite(CurrentOffset, Obj.Header.SymbolTableOffset, "SymbolTableOffset"); return false; } CurrentOffset = Obj.Header.SymbolTableOffset; } 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; } void XCOFFWriter::initAuxFileHeader() { if (Obj.AuxHeader) InitAuxFileHdr = *Obj.AuxHeader; // In general, an object file might contain multiple sections of a given type, // but in a loadable module, there must be exactly one .text, .data, .bss, and // .loader section. A loadable object might also have one .tdata section and // one .tbss section. // Set these section-related values if not set explicitly. We assume that the // input YAML matches the format of the loadable object, but if multiple input // sections still have the same type, the first section with that type // prevails. for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) { switch (InitSections[I].Flags) { case XCOFF::STYP_TEXT: if (!InitAuxFileHdr.TextSize) InitAuxFileHdr.TextSize = InitSections[I].Size; if (!InitAuxFileHdr.TextStartAddr) InitAuxFileHdr.TextStartAddr = InitSections[I].Address; if (!InitAuxFileHdr.SecNumOfText) InitAuxFileHdr.SecNumOfText = I + 1; break; case XCOFF::STYP_DATA: if (!InitAuxFileHdr.InitDataSize) InitAuxFileHdr.InitDataSize = InitSections[I].Size; if (!InitAuxFileHdr.DataStartAddr) InitAuxFileHdr.DataStartAddr = InitSections[I].Address; if (!InitAuxFileHdr.SecNumOfData) InitAuxFileHdr.SecNumOfData = I + 1; break; case XCOFF::STYP_BSS: if (!InitAuxFileHdr.BssDataSize) InitAuxFileHdr.BssDataSize = InitSections[I].Size; if (!InitAuxFileHdr.SecNumOfBSS) InitAuxFileHdr.SecNumOfBSS = I + 1; break; case XCOFF::STYP_TDATA: if (!InitAuxFileHdr.SecNumOfTData) InitAuxFileHdr.SecNumOfTData = I + 1; break; case XCOFF::STYP_TBSS: if (!InitAuxFileHdr.SecNumOfTBSS) InitAuxFileHdr.SecNumOfTBSS = I + 1; break; case XCOFF::STYP_LOADER: if (!InitAuxFileHdr.SecNumOfLoader) InitAuxFileHdr.SecNumOfLoader = I + 1; break; default: break; } } } bool XCOFFWriter::assignAddressesAndIndices() { uint64_t FileHdrSize = Is64Bit ? XCOFF::FileHeaderSize64 : XCOFF::FileHeaderSize32; // If AuxHeaderSize is specified in the YAML file, we construct // an auxiliary header. uint64_t AuxFileHdrSize = 0; if (Obj.Header.AuxHeaderSize) AuxFileHdrSize = Obj.Header.AuxHeaderSize; else if (Obj.AuxHeader) AuxFileHdrSize = (Is64Bit ? XCOFF::AuxFileHeaderSize64 : XCOFF::AuxFileHeaderSize32); uint64_t SecHdrSize = Is64Bit ? XCOFF::SectionHeaderSize64 : XCOFF::SectionHeaderSize32; uint64_t CurrentOffset = FileHdrSize + AuxFileHdrSize + InitSections.size() * SecHdrSize; // Calculate section header info. if (!initSectionHeaders(CurrentOffset)) return false; // Calculate file header info. if (!initFileHeader(CurrentOffset)) return false; InitFileHdr.AuxHeaderSize = AuxFileHdrSize; // Initialize the auxiliary file header. if (AuxFileHdrSize) initAuxFileHeader(); // Initialize the string table. return initStringTable(); } 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); if (Is64Bit) { W.write(InitFileHdr.SymbolTableOffset); W.write(InitFileHdr.AuxHeaderSize); W.write(Obj.Header.Flags); W.write(Obj.Header.NumberOfSymTableEntries ? Obj.Header.NumberOfSymTableEntries : InitFileHdr.NumberOfSymTableEntries); } else { W.write(InitFileHdr.SymbolTableOffset); W.write(Obj.Header.NumberOfSymTableEntries ? Obj.Header.NumberOfSymTableEntries : InitFileHdr.NumberOfSymTableEntries); W.write(InitFileHdr.AuxHeaderSize); W.write(Obj.Header.Flags); } } void XCOFFWriter::writeAuxFileHeader() { W.write(InitAuxFileHdr.Magic.value_or(yaml::Hex16(1))); W.write(InitAuxFileHdr.Version.value_or(yaml::Hex16(1))); if (Is64Bit) { W.OS.write_zeros(4); // Reserved for debugger. W.write(InitAuxFileHdr.TextStartAddr.value_or(yaml::Hex64(0))); W.write(InitAuxFileHdr.DataStartAddr.value_or(yaml::Hex64(0))); W.write(InitAuxFileHdr.TOCAnchorAddr.value_or(yaml::Hex64(0))); } else { W.write(InitAuxFileHdr.TextSize.value_or(yaml::Hex64(0))); W.write(InitAuxFileHdr.InitDataSize.value_or(yaml::Hex64(0))); W.write(InitAuxFileHdr.BssDataSize.value_or(yaml::Hex64(0))); W.write(InitAuxFileHdr.EntryPointAddr.value_or(yaml::Hex64(0))); W.write(InitAuxFileHdr.TextStartAddr.value_or(yaml::Hex64(0))); W.write(InitAuxFileHdr.DataStartAddr.value_or(yaml::Hex64(0))); // A short 32-bit auxiliary header ends here. if (InitFileHdr.AuxHeaderSize == XCOFF::AuxFileHeaderSizeShort) return; W.write(InitAuxFileHdr.TOCAnchorAddr.value_or(yaml::Hex64(0))); } W.write(InitAuxFileHdr.SecNumOfEntryPoint.value_or(0)); W.write(InitAuxFileHdr.SecNumOfText.value_or(0)); W.write(InitAuxFileHdr.SecNumOfData.value_or(0)); W.write(InitAuxFileHdr.SecNumOfTOC.value_or(0)); W.write(InitAuxFileHdr.SecNumOfLoader.value_or(0)); W.write(InitAuxFileHdr.SecNumOfBSS.value_or(0)); W.write(InitAuxFileHdr.MaxAlignOfText.value_or(yaml::Hex16(0))); W.write(InitAuxFileHdr.MaxAlignOfData.value_or(yaml::Hex16(0))); W.write(InitAuxFileHdr.ModuleType.value_or(yaml::Hex16(0))); W.write(InitAuxFileHdr.CpuFlag.value_or(yaml::Hex8(0))); W.write(0); // Reserved for CPU type. if (Is64Bit) { W.write(InitAuxFileHdr.TextPageSize.value_or(yaml::Hex8(0))); W.write(InitAuxFileHdr.DataPageSize.value_or(yaml::Hex8(0))); W.write(InitAuxFileHdr.StackPageSize.value_or(yaml::Hex8(0))); W.write( InitAuxFileHdr.FlagAndTDataAlignment.value_or(yaml::Hex8(0x80))); W.write(InitAuxFileHdr.TextSize.value_or(yaml::Hex64(0))); W.write(InitAuxFileHdr.InitDataSize.value_or(yaml::Hex64(0))); W.write(InitAuxFileHdr.BssDataSize.value_or(yaml::Hex64(0))); W.write(InitAuxFileHdr.EntryPointAddr.value_or(yaml::Hex64(0))); W.write(InitAuxFileHdr.MaxStackSize.value_or(yaml::Hex64(0))); W.write(InitAuxFileHdr.MaxDataSize.value_or(yaml::Hex64(0))); } else { W.write(InitAuxFileHdr.MaxStackSize.value_or(yaml::Hex64(0))); W.write(InitAuxFileHdr.MaxDataSize.value_or(yaml::Hex64(0))); W.OS.write_zeros(4); // Reserved for debugger. W.write(InitAuxFileHdr.TextPageSize.value_or(yaml::Hex8(0))); W.write(InitAuxFileHdr.DataPageSize.value_or(yaml::Hex8(0))); W.write(InitAuxFileHdr.StackPageSize.value_or(yaml::Hex8(0))); W.write( InitAuxFileHdr.FlagAndTDataAlignment.value_or(yaml::Hex8(0))); } W.write(InitAuxFileHdr.SecNumOfTData.value_or(0)); W.write(InitAuxFileHdr.SecNumOfTBSS.value_or(0)); if (Is64Bit) { W.write( InitAuxFileHdr.Flag.value_or(yaml::Hex16(XCOFF::SHR_SYMTAB))); if (InitFileHdr.AuxHeaderSize > XCOFF::AuxFileHeaderSize64) W.OS.write_zeros(InitFileHdr.AuxHeaderSize - XCOFF::AuxFileHeaderSize64); } else { if (InitFileHdr.AuxHeaderSize > XCOFF::AuxFileHeaderSize32) W.OS.write_zeros(InitFileHdr.AuxHeaderSize - XCOFF::AuxFileHeaderSize32); } } void XCOFFWriter::writeSectionHeaders() { for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) { XCOFFYAML::Section DerivedSec = InitSections[I]; writeName(DerivedSec.SectionName, W); if (Is64Bit) { // Virtual address is the same as physical address. W.write(DerivedSec.Address); // Physical address W.write(DerivedSec.Address); // Virtual address W.write(DerivedSec.Size); W.write(DerivedSec.FileOffsetToData); W.write(DerivedSec.FileOffsetToRelocations); W.write(DerivedSec.FileOffsetToLineNumbers); W.write(DerivedSec.NumberOfRelocations); W.write(DerivedSec.NumberOfLineNumbers); W.write(DerivedSec.Flags); W.OS.write_zeros(4); } else { // Virtual address is the same as physical address. W.write(DerivedSec.Address); // Physical address W.write(DerivedSec.Address); // Virtual address W.write(DerivedSec.Size); W.write(DerivedSec.FileOffsetToData); W.write(DerivedSec.FileOffsetToRelocations); W.write(DerivedSec.FileOffsetToLineNumbers); W.write(DerivedSec.NumberOfRelocations); W.write(DerivedSec.NumberOfLineNumbers); W.write(DerivedSec.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 = (uint64_t)InitSections[I].FileOffsetToData - (W.OS.tell() - StartOffset); if (PaddingSize < 0) { ErrHandler("redundant data was written before section data"); return false; } 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; } W.OS.write_zeros(PaddingSize); for (const XCOFFYAML::Relocation &YamlRel : YamlSec.Relocations) { if (Is64Bit) W.write(YamlRel.VirtualAddress); else W.write(YamlRel.VirtualAddress); W.write(YamlRel.SymbolIndex); W.write(YamlRel.Info); W.write(YamlRel.Type); } } } return true; } bool XCOFFWriter::writeAuxSymbol(const XCOFFYAML::CsectAuxEnt &AuxSym) { uint8_t SymAlignAndType = 0; if (AuxSym.SymbolAlignmentAndType) { if (AuxSym.SymbolType || AuxSym.SymbolAlignment) { ErrHandler("cannot specify SymbolType or SymbolAlignment if " "SymbolAlignmentAndType is specified"); return false; } SymAlignAndType = *AuxSym.SymbolAlignmentAndType; } else { if (AuxSym.SymbolType) { uint8_t SymbolType = *AuxSym.SymbolType; if (SymbolType & ~XCOFFCsectAuxRef::SymbolTypeMask) { ErrHandler("symbol type must be less than " + Twine(1 + XCOFFCsectAuxRef::SymbolTypeMask)); return false; } SymAlignAndType = SymbolType; } if (AuxSym.SymbolAlignment) { const uint8_t ShiftedSymbolAlignmentMask = XCOFFCsectAuxRef::SymbolAlignmentMask >> XCOFFCsectAuxRef::SymbolAlignmentBitOffset; if (*AuxSym.SymbolAlignment & ~ShiftedSymbolAlignmentMask) { ErrHandler("symbol alignment must be less than " + Twine(1 + ShiftedSymbolAlignmentMask)); return false; } SymAlignAndType |= (*AuxSym.SymbolAlignment << XCOFFCsectAuxRef::SymbolAlignmentBitOffset); } } if (Is64Bit) { W.write(AuxSym.SectionOrLengthLo.value_or(0)); W.write(AuxSym.ParameterHashIndex.value_or(0)); W.write(AuxSym.TypeChkSectNum.value_or(0)); W.write(SymAlignAndType); W.write(AuxSym.StorageMappingClass.value_or(XCOFF::XMC_PR)); W.write(AuxSym.SectionOrLengthHi.value_or(0)); W.write(0); W.write(XCOFF::AUX_CSECT); } else { W.write(AuxSym.SectionOrLength.value_or(0)); W.write(AuxSym.ParameterHashIndex.value_or(0)); W.write(AuxSym.TypeChkSectNum.value_or(0)); W.write(SymAlignAndType); W.write(AuxSym.StorageMappingClass.value_or(XCOFF::XMC_PR)); W.write(AuxSym.StabInfoIndex.value_or(0)); W.write(AuxSym.StabSectNum.value_or(0)); } return true; } bool XCOFFWriter::writeAuxSymbol(const XCOFFYAML::ExcpetionAuxEnt &AuxSym) { assert(Is64Bit && "can't write the exception auxiliary symbol for XCOFF32"); W.write(AuxSym.OffsetToExceptionTbl.value_or(0)); W.write(AuxSym.SizeOfFunction.value_or(0)); W.write(AuxSym.SymIdxOfNextBeyond.value_or(0)); W.write(0); W.write(XCOFF::AUX_EXCEPT); return true; } bool XCOFFWriter::writeAuxSymbol(const XCOFFYAML::FunctionAuxEnt &AuxSym) { if (Is64Bit) { W.write(AuxSym.PtrToLineNum.value_or(0)); W.write(AuxSym.SizeOfFunction.value_or(0)); W.write(AuxSym.SymIdxOfNextBeyond.value_or(0)); W.write(0); W.write(XCOFF::AUX_FCN); } else { W.write(AuxSym.OffsetToExceptionTbl.value_or(0)); W.write(AuxSym.SizeOfFunction.value_or(0)); W.write(AuxSym.PtrToLineNum.value_or(0)); W.write(AuxSym.SymIdxOfNextBeyond.value_or(0)); W.OS.write_zeros(2); } return true; } bool XCOFFWriter::writeAuxSymbol(const XCOFFYAML::FileAuxEnt &AuxSym) { StringRef FileName = AuxSym.FileNameOrString.value_or(""); if (nameShouldBeInStringTable(FileName)) { W.write(0); W.write(StrTblBuilder.getOffset(FileName)); } else { writeName(FileName, W); } W.OS.write_zeros(XCOFF::FileNamePadSize); W.write(AuxSym.FileStringType.value_or(XCOFF::XFT_FN)); if (Is64Bit) { W.OS.write_zeros(2); W.write(XCOFF::AUX_FILE); } else { W.OS.write_zeros(3); } return true; } bool XCOFFWriter::writeAuxSymbol(const XCOFFYAML::BlockAuxEnt &AuxSym) { if (Is64Bit) { W.write(AuxSym.LineNum.value_or(0)); W.OS.write_zeros(13); W.write(XCOFF::AUX_SYM); } else { W.OS.write_zeros(2); W.write(AuxSym.LineNumHi.value_or(0)); W.write(AuxSym.LineNumLo.value_or(0)); W.OS.write_zeros(12); } return true; } bool XCOFFWriter::writeAuxSymbol(const XCOFFYAML::SectAuxEntForDWARF &AuxSym) { if (Is64Bit) { W.write(AuxSym.LengthOfSectionPortion.value_or(0)); W.write(AuxSym.NumberOfRelocEnt.value_or(0)); W.write(0); W.write(XCOFF::AUX_SECT); } else { W.write(AuxSym.LengthOfSectionPortion.value_or(0)); W.OS.write_zeros(4); W.write(AuxSym.NumberOfRelocEnt.value_or(0)); W.OS.write_zeros(6); } return true; } bool XCOFFWriter::writeAuxSymbol(const XCOFFYAML::SectAuxEntForStat &AuxSym) { assert(!Is64Bit && "can't write the stat auxiliary symbol for XCOFF64"); W.write(AuxSym.SectionLength.value_or(0)); W.write(AuxSym.NumberOfRelocEnt.value_or(0)); W.write(AuxSym.NumberOfLineNum.value_or(0)); W.OS.write_zeros(10); return true; } bool XCOFFWriter::writeAuxSymbol( const std::unique_ptr &AuxSym) { if (auto AS = dyn_cast(AuxSym.get())) return writeAuxSymbol(*AS); else if (auto AS = dyn_cast(AuxSym.get())) return writeAuxSymbol(*AS); else if (auto AS = dyn_cast(AuxSym.get())) return writeAuxSymbol(*AS); else if (auto AS = dyn_cast(AuxSym.get())) return writeAuxSymbol(*AS); else if (auto AS = dyn_cast(AuxSym.get())) return writeAuxSymbol(*AS); else if (auto AS = dyn_cast(AuxSym.get())) return writeAuxSymbol(*AS); else if (auto AS = dyn_cast(AuxSym.get())) return writeAuxSymbol(*AS); llvm_unreachable("unknown auxiliary symbol type"); return false; } bool XCOFFWriter::writeSymbols() { int64_t PaddingSize = InitFileHdr.SymbolTableOffset - (W.OS.tell() - StartOffset); if (PaddingSize < 0) { ErrHandler("redundant data was written before symbols"); return false; } W.OS.write_zeros(PaddingSize); for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) { if (Is64Bit) { W.write(YamlSym.Value); W.write(StrTblBuilder.getOffset(YamlSym.SymbolName)); } else { 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(StrTblBuilder.getOffset(YamlSym.SymbolName)); } else { writeName(YamlSym.SymbolName, W); } W.write(YamlSym.Value); } if (YamlSym.SectionName) { if (!SectionIndexMap.count(*YamlSym.SectionName)) { ErrHandler("the SectionName " + *YamlSym.SectionName + " specified in the symbol does not exist"); return false; } if (YamlSym.SectionIndex && SectionIndexMap[*YamlSym.SectionName] != *YamlSym.SectionIndex) { ErrHandler("the SectionName " + *YamlSym.SectionName + " and the SectionIndex (" + Twine(*YamlSym.SectionIndex) + ") refer to different sections"); return false; } W.write(SectionIndexMap[*YamlSym.SectionName]); } else { W.write(YamlSym.SectionIndex ? *YamlSym.SectionIndex : 0); } W.write(YamlSym.Type); W.write(YamlSym.StorageClass); uint8_t NumOfAuxSym = YamlSym.NumberOfAuxEntries.value_or(0); W.write(NumOfAuxSym); if (!NumOfAuxSym && !YamlSym.AuxEntries.size()) continue; // Now write auxiliary entries. if (!YamlSym.AuxEntries.size()) { W.OS.write_zeros(XCOFF::SymbolTableEntrySize * NumOfAuxSym); } else { for (const std::unique_ptr &AuxSym : YamlSym.AuxEntries) { if (!writeAuxSymbol(AuxSym)) return false; } // Pad with zeros. if (NumOfAuxSym > YamlSym.AuxEntries.size()) W.OS.write_zeros(XCOFF::SymbolTableEntrySize * (NumOfAuxSym - YamlSym.AuxEntries.size())); } } return true; } void XCOFFWriter::writeStringTable() { if (Obj.StrTbl.RawContent) { Obj.StrTbl.RawContent->writeAsBinary(W.OS); if (Obj.StrTbl.ContentSize) { assert(*Obj.StrTbl.ContentSize >= Obj.StrTbl.RawContent->binary_size() && "Specified ContentSize is less than the RawContent size."); W.OS.write_zeros(*Obj.StrTbl.ContentSize - Obj.StrTbl.RawContent->binary_size()); } return; } size_t StrTblBuilderSize = StrTblBuilder.getSize(); // If neither Length nor ContentSize is specified, write the StrTblBuilder // directly, which contains the auto-generated Length value. if (!Obj.StrTbl.Length && !Obj.StrTbl.ContentSize) { if (StrTblBuilderSize <= 4) return; StrTblBuilder.write(W.OS); return; } // Serialize the string table's content to a temporary buffer. std::unique_ptr Buf = WritableMemoryBuffer::getNewMemBuffer(StrTblBuilderSize); uint8_t *Ptr = reinterpret_cast(Buf->getBufferStart()); StrTblBuilder.write(Ptr); // Replace the first 4 bytes, which contain the auto-generated Length value, // with the specified value. memset(Ptr, 0, 4); support::endian::write32be(Ptr, Obj.StrTbl.Length ? *Obj.StrTbl.Length : *Obj.StrTbl.ContentSize); // Copy the buffer content to the actual output stream. W.OS.write(Buf->getBufferStart(), Buf->getBufferSize()); // Add zeros as padding after strings. if (Obj.StrTbl.ContentSize) { assert(*Obj.StrTbl.ContentSize >= StrTblBuilderSize && "Specified ContentSize is less than the StringTableBuilder size."); W.OS.write_zeros(*Obj.StrTbl.ContentSize - StrTblBuilderSize); } } bool XCOFFWriter::writeXCOFF() { if (!assignAddressesAndIndices()) return false; StartOffset = W.OS.tell(); writeFileHeader(); if (InitFileHdr.AuxHeaderSize) writeAuxFileHeader(); if (!Obj.Sections.empty()) { writeSectionHeaders(); if (!writeSectionData()) return false; if (!writeRelocations()) return false; } if (!Obj.Symbols.empty() && !writeSymbols()) return false; writeStringTable(); 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