diff options
Diffstat (limited to 'lib/ObjectYAML')
-rw-r--r-- | lib/ObjectYAML/COFFEmitter.cpp | 622 | ||||
-rw-r--r-- | lib/ObjectYAML/CodeViewYAMLSymbols.cpp | 2 | ||||
-rw-r--r-- | lib/ObjectYAML/ELFEmitter.cpp | 1152 | ||||
-rw-r--r-- | lib/ObjectYAML/ELFYAML.cpp | 325 | ||||
-rw-r--r-- | lib/ObjectYAML/MachOEmitter.cpp | 580 | ||||
-rw-r--r-- | lib/ObjectYAML/MachOYAML.cpp | 9 | ||||
-rw-r--r-- | lib/ObjectYAML/MinidumpEmitter.cpp | 247 | ||||
-rw-r--r-- | lib/ObjectYAML/MinidumpYAML.cpp | 331 | ||||
-rw-r--r-- | lib/ObjectYAML/WasmEmitter.cpp | 633 | ||||
-rw-r--r-- | lib/ObjectYAML/WasmYAML.cpp | 4 | ||||
-rw-r--r-- | lib/ObjectYAML/yaml2obj.cpp | 77 |
11 files changed, 3705 insertions, 277 deletions
diff --git a/lib/ObjectYAML/COFFEmitter.cpp b/lib/ObjectYAML/COFFEmitter.cpp new file mode 100644 index 000000000000..efcdc51e1670 --- /dev/null +++ b/lib/ObjectYAML/COFFEmitter.cpp @@ -0,0 +1,622 @@ +//===- yaml2coff - Convert YAML to a COFF 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 COFF component of yaml2obj. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" +#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h" +#include "llvm/Object/COFF.h" +#include "llvm/ObjectYAML/ObjectYAML.h" +#include "llvm/ObjectYAML/yaml2obj.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include <vector> + +using namespace llvm; + +namespace { + +/// This parses a yaml stream that represents a COFF object file. +/// See docs/yaml2obj for the yaml scheema. +struct COFFParser { + COFFParser(COFFYAML::Object &Obj, yaml::ErrorHandler EH) + : Obj(Obj), SectionTableStart(0), SectionTableSize(0), ErrHandler(EH) { + // A COFF string table always starts with a 4 byte size field. Offsets into + // it include this size, so allocate it now. + StringTable.append(4, char(0)); + } + + bool useBigObj() const { + return static_cast<int32_t>(Obj.Sections.size()) > + COFF::MaxNumberOfSections16; + } + + bool isPE() const { return Obj.OptionalHeader.hasValue(); } + bool is64Bit() const { + return Obj.Header.Machine == COFF::IMAGE_FILE_MACHINE_AMD64 || + Obj.Header.Machine == COFF::IMAGE_FILE_MACHINE_ARM64; + } + + uint32_t getFileAlignment() const { + return Obj.OptionalHeader->Header.FileAlignment; + } + + unsigned getHeaderSize() const { + return useBigObj() ? COFF::Header32Size : COFF::Header16Size; + } + + unsigned getSymbolSize() const { + return useBigObj() ? COFF::Symbol32Size : COFF::Symbol16Size; + } + + bool parseSections() { + for (std::vector<COFFYAML::Section>::iterator i = Obj.Sections.begin(), + e = Obj.Sections.end(); + i != e; ++i) { + COFFYAML::Section &Sec = *i; + + // If the name is less than 8 bytes, store it in place, otherwise + // store it in the string table. + StringRef Name = Sec.Name; + + if (Name.size() <= COFF::NameSize) { + std::copy(Name.begin(), Name.end(), Sec.Header.Name); + } else { + // Add string to the string table and format the index for output. + unsigned Index = getStringIndex(Name); + std::string str = utostr(Index); + if (str.size() > 7) { + ErrHandler("string table got too large"); + return false; + } + Sec.Header.Name[0] = '/'; + std::copy(str.begin(), str.end(), Sec.Header.Name + 1); + } + + if (Sec.Alignment) { + if (Sec.Alignment > 8192) { + ErrHandler("section alignment is too large"); + return false; + } + if (!isPowerOf2_32(Sec.Alignment)) { + ErrHandler("section alignment is not a power of 2"); + return false; + } + Sec.Header.Characteristics |= (Log2_32(Sec.Alignment) + 1) << 20; + } + } + return true; + } + + bool parseSymbols() { + for (std::vector<COFFYAML::Symbol>::iterator i = Obj.Symbols.begin(), + e = Obj.Symbols.end(); + i != e; ++i) { + COFFYAML::Symbol &Sym = *i; + + // If the name is less than 8 bytes, store it in place, otherwise + // store it in the string table. + StringRef Name = Sym.Name; + if (Name.size() <= COFF::NameSize) { + std::copy(Name.begin(), Name.end(), Sym.Header.Name); + } else { + // Add string to the string table and format the index for output. + unsigned Index = getStringIndex(Name); + *reinterpret_cast<support::aligned_ulittle32_t *>(Sym.Header.Name + 4) = + Index; + } + + Sym.Header.Type = Sym.SimpleType; + Sym.Header.Type |= Sym.ComplexType << COFF::SCT_COMPLEX_TYPE_SHIFT; + } + return true; + } + + bool parse() { + if (!parseSections()) + return false; + if (!parseSymbols()) + return false; + return true; + } + + unsigned getStringIndex(StringRef Str) { + StringMap<unsigned>::iterator i = StringTableMap.find(Str); + if (i == StringTableMap.end()) { + unsigned Index = StringTable.size(); + StringTable.append(Str.begin(), Str.end()); + StringTable.push_back(0); + StringTableMap[Str] = Index; + return Index; + } + return i->second; + } + + COFFYAML::Object &Obj; + + codeview::StringsAndChecksums StringsAndChecksums; + BumpPtrAllocator Allocator; + StringMap<unsigned> StringTableMap; + std::string StringTable; + uint32_t SectionTableStart; + uint32_t SectionTableSize; + + yaml::ErrorHandler ErrHandler; +}; + +enum { DOSStubSize = 128 }; + +} // end anonymous namespace + +// Take a CP and assign addresses and sizes to everything. Returns false if the +// layout is not valid to do. +static bool layoutOptionalHeader(COFFParser &CP) { + if (!CP.isPE()) + return true; + unsigned PEHeaderSize = CP.is64Bit() ? sizeof(object::pe32plus_header) + : sizeof(object::pe32_header); + CP.Obj.Header.SizeOfOptionalHeader = + PEHeaderSize + + sizeof(object::data_directory) * (COFF::NUM_DATA_DIRECTORIES + 1); + return true; +} + +static yaml::BinaryRef +toDebugS(ArrayRef<CodeViewYAML::YAMLDebugSubsection> Subsections, + const codeview::StringsAndChecksums &SC, BumpPtrAllocator &Allocator) { + using namespace codeview; + ExitOnError Err("Error occurred writing .debug$S section"); + auto CVSS = + Err(CodeViewYAML::toCodeViewSubsectionList(Allocator, Subsections, SC)); + + std::vector<DebugSubsectionRecordBuilder> Builders; + uint32_t Size = sizeof(uint32_t); + for (auto &SS : CVSS) { + DebugSubsectionRecordBuilder B(SS, CodeViewContainer::ObjectFile); + Size += B.calculateSerializedLength(); + Builders.push_back(std::move(B)); + } + uint8_t *Buffer = Allocator.Allocate<uint8_t>(Size); + MutableArrayRef<uint8_t> Output(Buffer, Size); + BinaryStreamWriter Writer(Output, support::little); + + Err(Writer.writeInteger<uint32_t>(COFF::DEBUG_SECTION_MAGIC)); + for (const auto &B : Builders) { + Err(B.commit(Writer)); + } + return {Output}; +} + +// Take a CP and assign addresses and sizes to everything. Returns false if the +// layout is not valid to do. +static bool layoutCOFF(COFFParser &CP) { + // The section table starts immediately after the header, including the + // optional header. + CP.SectionTableStart = + CP.getHeaderSize() + CP.Obj.Header.SizeOfOptionalHeader; + if (CP.isPE()) + CP.SectionTableStart += DOSStubSize + sizeof(COFF::PEMagic); + CP.SectionTableSize = COFF::SectionSize * CP.Obj.Sections.size(); + + uint32_t CurrentSectionDataOffset = + CP.SectionTableStart + CP.SectionTableSize; + + for (COFFYAML::Section &S : CP.Obj.Sections) { + // We support specifying exactly one of SectionData or Subsections. So if + // there is already some SectionData, then we don't need to do any of this. + if (S.Name == ".debug$S" && S.SectionData.binary_size() == 0) { + CodeViewYAML::initializeStringsAndChecksums(S.DebugS, + CP.StringsAndChecksums); + if (CP.StringsAndChecksums.hasChecksums() && + CP.StringsAndChecksums.hasStrings()) + break; + } + } + + // Assign each section data address consecutively. + for (COFFYAML::Section &S : CP.Obj.Sections) { + if (S.Name == ".debug$S") { + if (S.SectionData.binary_size() == 0) { + assert(CP.StringsAndChecksums.hasStrings() && + "Object file does not have debug string table!"); + + S.SectionData = + toDebugS(S.DebugS, CP.StringsAndChecksums, CP.Allocator); + } + } else if (S.Name == ".debug$T") { + if (S.SectionData.binary_size() == 0) + S.SectionData = CodeViewYAML::toDebugT(S.DebugT, CP.Allocator, S.Name); + } else if (S.Name == ".debug$P") { + if (S.SectionData.binary_size() == 0) + S.SectionData = CodeViewYAML::toDebugT(S.DebugP, CP.Allocator, S.Name); + } else if (S.Name == ".debug$H") { + if (S.DebugH.hasValue() && S.SectionData.binary_size() == 0) + S.SectionData = CodeViewYAML::toDebugH(*S.DebugH, CP.Allocator); + } + + if (S.SectionData.binary_size() > 0) { + CurrentSectionDataOffset = alignTo(CurrentSectionDataOffset, + CP.isPE() ? CP.getFileAlignment() : 4); + S.Header.SizeOfRawData = S.SectionData.binary_size(); + if (CP.isPE()) + S.Header.SizeOfRawData = + alignTo(S.Header.SizeOfRawData, CP.getFileAlignment()); + S.Header.PointerToRawData = CurrentSectionDataOffset; + CurrentSectionDataOffset += S.Header.SizeOfRawData; + if (!S.Relocations.empty()) { + S.Header.PointerToRelocations = CurrentSectionDataOffset; + S.Header.NumberOfRelocations = S.Relocations.size(); + CurrentSectionDataOffset += + S.Header.NumberOfRelocations * COFF::RelocationSize; + } + } else { + // Leave SizeOfRawData unaltered. For .bss sections in object files, it + // carries the section size. + S.Header.PointerToRawData = 0; + } + } + + uint32_t SymbolTableStart = CurrentSectionDataOffset; + + // Calculate number of symbols. + uint32_t NumberOfSymbols = 0; + for (std::vector<COFFYAML::Symbol>::iterator i = CP.Obj.Symbols.begin(), + e = CP.Obj.Symbols.end(); + i != e; ++i) { + uint32_t NumberOfAuxSymbols = 0; + if (i->FunctionDefinition) + NumberOfAuxSymbols += 1; + if (i->bfAndefSymbol) + NumberOfAuxSymbols += 1; + if (i->WeakExternal) + NumberOfAuxSymbols += 1; + if (!i->File.empty()) + NumberOfAuxSymbols += + (i->File.size() + CP.getSymbolSize() - 1) / CP.getSymbolSize(); + if (i->SectionDefinition) + NumberOfAuxSymbols += 1; + if (i->CLRToken) + NumberOfAuxSymbols += 1; + i->Header.NumberOfAuxSymbols = NumberOfAuxSymbols; + NumberOfSymbols += 1 + NumberOfAuxSymbols; + } + + // Store all the allocated start addresses in the header. + CP.Obj.Header.NumberOfSections = CP.Obj.Sections.size(); + CP.Obj.Header.NumberOfSymbols = NumberOfSymbols; + if (NumberOfSymbols > 0 || CP.StringTable.size() > 4) + CP.Obj.Header.PointerToSymbolTable = SymbolTableStart; + else + CP.Obj.Header.PointerToSymbolTable = 0; + + *reinterpret_cast<support::ulittle32_t *>(&CP.StringTable[0]) = + CP.StringTable.size(); + + return true; +} + +template <typename value_type> struct binary_le_impl { + value_type Value; + binary_le_impl(value_type V) : Value(V) {} +}; + +template <typename value_type> +raw_ostream &operator<<(raw_ostream &OS, + const binary_le_impl<value_type> &BLE) { + char Buffer[sizeof(BLE.Value)]; + support::endian::write<value_type, support::little, support::unaligned>( + Buffer, BLE.Value); + OS.write(Buffer, sizeof(BLE.Value)); + return OS; +} + +template <typename value_type> +binary_le_impl<value_type> binary_le(value_type V) { + return binary_le_impl<value_type>(V); +} + +template <size_t NumBytes> struct zeros_impl {}; + +template <size_t NumBytes> +raw_ostream &operator<<(raw_ostream &OS, const zeros_impl<NumBytes> &) { + char Buffer[NumBytes]; + memset(Buffer, 0, sizeof(Buffer)); + OS.write(Buffer, sizeof(Buffer)); + return OS; +} + +template <typename T> zeros_impl<sizeof(T)> zeros(const T &) { + return zeros_impl<sizeof(T)>(); +} + +template <typename T> +static uint32_t initializeOptionalHeader(COFFParser &CP, uint16_t Magic, + T Header) { + memset(Header, 0, sizeof(*Header)); + Header->Magic = Magic; + Header->SectionAlignment = CP.Obj.OptionalHeader->Header.SectionAlignment; + Header->FileAlignment = CP.Obj.OptionalHeader->Header.FileAlignment; + uint32_t SizeOfCode = 0, SizeOfInitializedData = 0, + SizeOfUninitializedData = 0; + uint32_t SizeOfHeaders = alignTo(CP.SectionTableStart + CP.SectionTableSize, + Header->FileAlignment); + uint32_t SizeOfImage = alignTo(SizeOfHeaders, Header->SectionAlignment); + uint32_t BaseOfData = 0; + for (const COFFYAML::Section &S : CP.Obj.Sections) { + if (S.Header.Characteristics & COFF::IMAGE_SCN_CNT_CODE) + SizeOfCode += S.Header.SizeOfRawData; + if (S.Header.Characteristics & COFF::IMAGE_SCN_CNT_INITIALIZED_DATA) + SizeOfInitializedData += S.Header.SizeOfRawData; + if (S.Header.Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) + SizeOfUninitializedData += S.Header.SizeOfRawData; + if (S.Name.equals(".text")) + Header->BaseOfCode = S.Header.VirtualAddress; // RVA + else if (S.Name.equals(".data")) + BaseOfData = S.Header.VirtualAddress; // RVA + if (S.Header.VirtualAddress) + SizeOfImage += alignTo(S.Header.VirtualSize, Header->SectionAlignment); + } + Header->SizeOfCode = SizeOfCode; + Header->SizeOfInitializedData = SizeOfInitializedData; + Header->SizeOfUninitializedData = SizeOfUninitializedData; + Header->AddressOfEntryPoint = + CP.Obj.OptionalHeader->Header.AddressOfEntryPoint; // RVA + Header->ImageBase = CP.Obj.OptionalHeader->Header.ImageBase; + Header->MajorOperatingSystemVersion = + CP.Obj.OptionalHeader->Header.MajorOperatingSystemVersion; + Header->MinorOperatingSystemVersion = + CP.Obj.OptionalHeader->Header.MinorOperatingSystemVersion; + Header->MajorImageVersion = CP.Obj.OptionalHeader->Header.MajorImageVersion; + Header->MinorImageVersion = CP.Obj.OptionalHeader->Header.MinorImageVersion; + Header->MajorSubsystemVersion = + CP.Obj.OptionalHeader->Header.MajorSubsystemVersion; + Header->MinorSubsystemVersion = + CP.Obj.OptionalHeader->Header.MinorSubsystemVersion; + Header->SizeOfImage = SizeOfImage; + Header->SizeOfHeaders = SizeOfHeaders; + Header->Subsystem = CP.Obj.OptionalHeader->Header.Subsystem; + Header->DLLCharacteristics = CP.Obj.OptionalHeader->Header.DLLCharacteristics; + Header->SizeOfStackReserve = CP.Obj.OptionalHeader->Header.SizeOfStackReserve; + Header->SizeOfStackCommit = CP.Obj.OptionalHeader->Header.SizeOfStackCommit; + Header->SizeOfHeapReserve = CP.Obj.OptionalHeader->Header.SizeOfHeapReserve; + Header->SizeOfHeapCommit = CP.Obj.OptionalHeader->Header.SizeOfHeapCommit; + Header->NumberOfRvaAndSize = COFF::NUM_DATA_DIRECTORIES + 1; + return BaseOfData; +} + +static bool writeCOFF(COFFParser &CP, raw_ostream &OS) { + if (CP.isPE()) { + // PE files start with a DOS stub. + object::dos_header DH; + memset(&DH, 0, sizeof(DH)); + + // DOS EXEs start with "MZ" magic. + DH.Magic[0] = 'M'; + DH.Magic[1] = 'Z'; + // Initializing the AddressOfRelocationTable is strictly optional but + // mollifies certain tools which expect it to have a value greater than + // 0x40. + DH.AddressOfRelocationTable = sizeof(DH); + // This is the address of the PE signature. + DH.AddressOfNewExeHeader = DOSStubSize; + + // Write out our DOS stub. + OS.write(reinterpret_cast<char *>(&DH), sizeof(DH)); + // Write padding until we reach the position of where our PE signature + // should live. + OS.write_zeros(DOSStubSize - sizeof(DH)); + // Write out the PE signature. + OS.write(COFF::PEMagic, sizeof(COFF::PEMagic)); + } + if (CP.useBigObj()) { + OS << binary_le(static_cast<uint16_t>(COFF::IMAGE_FILE_MACHINE_UNKNOWN)) + << binary_le(static_cast<uint16_t>(0xffff)) + << binary_le( + static_cast<uint16_t>(COFF::BigObjHeader::MinBigObjectVersion)) + << binary_le(CP.Obj.Header.Machine) + << binary_le(CP.Obj.Header.TimeDateStamp); + OS.write(COFF::BigObjMagic, sizeof(COFF::BigObjMagic)); + OS << zeros(uint32_t(0)) << zeros(uint32_t(0)) << zeros(uint32_t(0)) + << zeros(uint32_t(0)) << binary_le(CP.Obj.Header.NumberOfSections) + << binary_le(CP.Obj.Header.PointerToSymbolTable) + << binary_le(CP.Obj.Header.NumberOfSymbols); + } else { + OS << binary_le(CP.Obj.Header.Machine) + << binary_le(static_cast<int16_t>(CP.Obj.Header.NumberOfSections)) + << binary_le(CP.Obj.Header.TimeDateStamp) + << binary_le(CP.Obj.Header.PointerToSymbolTable) + << binary_le(CP.Obj.Header.NumberOfSymbols) + << binary_le(CP.Obj.Header.SizeOfOptionalHeader) + << binary_le(CP.Obj.Header.Characteristics); + } + if (CP.isPE()) { + if (CP.is64Bit()) { + object::pe32plus_header PEH; + initializeOptionalHeader(CP, COFF::PE32Header::PE32_PLUS, &PEH); + OS.write(reinterpret_cast<char *>(&PEH), sizeof(PEH)); + } else { + object::pe32_header PEH; + uint32_t BaseOfData = + initializeOptionalHeader(CP, COFF::PE32Header::PE32, &PEH); + PEH.BaseOfData = BaseOfData; + OS.write(reinterpret_cast<char *>(&PEH), sizeof(PEH)); + } + for (const Optional<COFF::DataDirectory> &DD : + CP.Obj.OptionalHeader->DataDirectories) { + if (!DD.hasValue()) { + OS << zeros(uint32_t(0)); + OS << zeros(uint32_t(0)); + } else { + OS << binary_le(DD->RelativeVirtualAddress); + OS << binary_le(DD->Size); + } + } + OS << zeros(uint32_t(0)); + OS << zeros(uint32_t(0)); + } + + assert(OS.tell() == CP.SectionTableStart); + // Output section table. + for (std::vector<COFFYAML::Section>::iterator i = CP.Obj.Sections.begin(), + e = CP.Obj.Sections.end(); + i != e; ++i) { + OS.write(i->Header.Name, COFF::NameSize); + OS << binary_le(i->Header.VirtualSize) + << binary_le(i->Header.VirtualAddress) + << binary_le(i->Header.SizeOfRawData) + << binary_le(i->Header.PointerToRawData) + << binary_le(i->Header.PointerToRelocations) + << binary_le(i->Header.PointerToLineNumbers) + << binary_le(i->Header.NumberOfRelocations) + << binary_le(i->Header.NumberOfLineNumbers) + << binary_le(i->Header.Characteristics); + } + assert(OS.tell() == CP.SectionTableStart + CP.SectionTableSize); + + unsigned CurSymbol = 0; + StringMap<unsigned> SymbolTableIndexMap; + for (std::vector<COFFYAML::Symbol>::iterator I = CP.Obj.Symbols.begin(), + E = CP.Obj.Symbols.end(); + I != E; ++I) { + SymbolTableIndexMap[I->Name] = CurSymbol; + CurSymbol += 1 + I->Header.NumberOfAuxSymbols; + } + + // Output section data. + for (const COFFYAML::Section &S : CP.Obj.Sections) { + if (S.Header.SizeOfRawData == 0 || S.Header.PointerToRawData == 0) + continue; + assert(S.Header.PointerToRawData >= OS.tell()); + OS.write_zeros(S.Header.PointerToRawData - OS.tell()); + S.SectionData.writeAsBinary(OS); + assert(S.Header.SizeOfRawData >= S.SectionData.binary_size()); + OS.write_zeros(S.Header.SizeOfRawData - S.SectionData.binary_size()); + for (const COFFYAML::Relocation &R : S.Relocations) { + uint32_t SymbolTableIndex; + if (R.SymbolTableIndex) { + if (!R.SymbolName.empty()) + WithColor::error() + << "Both SymbolName and SymbolTableIndex specified\n"; + SymbolTableIndex = *R.SymbolTableIndex; + } else { + SymbolTableIndex = SymbolTableIndexMap[R.SymbolName]; + } + OS << binary_le(R.VirtualAddress) << binary_le(SymbolTableIndex) + << binary_le(R.Type); + } + } + + // Output symbol table. + + for (std::vector<COFFYAML::Symbol>::const_iterator i = CP.Obj.Symbols.begin(), + e = CP.Obj.Symbols.end(); + i != e; ++i) { + OS.write(i->Header.Name, COFF::NameSize); + OS << binary_le(i->Header.Value); + if (CP.useBigObj()) + OS << binary_le(i->Header.SectionNumber); + else + OS << binary_le(static_cast<int16_t>(i->Header.SectionNumber)); + OS << binary_le(i->Header.Type) << binary_le(i->Header.StorageClass) + << binary_le(i->Header.NumberOfAuxSymbols); + + if (i->FunctionDefinition) { + OS << binary_le(i->FunctionDefinition->TagIndex) + << binary_le(i->FunctionDefinition->TotalSize) + << binary_le(i->FunctionDefinition->PointerToLinenumber) + << binary_le(i->FunctionDefinition->PointerToNextFunction) + << zeros(i->FunctionDefinition->unused); + OS.write_zeros(CP.getSymbolSize() - COFF::Symbol16Size); + } + if (i->bfAndefSymbol) { + OS << zeros(i->bfAndefSymbol->unused1) + << binary_le(i->bfAndefSymbol->Linenumber) + << zeros(i->bfAndefSymbol->unused2) + << binary_le(i->bfAndefSymbol->PointerToNextFunction) + << zeros(i->bfAndefSymbol->unused3); + OS.write_zeros(CP.getSymbolSize() - COFF::Symbol16Size); + } + if (i->WeakExternal) { + OS << binary_le(i->WeakExternal->TagIndex) + << binary_le(i->WeakExternal->Characteristics) + << zeros(i->WeakExternal->unused); + OS.write_zeros(CP.getSymbolSize() - COFF::Symbol16Size); + } + if (!i->File.empty()) { + unsigned SymbolSize = CP.getSymbolSize(); + uint32_t NumberOfAuxRecords = + (i->File.size() + SymbolSize - 1) / SymbolSize; + uint32_t NumberOfAuxBytes = NumberOfAuxRecords * SymbolSize; + uint32_t NumZeros = NumberOfAuxBytes - i->File.size(); + OS.write(i->File.data(), i->File.size()); + OS.write_zeros(NumZeros); + } + if (i->SectionDefinition) { + OS << binary_le(i->SectionDefinition->Length) + << binary_le(i->SectionDefinition->NumberOfRelocations) + << binary_le(i->SectionDefinition->NumberOfLinenumbers) + << binary_le(i->SectionDefinition->CheckSum) + << binary_le(static_cast<int16_t>(i->SectionDefinition->Number)) + << binary_le(i->SectionDefinition->Selection) + << zeros(i->SectionDefinition->unused) + << binary_le(static_cast<int16_t>(i->SectionDefinition->Number >> 16)); + OS.write_zeros(CP.getSymbolSize() - COFF::Symbol16Size); + } + if (i->CLRToken) { + OS << binary_le(i->CLRToken->AuxType) << zeros(i->CLRToken->unused1) + << binary_le(i->CLRToken->SymbolTableIndex) + << zeros(i->CLRToken->unused2); + OS.write_zeros(CP.getSymbolSize() - COFF::Symbol16Size); + } + } + + // Output string table. + if (CP.Obj.Header.PointerToSymbolTable) + OS.write(&CP.StringTable[0], CP.StringTable.size()); + return true; +} + +namespace llvm { +namespace yaml { + +bool yaml2coff(llvm::COFFYAML::Object &Doc, raw_ostream &Out, + ErrorHandler ErrHandler) { + COFFParser CP(Doc, ErrHandler); + if (!CP.parse()) { + ErrHandler("failed to parse YAML file"); + return false; + } + + if (!layoutOptionalHeader(CP)) { + ErrHandler("failed to layout optional header for COFF file"); + return false; + } + + if (!layoutCOFF(CP)) { + ErrHandler("failed to layout COFF file"); + return false; + } + if (!writeCOFF(CP, Out)) { + ErrHandler("failed to write COFF file"); + return false; + } + return true; +} + +} // namespace yaml +} // namespace llvm diff --git a/lib/ObjectYAML/CodeViewYAMLSymbols.cpp b/lib/ObjectYAML/CodeViewYAMLSymbols.cpp index 227107c051dd..95409fdc3300 100644 --- a/lib/ObjectYAML/CodeViewYAMLSymbols.cpp +++ b/lib/ObjectYAML/CodeViewYAMLSymbols.cpp @@ -391,7 +391,7 @@ template <> void SymbolRecordImpl<DefRangeRegisterSym>::map(IO &IO) { } template <> void SymbolRecordImpl<DefRangeFramePointerRelSym>::map(IO &IO) { - IO.mapRequired("Offset", Symbol.Offset); + IO.mapRequired("Offset", Symbol.Hdr.Offset); IO.mapRequired("Range", Symbol.Range); IO.mapRequired("Gaps", Symbol.Gaps); } diff --git a/lib/ObjectYAML/ELFEmitter.cpp b/lib/ObjectYAML/ELFEmitter.cpp new file mode 100644 index 000000000000..e0faed256f6b --- /dev/null +++ b/lib/ObjectYAML/ELFEmitter.cpp @@ -0,0 +1,1152 @@ +//===- yaml2elf - Convert YAML to a ELF 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 ELF component of yaml2obj. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/StringTableBuilder.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/ObjectYAML/ELFYAML.h" +#include "llvm/ObjectYAML/yaml2obj.h" +#include "llvm/Support/EndianStream.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +// This class is used to build up a contiguous binary blob while keeping +// track of an offset in the output (which notionally begins at +// `InitialOffset`). +namespace { +class ContiguousBlobAccumulator { + const uint64_t InitialOffset; + SmallVector<char, 128> Buf; + raw_svector_ostream OS; + + /// \returns The new offset. + uint64_t padToAlignment(unsigned Align) { + if (Align == 0) + Align = 1; + uint64_t CurrentOffset = InitialOffset + OS.tell(); + uint64_t AlignedOffset = alignTo(CurrentOffset, Align); + OS.write_zeros(AlignedOffset - CurrentOffset); + return AlignedOffset; // == CurrentOffset; + } + +public: + ContiguousBlobAccumulator(uint64_t InitialOffset_) + : InitialOffset(InitialOffset_), Buf(), OS(Buf) {} + template <class Integer> + raw_ostream &getOSAndAlignedOffset(Integer &Offset, unsigned Align) { + Offset = padToAlignment(Align); + return OS; + } + void writeBlobToStream(raw_ostream &Out) { Out << OS.str(); } +}; + +// Used to keep track of section and symbol names, so that in the YAML file +// sections and symbols can be referenced by name instead of by index. +class NameToIdxMap { + StringMap<unsigned> Map; + +public: + /// \Returns false if name is already present in the map. + bool addName(StringRef Name, unsigned Ndx) { + return Map.insert({Name, Ndx}).second; + } + /// \Returns false if name is not present in the map. + bool lookup(StringRef Name, unsigned &Idx) const { + auto I = Map.find(Name); + if (I == Map.end()) + return false; + Idx = I->getValue(); + return true; + } + /// Asserts if name is not present in the map. + unsigned get(StringRef Name) const { + unsigned Idx; + if (lookup(Name, Idx)) + return Idx; + assert(false && "Expected section not found in index"); + return 0; + } + unsigned size() const { return Map.size(); } +}; + +/// "Single point of truth" for the ELF file construction. +/// TODO: This class still has a ways to go before it is truly a "single +/// point of truth". +template <class ELFT> class ELFState { + typedef typename ELFT::Ehdr Elf_Ehdr; + typedef typename ELFT::Phdr Elf_Phdr; + typedef typename ELFT::Shdr Elf_Shdr; + typedef typename ELFT::Sym Elf_Sym; + typedef typename ELFT::Rel Elf_Rel; + typedef typename ELFT::Rela Elf_Rela; + typedef typename ELFT::Relr Elf_Relr; + typedef typename ELFT::Dyn Elf_Dyn; + + enum class SymtabType { Static, Dynamic }; + + /// The future ".strtab" section. + StringTableBuilder DotStrtab{StringTableBuilder::ELF}; + + /// The future ".shstrtab" section. + StringTableBuilder DotShStrtab{StringTableBuilder::ELF}; + + /// The future ".dynstr" section. + StringTableBuilder DotDynstr{StringTableBuilder::ELF}; + + NameToIdxMap SN2I; + NameToIdxMap SymN2I; + NameToIdxMap DynSymN2I; + ELFYAML::Object &Doc; + + bool HasError = false; + yaml::ErrorHandler ErrHandler; + void reportError(const Twine &Msg); + + std::vector<Elf_Sym> toELFSymbols(ArrayRef<ELFYAML::Symbol> Symbols, + const StringTableBuilder &Strtab); + unsigned toSectionIndex(StringRef S, StringRef LocSec, StringRef LocSym = ""); + unsigned toSymbolIndex(StringRef S, StringRef LocSec, bool IsDynamic); + + void buildSectionIndex(); + void buildSymbolIndexes(); + void initProgramHeaders(std::vector<Elf_Phdr> &PHeaders); + bool initImplicitHeader(ContiguousBlobAccumulator &CBA, Elf_Shdr &Header, + StringRef SecName, ELFYAML::Section *YAMLSec); + void initSectionHeaders(std::vector<Elf_Shdr> &SHeaders, + ContiguousBlobAccumulator &CBA); + void initSymtabSectionHeader(Elf_Shdr &SHeader, SymtabType STType, + ContiguousBlobAccumulator &CBA, + ELFYAML::Section *YAMLSec); + void initStrtabSectionHeader(Elf_Shdr &SHeader, StringRef Name, + StringTableBuilder &STB, + ContiguousBlobAccumulator &CBA, + ELFYAML::Section *YAMLSec); + void setProgramHeaderLayout(std::vector<Elf_Phdr> &PHeaders, + std::vector<Elf_Shdr> &SHeaders); + void finalizeStrings(); + void writeELFHeader(ContiguousBlobAccumulator &CBA, raw_ostream &OS); + void writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::RawContentSection &Section, + ContiguousBlobAccumulator &CBA); + void writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::RelocationSection &Section, + ContiguousBlobAccumulator &CBA); + void writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::Group &Group, + ContiguousBlobAccumulator &CBA); + void writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::SymtabShndxSection &Shndx, + ContiguousBlobAccumulator &CBA); + void writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::SymverSection &Section, + ContiguousBlobAccumulator &CBA); + void writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::VerneedSection &Section, + ContiguousBlobAccumulator &CBA); + void writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::VerdefSection &Section, + ContiguousBlobAccumulator &CBA); + void writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::MipsABIFlags &Section, + ContiguousBlobAccumulator &CBA); + void writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::DynamicSection &Section, + ContiguousBlobAccumulator &CBA); + void writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::StackSizesSection &Section, + ContiguousBlobAccumulator &CBA); + void writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::HashSection &Section, + ContiguousBlobAccumulator &CBA); + void writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::AddrsigSection &Section, + ContiguousBlobAccumulator &CBA); + + ELFState(ELFYAML::Object &D, yaml::ErrorHandler EH); + +public: + static bool writeELF(raw_ostream &OS, ELFYAML::Object &Doc, + yaml::ErrorHandler EH); +}; +} // end anonymous namespace + +template <class T> static size_t arrayDataSize(ArrayRef<T> A) { + return A.size() * sizeof(T); +} + +template <class T> static void writeArrayData(raw_ostream &OS, ArrayRef<T> A) { + OS.write((const char *)A.data(), arrayDataSize(A)); +} + +template <class T> static void zero(T &Obj) { memset(&Obj, 0, sizeof(Obj)); } + +template <class ELFT> +ELFState<ELFT>::ELFState(ELFYAML::Object &D, yaml::ErrorHandler EH) + : Doc(D), ErrHandler(EH) { + StringSet<> DocSections; + for (std::unique_ptr<ELFYAML::Section> &D : Doc.Sections) { + if (!D->Name.empty()) + DocSections.insert(D->Name); + + // Some sections wants to link to .symtab by default. + // That means we want to create the symbol table for them. + if (D->Type == llvm::ELF::SHT_REL || D->Type == llvm::ELF::SHT_RELA) + if (!Doc.Symbols && D->Link.empty()) + Doc.Symbols.emplace(); + } + + // Insert SHT_NULL section implicitly when it is not defined in YAML. + if (Doc.Sections.empty() || Doc.Sections.front()->Type != ELF::SHT_NULL) + Doc.Sections.insert( + Doc.Sections.begin(), + std::make_unique<ELFYAML::Section>( + ELFYAML::Section::SectionKind::RawContent, /*IsImplicit=*/true)); + + std::vector<StringRef> ImplicitSections; + if (Doc.Symbols) + ImplicitSections.push_back(".symtab"); + ImplicitSections.insert(ImplicitSections.end(), {".strtab", ".shstrtab"}); + + if (!Doc.DynamicSymbols.empty()) + ImplicitSections.insert(ImplicitSections.end(), {".dynsym", ".dynstr"}); + + // Insert placeholders for implicit sections that are not + // defined explicitly in YAML. + for (StringRef SecName : ImplicitSections) { + if (DocSections.count(SecName)) + continue; + + std::unique_ptr<ELFYAML::Section> Sec = std::make_unique<ELFYAML::Section>( + ELFYAML::Section::SectionKind::RawContent, true /*IsImplicit*/); + Sec->Name = SecName; + Doc.Sections.push_back(std::move(Sec)); + } +} + +template <class ELFT> +void ELFState<ELFT>::writeELFHeader(ContiguousBlobAccumulator &CBA, raw_ostream &OS) { + using namespace llvm::ELF; + + Elf_Ehdr Header; + zero(Header); + Header.e_ident[EI_MAG0] = 0x7f; + Header.e_ident[EI_MAG1] = 'E'; + Header.e_ident[EI_MAG2] = 'L'; + Header.e_ident[EI_MAG3] = 'F'; + Header.e_ident[EI_CLASS] = ELFT::Is64Bits ? ELFCLASS64 : ELFCLASS32; + Header.e_ident[EI_DATA] = Doc.Header.Data; + Header.e_ident[EI_VERSION] = EV_CURRENT; + Header.e_ident[EI_OSABI] = Doc.Header.OSABI; + Header.e_ident[EI_ABIVERSION] = Doc.Header.ABIVersion; + Header.e_type = Doc.Header.Type; + Header.e_machine = Doc.Header.Machine; + Header.e_version = EV_CURRENT; + Header.e_entry = Doc.Header.Entry; + Header.e_phoff = Doc.ProgramHeaders.size() ? sizeof(Header) : 0; + Header.e_flags = Doc.Header.Flags; + Header.e_ehsize = sizeof(Elf_Ehdr); + Header.e_phentsize = Doc.ProgramHeaders.size() ? sizeof(Elf_Phdr) : 0; + Header.e_phnum = Doc.ProgramHeaders.size(); + + Header.e_shentsize = + Doc.Header.SHEntSize ? (uint16_t)*Doc.Header.SHEntSize : sizeof(Elf_Shdr); + // Immediately following the ELF header and program headers. + // Align the start of the section header and write the ELF header. + uint64_t SHOff; + CBA.getOSAndAlignedOffset(SHOff, sizeof(typename ELFT::uint)); + Header.e_shoff = + Doc.Header.SHOff ? typename ELFT::uint(*Doc.Header.SHOff) : SHOff; + Header.e_shnum = + Doc.Header.SHNum ? (uint16_t)*Doc.Header.SHNum : Doc.Sections.size(); + Header.e_shstrndx = Doc.Header.SHStrNdx ? (uint16_t)*Doc.Header.SHStrNdx + : SN2I.get(".shstrtab"); + + OS.write((const char *)&Header, sizeof(Header)); +} + +template <class ELFT> +void ELFState<ELFT>::initProgramHeaders(std::vector<Elf_Phdr> &PHeaders) { + for (const auto &YamlPhdr : Doc.ProgramHeaders) { + Elf_Phdr Phdr; + Phdr.p_type = YamlPhdr.Type; + Phdr.p_flags = YamlPhdr.Flags; + Phdr.p_vaddr = YamlPhdr.VAddr; + Phdr.p_paddr = YamlPhdr.PAddr; + PHeaders.push_back(Phdr); + } +} + +template <class ELFT> +unsigned ELFState<ELFT>::toSectionIndex(StringRef S, StringRef LocSec, + StringRef LocSym) { + unsigned Index; + if (SN2I.lookup(S, Index) || to_integer(S, Index)) + return Index; + + assert(LocSec.empty() || LocSym.empty()); + if (!LocSym.empty()) + reportError("unknown section referenced: '" + S + "' by YAML symbol '" + + LocSym + "'"); + else + reportError("unknown section referenced: '" + S + "' by YAML section '" + + LocSec + "'"); + return 0; +} + +template <class ELFT> +unsigned ELFState<ELFT>::toSymbolIndex(StringRef S, StringRef LocSec, + bool IsDynamic) { + const NameToIdxMap &SymMap = IsDynamic ? DynSymN2I : SymN2I; + unsigned Index; + // Here we try to look up S in the symbol table. If it is not there, + // treat its value as a symbol index. + if (!SymMap.lookup(S, Index) && !to_integer(S, Index)) { + reportError("unknown symbol referenced: '" + S + "' by YAML section '" + + LocSec + "'"); + return 0; + } + return Index; +} + +template <class ELFT> +bool ELFState<ELFT>::initImplicitHeader(ContiguousBlobAccumulator &CBA, + Elf_Shdr &Header, StringRef SecName, + ELFYAML::Section *YAMLSec) { + // Check if the header was already initialized. + if (Header.sh_offset) + return false; + + if (SecName == ".symtab") + initSymtabSectionHeader(Header, SymtabType::Static, CBA, YAMLSec); + else if (SecName == ".strtab") + initStrtabSectionHeader(Header, SecName, DotStrtab, CBA, YAMLSec); + else if (SecName == ".shstrtab") + initStrtabSectionHeader(Header, SecName, DotShStrtab, CBA, YAMLSec); + else if (SecName == ".dynsym") + initSymtabSectionHeader(Header, SymtabType::Dynamic, CBA, YAMLSec); + else if (SecName == ".dynstr") + initStrtabSectionHeader(Header, SecName, DotDynstr, CBA, YAMLSec); + else + return false; + + // Override the fields if requested. + if (YAMLSec) { + if (YAMLSec->ShName) + Header.sh_name = *YAMLSec->ShName; + if (YAMLSec->ShOffset) + Header.sh_offset = *YAMLSec->ShOffset; + if (YAMLSec->ShSize) + Header.sh_size = *YAMLSec->ShSize; + } + + return true; +} + +StringRef llvm::ELFYAML::dropUniqueSuffix(StringRef S) { + size_t SuffixPos = S.rfind(" ["); + if (SuffixPos == StringRef::npos) + return S; + return S.substr(0, SuffixPos); +} + +template <class ELFT> +void ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders, + ContiguousBlobAccumulator &CBA) { + // Ensure SHN_UNDEF entry is present. An all-zero section header is a + // valid SHN_UNDEF entry since SHT_NULL == 0. + SHeaders.resize(Doc.Sections.size()); + + for (size_t I = 0; I < Doc.Sections.size(); ++I) { + ELFYAML::Section *Sec = Doc.Sections[I].get(); + if (I == 0 && Sec->IsImplicit) + continue; + + // We have a few sections like string or symbol tables that are usually + // added implicitly to the end. However, if they are explicitly specified + // in the YAML, we need to write them here. This ensures the file offset + // remains correct. + Elf_Shdr &SHeader = SHeaders[I]; + if (initImplicitHeader(CBA, SHeader, Sec->Name, + Sec->IsImplicit ? nullptr : Sec)) + continue; + + assert(Sec && "It can't be null unless it is an implicit section. But all " + "implicit sections should already have been handled above."); + + SHeader.sh_name = + DotShStrtab.getOffset(ELFYAML::dropUniqueSuffix(Sec->Name)); + SHeader.sh_type = Sec->Type; + if (Sec->Flags) + SHeader.sh_flags = *Sec->Flags; + SHeader.sh_addr = Sec->Address; + SHeader.sh_addralign = Sec->AddressAlign; + + if (!Sec->Link.empty()) + SHeader.sh_link = toSectionIndex(Sec->Link, Sec->Name); + + if (I == 0) { + if (auto RawSec = dyn_cast<ELFYAML::RawContentSection>(Sec)) { + // We do not write any content for special SHN_UNDEF section. + if (RawSec->Size) + SHeader.sh_size = *RawSec->Size; + if (RawSec->Info) + SHeader.sh_info = *RawSec->Info; + } + if (Sec->EntSize) + SHeader.sh_entsize = *Sec->EntSize; + } else if (auto S = dyn_cast<ELFYAML::RawContentSection>(Sec)) { + writeSectionContent(SHeader, *S, CBA); + } else if (auto S = dyn_cast<ELFYAML::SymtabShndxSection>(Sec)) { + writeSectionContent(SHeader, *S, CBA); + } else if (auto S = dyn_cast<ELFYAML::RelocationSection>(Sec)) { + writeSectionContent(SHeader, *S, CBA); + } else if (auto S = dyn_cast<ELFYAML::Group>(Sec)) { + writeSectionContent(SHeader, *S, CBA); + } else if (auto S = dyn_cast<ELFYAML::MipsABIFlags>(Sec)) { + writeSectionContent(SHeader, *S, CBA); + } else if (auto S = dyn_cast<ELFYAML::NoBitsSection>(Sec)) { + SHeader.sh_entsize = 0; + SHeader.sh_size = S->Size; + // SHT_NOBITS section does not have content + // so just to setup the section offset. + CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + } else if (auto S = dyn_cast<ELFYAML::DynamicSection>(Sec)) { + writeSectionContent(SHeader, *S, CBA); + } else if (auto S = dyn_cast<ELFYAML::SymverSection>(Sec)) { + writeSectionContent(SHeader, *S, CBA); + } else if (auto S = dyn_cast<ELFYAML::VerneedSection>(Sec)) { + writeSectionContent(SHeader, *S, CBA); + } else if (auto S = dyn_cast<ELFYAML::VerdefSection>(Sec)) { + writeSectionContent(SHeader, *S, CBA); + } else if (auto S = dyn_cast<ELFYAML::StackSizesSection>(Sec)) { + writeSectionContent(SHeader, *S, CBA); + } else if (auto S = dyn_cast<ELFYAML::HashSection>(Sec)) { + writeSectionContent(SHeader, *S, CBA); + } else if (auto S = dyn_cast<ELFYAML::AddrsigSection>(Sec)) { + writeSectionContent(SHeader, *S, CBA); + } else { + llvm_unreachable("Unknown section type"); + } + + // Override the fields if requested. + if (Sec) { + if (Sec->ShName) + SHeader.sh_name = *Sec->ShName; + if (Sec->ShOffset) + SHeader.sh_offset = *Sec->ShOffset; + if (Sec->ShSize) + SHeader.sh_size = *Sec->ShSize; + } + } +} + +static size_t findFirstNonGlobal(ArrayRef<ELFYAML::Symbol> Symbols) { + for (size_t I = 0; I < Symbols.size(); ++I) + if (Symbols[I].Binding.value != ELF::STB_LOCAL) + return I; + return Symbols.size(); +} + +static uint64_t writeContent(raw_ostream &OS, + const Optional<yaml::BinaryRef> &Content, + const Optional<llvm::yaml::Hex64> &Size) { + size_t ContentSize = 0; + if (Content) { + Content->writeAsBinary(OS); + ContentSize = Content->binary_size(); + } + + if (!Size) + return ContentSize; + + OS.write_zeros(*Size - ContentSize); + return *Size; +} + +template <class ELFT> +std::vector<typename ELFT::Sym> +ELFState<ELFT>::toELFSymbols(ArrayRef<ELFYAML::Symbol> Symbols, + const StringTableBuilder &Strtab) { + std::vector<Elf_Sym> Ret; + Ret.resize(Symbols.size() + 1); + + size_t I = 0; + for (const auto &Sym : Symbols) { + Elf_Sym &Symbol = Ret[++I]; + + // If NameIndex, which contains the name offset, is explicitly specified, we + // use it. This is useful for preparing broken objects. Otherwise, we add + // the specified Name to the string table builder to get its offset. + if (Sym.NameIndex) + Symbol.st_name = *Sym.NameIndex; + else if (!Sym.Name.empty()) + Symbol.st_name = Strtab.getOffset(ELFYAML::dropUniqueSuffix(Sym.Name)); + + Symbol.setBindingAndType(Sym.Binding, Sym.Type); + if (!Sym.Section.empty()) + Symbol.st_shndx = toSectionIndex(Sym.Section, "", Sym.Name); + else if (Sym.Index) + Symbol.st_shndx = *Sym.Index; + + Symbol.st_value = Sym.Value; + Symbol.st_other = Sym.Other ? *Sym.Other : 0; + Symbol.st_size = Sym.Size; + } + + return Ret; +} + +template <class ELFT> +void ELFState<ELFT>::initSymtabSectionHeader(Elf_Shdr &SHeader, + SymtabType STType, + ContiguousBlobAccumulator &CBA, + ELFYAML::Section *YAMLSec) { + + bool IsStatic = STType == SymtabType::Static; + ArrayRef<ELFYAML::Symbol> Symbols; + if (IsStatic && Doc.Symbols) + Symbols = *Doc.Symbols; + else if (!IsStatic) + Symbols = Doc.DynamicSymbols; + + ELFYAML::RawContentSection *RawSec = + dyn_cast_or_null<ELFYAML::RawContentSection>(YAMLSec); + if (RawSec && !Symbols.empty() && (RawSec->Content || RawSec->Size)) { + if (RawSec->Content) + reportError("cannot specify both `Content` and " + + (IsStatic ? Twine("`Symbols`") : Twine("`DynamicSymbols`")) + + " for symbol table section '" + RawSec->Name + "'"); + if (RawSec->Size) + reportError("cannot specify both `Size` and " + + (IsStatic ? Twine("`Symbols`") : Twine("`DynamicSymbols`")) + + " for symbol table section '" + RawSec->Name + "'"); + return; + } + + zero(SHeader); + SHeader.sh_name = DotShStrtab.getOffset(IsStatic ? ".symtab" : ".dynsym"); + + if (YAMLSec) + SHeader.sh_type = YAMLSec->Type; + else + SHeader.sh_type = IsStatic ? ELF::SHT_SYMTAB : ELF::SHT_DYNSYM; + + if (RawSec && !RawSec->Link.empty()) { + // If the Link field is explicitly defined in the document, + // we should use it. + SHeader.sh_link = toSectionIndex(RawSec->Link, RawSec->Name); + } else { + // When we describe the .dynsym section in the document explicitly, it is + // allowed to omit the "DynamicSymbols" tag. In this case .dynstr is not + // added implicitly and we should be able to leave the Link zeroed if + // .dynstr is not defined. + unsigned Link = 0; + if (IsStatic) + Link = SN2I.get(".strtab"); + else + SN2I.lookup(".dynstr", Link); + SHeader.sh_link = Link; + } + + if (YAMLSec && YAMLSec->Flags) + SHeader.sh_flags = *YAMLSec->Flags; + else if (!IsStatic) + SHeader.sh_flags = ELF::SHF_ALLOC; + + // If the symbol table section is explicitly described in the YAML + // then we should set the fields requested. + SHeader.sh_info = (RawSec && RawSec->Info) ? (unsigned)(*RawSec->Info) + : findFirstNonGlobal(Symbols) + 1; + SHeader.sh_entsize = (YAMLSec && YAMLSec->EntSize) + ? (uint64_t)(*YAMLSec->EntSize) + : sizeof(Elf_Sym); + SHeader.sh_addralign = YAMLSec ? (uint64_t)YAMLSec->AddressAlign : 8; + SHeader.sh_addr = YAMLSec ? (uint64_t)YAMLSec->Address : 0; + + auto &OS = CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + if (RawSec && (RawSec->Content || RawSec->Size)) { + assert(Symbols.empty()); + SHeader.sh_size = writeContent(OS, RawSec->Content, RawSec->Size); + return; + } + + std::vector<Elf_Sym> Syms = + toELFSymbols(Symbols, IsStatic ? DotStrtab : DotDynstr); + writeArrayData(OS, makeArrayRef(Syms)); + SHeader.sh_size = arrayDataSize(makeArrayRef(Syms)); +} + +template <class ELFT> +void ELFState<ELFT>::initStrtabSectionHeader(Elf_Shdr &SHeader, StringRef Name, + StringTableBuilder &STB, + ContiguousBlobAccumulator &CBA, + ELFYAML::Section *YAMLSec) { + zero(SHeader); + SHeader.sh_name = DotShStrtab.getOffset(Name); + SHeader.sh_type = YAMLSec ? YAMLSec->Type : ELF::SHT_STRTAB; + SHeader.sh_addralign = YAMLSec ? (uint64_t)YAMLSec->AddressAlign : 1; + + ELFYAML::RawContentSection *RawSec = + dyn_cast_or_null<ELFYAML::RawContentSection>(YAMLSec); + + auto &OS = CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + if (RawSec && (RawSec->Content || RawSec->Size)) { + SHeader.sh_size = writeContent(OS, RawSec->Content, RawSec->Size); + } else { + STB.write(OS); + SHeader.sh_size = STB.getSize(); + } + + if (YAMLSec && YAMLSec->EntSize) + SHeader.sh_entsize = *YAMLSec->EntSize; + + if (RawSec && RawSec->Info) + SHeader.sh_info = *RawSec->Info; + + if (YAMLSec && YAMLSec->Flags) + SHeader.sh_flags = *YAMLSec->Flags; + else if (Name == ".dynstr") + SHeader.sh_flags = ELF::SHF_ALLOC; + + // If the section is explicitly described in the YAML + // then we want to use its section address. + if (YAMLSec) + SHeader.sh_addr = YAMLSec->Address; +} + +template <class ELFT> void ELFState<ELFT>::reportError(const Twine &Msg) { + ErrHandler(Msg); + HasError = true; +} + +template <class ELFT> +void ELFState<ELFT>::setProgramHeaderLayout(std::vector<Elf_Phdr> &PHeaders, + std::vector<Elf_Shdr> &SHeaders) { + uint32_t PhdrIdx = 0; + for (auto &YamlPhdr : Doc.ProgramHeaders) { + Elf_Phdr &PHeader = PHeaders[PhdrIdx++]; + + std::vector<Elf_Shdr *> Sections; + for (const ELFYAML::SectionName &SecName : YamlPhdr.Sections) { + unsigned Index; + if (!SN2I.lookup(SecName.Section, Index)) { + reportError("unknown section referenced: '" + SecName.Section + + "' by program header"); + continue; + } + Sections.push_back(&SHeaders[Index]); + } + + if (YamlPhdr.Offset) { + PHeader.p_offset = *YamlPhdr.Offset; + } else { + if (YamlPhdr.Sections.size()) + PHeader.p_offset = UINT32_MAX; + else + PHeader.p_offset = 0; + + // Find the minimum offset for the program header. + for (Elf_Shdr *SHeader : Sections) + PHeader.p_offset = std::min(PHeader.p_offset, SHeader->sh_offset); + } + + // Find the maximum offset of the end of a section in order to set p_filesz + // and p_memsz. When setting p_filesz, trailing SHT_NOBITS sections are not + // counted. + uint64_t FileOffset = PHeader.p_offset, MemOffset = PHeader.p_offset; + for (Elf_Shdr *SHeader : Sections) { + uint64_t End = SHeader->sh_offset + SHeader->sh_size; + MemOffset = std::max(MemOffset, End); + + if (SHeader->sh_type != llvm::ELF::SHT_NOBITS) + FileOffset = std::max(FileOffset, End); + } + + // Set the file size and the memory size if not set explicitly. + PHeader.p_filesz = YamlPhdr.FileSize ? uint64_t(*YamlPhdr.FileSize) + : FileOffset - PHeader.p_offset; + PHeader.p_memsz = YamlPhdr.MemSize ? uint64_t(*YamlPhdr.MemSize) + : MemOffset - PHeader.p_offset; + + if (YamlPhdr.Align) { + PHeader.p_align = *YamlPhdr.Align; + } else { + // Set the alignment of the segment to be the maximum alignment of the + // sections so that by default the segment has a valid and sensible + // alignment. + PHeader.p_align = 1; + for (Elf_Shdr *SHeader : Sections) + PHeader.p_align = std::max(PHeader.p_align, SHeader->sh_addralign); + } + } +} + +template <class ELFT> +void ELFState<ELFT>::writeSectionContent( + Elf_Shdr &SHeader, const ELFYAML::RawContentSection &Section, + ContiguousBlobAccumulator &CBA) { + raw_ostream &OS = + CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + SHeader.sh_size = writeContent(OS, Section.Content, Section.Size); + + if (Section.EntSize) + SHeader.sh_entsize = *Section.EntSize; + else if (Section.Type == llvm::ELF::SHT_RELR) + SHeader.sh_entsize = sizeof(Elf_Relr); + else + SHeader.sh_entsize = 0; + + if (Section.Info) + SHeader.sh_info = *Section.Info; +} + +static bool isMips64EL(const ELFYAML::Object &Doc) { + return Doc.Header.Machine == ELFYAML::ELF_EM(llvm::ELF::EM_MIPS) && + Doc.Header.Class == ELFYAML::ELF_ELFCLASS(ELF::ELFCLASS64) && + Doc.Header.Data == ELFYAML::ELF_ELFDATA(ELF::ELFDATA2LSB); +} + +template <class ELFT> +void ELFState<ELFT>::writeSectionContent( + Elf_Shdr &SHeader, const ELFYAML::RelocationSection &Section, + ContiguousBlobAccumulator &CBA) { + assert((Section.Type == llvm::ELF::SHT_REL || + Section.Type == llvm::ELF::SHT_RELA) && + "Section type is not SHT_REL nor SHT_RELA"); + + bool IsRela = Section.Type == llvm::ELF::SHT_RELA; + SHeader.sh_entsize = IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel); + SHeader.sh_size = SHeader.sh_entsize * Section.Relocations.size(); + + // For relocation section set link to .symtab by default. + if (Section.Link.empty()) + SHeader.sh_link = SN2I.get(".symtab"); + + if (!Section.RelocatableSec.empty()) + SHeader.sh_info = toSectionIndex(Section.RelocatableSec, Section.Name); + + auto &OS = CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + for (const auto &Rel : Section.Relocations) { + unsigned SymIdx = Rel.Symbol ? toSymbolIndex(*Rel.Symbol, Section.Name, + Section.Link == ".dynsym") + : 0; + if (IsRela) { + Elf_Rela REntry; + zero(REntry); + REntry.r_offset = Rel.Offset; + REntry.r_addend = Rel.Addend; + REntry.setSymbolAndType(SymIdx, Rel.Type, isMips64EL(Doc)); + OS.write((const char *)&REntry, sizeof(REntry)); + } else { + Elf_Rel REntry; + zero(REntry); + REntry.r_offset = Rel.Offset; + REntry.setSymbolAndType(SymIdx, Rel.Type, isMips64EL(Doc)); + OS.write((const char *)&REntry, sizeof(REntry)); + } + } +} + +template <class ELFT> +void ELFState<ELFT>::writeSectionContent( + Elf_Shdr &SHeader, const ELFYAML::SymtabShndxSection &Shndx, + ContiguousBlobAccumulator &CBA) { + raw_ostream &OS = + CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + + for (uint32_t E : Shndx.Entries) + support::endian::write<uint32_t>(OS, E, ELFT::TargetEndianness); + + SHeader.sh_entsize = Shndx.EntSize ? (uint64_t)*Shndx.EntSize : 4; + SHeader.sh_size = Shndx.Entries.size() * SHeader.sh_entsize; +} + +template <class ELFT> +void ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::Group &Section, + ContiguousBlobAccumulator &CBA) { + assert(Section.Type == llvm::ELF::SHT_GROUP && + "Section type is not SHT_GROUP"); + + SHeader.sh_entsize = 4; + SHeader.sh_size = SHeader.sh_entsize * Section.Members.size(); + SHeader.sh_info = + toSymbolIndex(Section.Signature, Section.Name, /*IsDynamic=*/false); + + raw_ostream &OS = + CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + + for (const ELFYAML::SectionOrType &Member : Section.Members) { + unsigned int SectionIndex = 0; + if (Member.sectionNameOrType == "GRP_COMDAT") + SectionIndex = llvm::ELF::GRP_COMDAT; + else + SectionIndex = toSectionIndex(Member.sectionNameOrType, Section.Name); + support::endian::write<uint32_t>(OS, SectionIndex, ELFT::TargetEndianness); + } +} + +template <class ELFT> +void ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::SymverSection &Section, + ContiguousBlobAccumulator &CBA) { + raw_ostream &OS = + CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + for (uint16_t Version : Section.Entries) + support::endian::write<uint16_t>(OS, Version, ELFT::TargetEndianness); + + SHeader.sh_entsize = Section.EntSize ? (uint64_t)*Section.EntSize : 2; + SHeader.sh_size = Section.Entries.size() * SHeader.sh_entsize; +} + +template <class ELFT> +void ELFState<ELFT>::writeSectionContent( + Elf_Shdr &SHeader, const ELFYAML::StackSizesSection &Section, + ContiguousBlobAccumulator &CBA) { + using uintX_t = typename ELFT::uint; + raw_ostream &OS = + CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + + if (Section.Content || Section.Size) { + SHeader.sh_size = writeContent(OS, Section.Content, Section.Size); + return; + } + + for (const ELFYAML::StackSizeEntry &E : *Section.Entries) { + support::endian::write<uintX_t>(OS, E.Address, ELFT::TargetEndianness); + SHeader.sh_size += sizeof(uintX_t) + encodeULEB128(E.Size, OS); + } +} + +template <class ELFT> +void ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::HashSection &Section, + ContiguousBlobAccumulator &CBA) { + raw_ostream &OS = + CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + + unsigned Link = 0; + if (Section.Link.empty() && SN2I.lookup(".dynsym", Link)) + SHeader.sh_link = Link; + + if (Section.Content || Section.Size) { + SHeader.sh_size = writeContent(OS, Section.Content, Section.Size); + return; + } + + support::endian::write<uint32_t>(OS, Section.Bucket->size(), + ELFT::TargetEndianness); + support::endian::write<uint32_t>(OS, Section.Chain->size(), + ELFT::TargetEndianness); + for (uint32_t Val : *Section.Bucket) + support::endian::write<uint32_t>(OS, Val, ELFT::TargetEndianness); + for (uint32_t Val : *Section.Chain) + support::endian::write<uint32_t>(OS, Val, ELFT::TargetEndianness); + + SHeader.sh_size = (2 + Section.Bucket->size() + Section.Chain->size()) * 4; +} + +template <class ELFT> +void ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::VerdefSection &Section, + ContiguousBlobAccumulator &CBA) { + typedef typename ELFT::Verdef Elf_Verdef; + typedef typename ELFT::Verdaux Elf_Verdaux; + raw_ostream &OS = + CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + + uint64_t AuxCnt = 0; + for (size_t I = 0; I < Section.Entries.size(); ++I) { + const ELFYAML::VerdefEntry &E = Section.Entries[I]; + + Elf_Verdef VerDef; + VerDef.vd_version = E.Version; + VerDef.vd_flags = E.Flags; + VerDef.vd_ndx = E.VersionNdx; + VerDef.vd_hash = E.Hash; + VerDef.vd_aux = sizeof(Elf_Verdef); + VerDef.vd_cnt = E.VerNames.size(); + if (I == Section.Entries.size() - 1) + VerDef.vd_next = 0; + else + VerDef.vd_next = + sizeof(Elf_Verdef) + E.VerNames.size() * sizeof(Elf_Verdaux); + OS.write((const char *)&VerDef, sizeof(Elf_Verdef)); + + for (size_t J = 0; J < E.VerNames.size(); ++J, ++AuxCnt) { + Elf_Verdaux VernAux; + VernAux.vda_name = DotDynstr.getOffset(E.VerNames[J]); + if (J == E.VerNames.size() - 1) + VernAux.vda_next = 0; + else + VernAux.vda_next = sizeof(Elf_Verdaux); + OS.write((const char *)&VernAux, sizeof(Elf_Verdaux)); + } + } + + SHeader.sh_size = Section.Entries.size() * sizeof(Elf_Verdef) + + AuxCnt * sizeof(Elf_Verdaux); + SHeader.sh_info = Section.Info; +} + +template <class ELFT> +void ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::VerneedSection &Section, + ContiguousBlobAccumulator &CBA) { + typedef typename ELFT::Verneed Elf_Verneed; + typedef typename ELFT::Vernaux Elf_Vernaux; + + auto &OS = CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + + uint64_t AuxCnt = 0; + for (size_t I = 0; I < Section.VerneedV.size(); ++I) { + const ELFYAML::VerneedEntry &VE = Section.VerneedV[I]; + + Elf_Verneed VerNeed; + VerNeed.vn_version = VE.Version; + VerNeed.vn_file = DotDynstr.getOffset(VE.File); + if (I == Section.VerneedV.size() - 1) + VerNeed.vn_next = 0; + else + VerNeed.vn_next = + sizeof(Elf_Verneed) + VE.AuxV.size() * sizeof(Elf_Vernaux); + VerNeed.vn_cnt = VE.AuxV.size(); + VerNeed.vn_aux = sizeof(Elf_Verneed); + OS.write((const char *)&VerNeed, sizeof(Elf_Verneed)); + + for (size_t J = 0; J < VE.AuxV.size(); ++J, ++AuxCnt) { + const ELFYAML::VernauxEntry &VAuxE = VE.AuxV[J]; + + Elf_Vernaux VernAux; + VernAux.vna_hash = VAuxE.Hash; + VernAux.vna_flags = VAuxE.Flags; + VernAux.vna_other = VAuxE.Other; + VernAux.vna_name = DotDynstr.getOffset(VAuxE.Name); + if (J == VE.AuxV.size() - 1) + VernAux.vna_next = 0; + else + VernAux.vna_next = sizeof(Elf_Vernaux); + OS.write((const char *)&VernAux, sizeof(Elf_Vernaux)); + } + } + + SHeader.sh_size = Section.VerneedV.size() * sizeof(Elf_Verneed) + + AuxCnt * sizeof(Elf_Vernaux); + SHeader.sh_info = Section.Info; +} + +template <class ELFT> +void ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::MipsABIFlags &Section, + ContiguousBlobAccumulator &CBA) { + assert(Section.Type == llvm::ELF::SHT_MIPS_ABIFLAGS && + "Section type is not SHT_MIPS_ABIFLAGS"); + + object::Elf_Mips_ABIFlags<ELFT> Flags; + zero(Flags); + SHeader.sh_entsize = sizeof(Flags); + SHeader.sh_size = SHeader.sh_entsize; + + auto &OS = CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + Flags.version = Section.Version; + Flags.isa_level = Section.ISALevel; + Flags.isa_rev = Section.ISARevision; + Flags.gpr_size = Section.GPRSize; + Flags.cpr1_size = Section.CPR1Size; + Flags.cpr2_size = Section.CPR2Size; + Flags.fp_abi = Section.FpABI; + Flags.isa_ext = Section.ISAExtension; + Flags.ases = Section.ASEs; + Flags.flags1 = Section.Flags1; + Flags.flags2 = Section.Flags2; + OS.write((const char *)&Flags, sizeof(Flags)); +} + +template <class ELFT> +void ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::DynamicSection &Section, + ContiguousBlobAccumulator &CBA) { + typedef typename ELFT::uint uintX_t; + + assert(Section.Type == llvm::ELF::SHT_DYNAMIC && + "Section type is not SHT_DYNAMIC"); + + if (!Section.Entries.empty() && Section.Content) + reportError("cannot specify both raw content and explicit entries " + "for dynamic section '" + + Section.Name + "'"); + + if (Section.Content) + SHeader.sh_size = Section.Content->binary_size(); + else + SHeader.sh_size = 2 * sizeof(uintX_t) * Section.Entries.size(); + if (Section.EntSize) + SHeader.sh_entsize = *Section.EntSize; + else + SHeader.sh_entsize = sizeof(Elf_Dyn); + + raw_ostream &OS = + CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + for (const ELFYAML::DynamicEntry &DE : Section.Entries) { + support::endian::write<uintX_t>(OS, DE.Tag, ELFT::TargetEndianness); + support::endian::write<uintX_t>(OS, DE.Val, ELFT::TargetEndianness); + } + if (Section.Content) + Section.Content->writeAsBinary(OS); +} + +template <class ELFT> +void ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::AddrsigSection &Section, + ContiguousBlobAccumulator &CBA) { + raw_ostream &OS = + CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + + unsigned Link = 0; + if (Section.Link.empty() && SN2I.lookup(".symtab", Link)) + SHeader.sh_link = Link; + + if (Section.Content || Section.Size) { + SHeader.sh_size = writeContent(OS, Section.Content, Section.Size); + return; + } + + for (const ELFYAML::AddrsigSymbol &Sym : *Section.Symbols) { + uint64_t Val = + Sym.Name ? toSymbolIndex(*Sym.Name, Section.Name, /*IsDynamic=*/false) + : (uint32_t)*Sym.Index; + SHeader.sh_size += encodeULEB128(Val, OS); + } +} + +template <class ELFT> void ELFState<ELFT>::buildSectionIndex() { + for (unsigned I = 0, E = Doc.Sections.size(); I != E; ++I) { + StringRef Name = Doc.Sections[I]->Name; + if (Name.empty()) + continue; + + DotShStrtab.add(ELFYAML::dropUniqueSuffix(Name)); + if (!SN2I.addName(Name, I)) + reportError("repeated section name: '" + Name + + "' at YAML section number " + Twine(I)); + } + + DotShStrtab.finalize(); +} + +template <class ELFT> void ELFState<ELFT>::buildSymbolIndexes() { + auto Build = [this](ArrayRef<ELFYAML::Symbol> V, NameToIdxMap &Map) { + for (size_t I = 0, S = V.size(); I < S; ++I) { + const ELFYAML::Symbol &Sym = V[I]; + if (!Sym.Name.empty() && !Map.addName(Sym.Name, I + 1)) + reportError("repeated symbol name: '" + Sym.Name + "'"); + } + }; + + if (Doc.Symbols) + Build(*Doc.Symbols, SymN2I); + Build(Doc.DynamicSymbols, DynSymN2I); +} + +template <class ELFT> void ELFState<ELFT>::finalizeStrings() { + // Add the regular symbol names to .strtab section. + if (Doc.Symbols) + for (const ELFYAML::Symbol &Sym : *Doc.Symbols) + DotStrtab.add(ELFYAML::dropUniqueSuffix(Sym.Name)); + DotStrtab.finalize(); + + // Add the dynamic symbol names to .dynstr section. + for (const ELFYAML::Symbol &Sym : Doc.DynamicSymbols) + DotDynstr.add(ELFYAML::dropUniqueSuffix(Sym.Name)); + + // SHT_GNU_verdef and SHT_GNU_verneed sections might also + // add strings to .dynstr section. + for (const std::unique_ptr<ELFYAML::Section> &Sec : Doc.Sections) { + if (auto VerNeed = dyn_cast<ELFYAML::VerneedSection>(Sec.get())) { + for (const ELFYAML::VerneedEntry &VE : VerNeed->VerneedV) { + DotDynstr.add(VE.File); + for (const ELFYAML::VernauxEntry &Aux : VE.AuxV) + DotDynstr.add(Aux.Name); + } + } else if (auto VerDef = dyn_cast<ELFYAML::VerdefSection>(Sec.get())) { + for (const ELFYAML::VerdefEntry &E : VerDef->Entries) + for (StringRef Name : E.VerNames) + DotDynstr.add(Name); + } + } + + DotDynstr.finalize(); +} + +template <class ELFT> +bool ELFState<ELFT>::writeELF(raw_ostream &OS, ELFYAML::Object &Doc, + yaml::ErrorHandler EH) { + ELFState<ELFT> State(Doc, EH); + + // Finalize .strtab and .dynstr sections. We do that early because want to + // finalize the string table builders before writing the content of the + // sections that might want to use them. + State.finalizeStrings(); + + State.buildSectionIndex(); + State.buildSymbolIndexes(); + + std::vector<Elf_Phdr> PHeaders; + State.initProgramHeaders(PHeaders); + + // XXX: This offset is tightly coupled with the order that we write + // things to `OS`. + const size_t SectionContentBeginOffset = + sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * Doc.ProgramHeaders.size(); + ContiguousBlobAccumulator CBA(SectionContentBeginOffset); + + std::vector<Elf_Shdr> SHeaders; + State.initSectionHeaders(SHeaders, CBA); + + // Now we can decide segment offsets + State.setProgramHeaderLayout(PHeaders, SHeaders); + + if (State.HasError) + return false; + + State.writeELFHeader(CBA, OS); + writeArrayData(OS, makeArrayRef(PHeaders)); + CBA.writeBlobToStream(OS); + writeArrayData(OS, makeArrayRef(SHeaders)); + return true; +} + +namespace llvm { +namespace yaml { + +bool yaml2elf(llvm::ELFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH) { + bool IsLE = Doc.Header.Data == ELFYAML::ELF_ELFDATA(ELF::ELFDATA2LSB); + bool Is64Bit = Doc.Header.Class == ELFYAML::ELF_ELFCLASS(ELF::ELFCLASS64); + if (Is64Bit) { + if (IsLE) + return ELFState<object::ELF64LE>::writeELF(Out, Doc, EH); + return ELFState<object::ELF64BE>::writeELF(Out, Doc, EH); + } + if (IsLE) + return ELFState<object::ELF32LE>::writeELF(Out, Doc, EH); + return ELFState<object::ELF32BE>::writeELF(Out, Doc, EH); +} + +} // namespace yaml +} // namespace llvm diff --git a/lib/ObjectYAML/ELFYAML.cpp b/lib/ObjectYAML/ELFYAML.cpp index 7497154c757d..29585abe6e80 100644 --- a/lib/ObjectYAML/ELFYAML.cpp +++ b/lib/ObjectYAML/ELFYAML.cpp @@ -11,12 +11,14 @@ //===----------------------------------------------------------------------===// #include "llvm/ObjectYAML/ELFYAML.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MipsABIFlags.h" #include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/WithColor.h" #include <cassert> #include <cstdint> @@ -50,6 +52,8 @@ void ScalarEnumerationTraits<ELFYAML::ELF_PT>::enumeration( ECase(PT_PHDR); ECase(PT_TLS); ECase(PT_GNU_EH_FRAME); + ECase(PT_GNU_STACK); + ECase(PT_GNU_RELRO); #undef ECase IO.enumFallback<Hex32>(Value); } @@ -217,6 +221,7 @@ void ScalarEnumerationTraits<ELFYAML::ELF_EM>::enumeration( ECase(EM_LANAI); ECase(EM_BPF); #undef ECase + IO.enumFallback<Hex16>(Value); } void ScalarEnumerationTraits<ELFYAML::ELF_ELFCLASS>::enumeration( @@ -459,6 +464,9 @@ void ScalarEnumerationTraits<ELFYAML::ELF_SHT>::enumeration( ECase(SHT_LLVM_CALL_GRAPH_PROFILE); ECase(SHT_LLVM_ADDRSIG); ECase(SHT_LLVM_DEPENDENT_LIBRARIES); + ECase(SHT_LLVM_SYMPART); + ECase(SHT_LLVM_PART_EHDR); + ECase(SHT_LLVM_PART_PHDR); ECase(SHT_GNU_ATTRIBUTES); ECase(SHT_GNU_HASH); ECase(SHT_GNU_verdef); @@ -563,7 +571,7 @@ void ScalarEnumerationTraits<ELFYAML::ELF_SHN>::enumeration( ECase(SHN_HEXAGON_SCOMMON_4); ECase(SHN_HEXAGON_SCOMMON_8); #undef ECase - IO.enumFallback<Hex32>(Value); + IO.enumFallback<Hex16>(Value); } void ScalarEnumerationTraits<ELFYAML::ELF_STB>::enumeration( @@ -592,34 +600,6 @@ void ScalarEnumerationTraits<ELFYAML::ELF_STT>::enumeration( IO.enumFallback<Hex8>(Value); } -void ScalarEnumerationTraits<ELFYAML::ELF_STV>::enumeration( - IO &IO, ELFYAML::ELF_STV &Value) { -#define ECase(X) IO.enumCase(Value, #X, ELF::X) - ECase(STV_DEFAULT); - ECase(STV_INTERNAL); - ECase(STV_HIDDEN); - ECase(STV_PROTECTED); -#undef ECase -} - -void ScalarBitSetTraits<ELFYAML::ELF_STO>::bitset(IO &IO, - ELFYAML::ELF_STO &Value) { - const auto *Object = static_cast<ELFYAML::Object *>(IO.getContext()); - assert(Object && "The IO context is not initialized"); -#define BCase(X) IO.bitSetCase(Value, #X, ELF::X) - switch (Object->Header.Machine) { - case ELF::EM_MIPS: - BCase(STO_MIPS_OPTIONAL); - BCase(STO_MIPS_PLT); - BCase(STO_MIPS_PIC); - BCase(STO_MIPS_MICROMIPS); - break; - default: - break; // Nothing to do - } -#undef BCase -#undef BCaseMask -} void ScalarEnumerationTraits<ELFYAML::ELF_RSS>::enumeration( IO &IO, ELFYAML::ELF_RSS &Value) { @@ -671,8 +651,12 @@ void ScalarEnumerationTraits<ELFYAML::ELF_REL>::enumeration( case ELF::EM_BPF: #include "llvm/BinaryFormat/ELFRelocs/BPF.def" break; + case ELF::EM_PPC64: +#include "llvm/BinaryFormat/ELFRelocs/PowerPC64.def" + break; default: - llvm_unreachable("Unsupported architecture"); + // Nothing to do. + break; } #undef ELF_RELOC IO.enumFallback<Hex32>(Value); @@ -845,7 +829,7 @@ void MappingTraits<ELFYAML::FileHeader>::mapping(IO &IO, IO.mapOptional("Entry", FileHdr.Entry, Hex64(0)); IO.mapOptional("SHEntSize", FileHdr.SHEntSize); - IO.mapOptional("SHOffset", FileHdr.SHOffset); + IO.mapOptional("SHOff", FileHdr.SHOff); IO.mapOptional("SHNum", FileHdr.SHNum); IO.mapOptional("SHStrNdx", FileHdr.SHStrNdx); } @@ -863,18 +847,111 @@ void MappingTraits<ELFYAML::ProgramHeader>::mapping( IO.mapOptional("Offset", Phdr.Offset); } +LLVM_YAML_STRONG_TYPEDEF(StringRef, StOtherPiece) + +template <> struct ScalarTraits<StOtherPiece> { + static void output(const StOtherPiece &Val, void *, raw_ostream &Out) { + Out << Val; + } + static StringRef input(StringRef Scalar, void *, StOtherPiece &Val) { + Val = Scalar; + return {}; + } + static QuotingType mustQuote(StringRef) { return QuotingType::None; } +}; +template <> struct SequenceElementTraits<StOtherPiece> { + static const bool flow = true; +}; + namespace { struct NormalizedOther { - NormalizedOther(IO &) - : Visibility(ELFYAML::ELF_STV(0)), Other(ELFYAML::ELF_STO(0)) {} - NormalizedOther(IO &, uint8_t Original) - : Visibility(Original & 0x3), Other(Original & ~0x3) {} + NormalizedOther(IO &IO) : YamlIO(IO) {} + NormalizedOther(IO &IO, Optional<uint8_t> Original) : YamlIO(IO) { + assert(Original && "This constructor is only used for outputting YAML and " + "assumes a non-empty Original"); + std::vector<StOtherPiece> Ret; + const auto *Object = static_cast<ELFYAML::Object *>(YamlIO.getContext()); + for (std::pair<StringRef, uint8_t> &P : + getFlags(Object->Header.Machine).takeVector()) { + uint8_t FlagValue = P.second; + if ((*Original & FlagValue) != FlagValue) + continue; + *Original &= ~FlagValue; + Ret.push_back({P.first}); + } + + if (*Original != 0) { + UnknownFlagsHolder = std::to_string(*Original); + Ret.push_back({UnknownFlagsHolder}); + } + + if (!Ret.empty()) + Other = std::move(Ret); + } + + uint8_t toValue(StringRef Name) { + const auto *Object = static_cast<ELFYAML::Object *>(YamlIO.getContext()); + MapVector<StringRef, uint8_t> Flags = getFlags(Object->Header.Machine); - uint8_t denormalize(IO &) { return Visibility | Other; } + auto It = Flags.find(Name); + if (It != Flags.end()) + return It->second; + + uint8_t Val; + if (to_integer(Name, Val)) + return Val; + + YamlIO.setError("an unknown value is used for symbol's 'Other' field: " + + Name); + return 0; + } - ELFYAML::ELF_STV Visibility; - ELFYAML::ELF_STO Other; + Optional<uint8_t> denormalize(IO &) { + if (!Other) + return None; + uint8_t Ret = 0; + for (StOtherPiece &Val : *Other) + Ret |= toValue(Val); + return Ret; + } + + // st_other field is used to encode symbol visibility and platform-dependent + // flags and values. This method returns a name to value map that is used for + // parsing and encoding this field. + MapVector<StringRef, uint8_t> getFlags(unsigned EMachine) { + MapVector<StringRef, uint8_t> Map; + // STV_* values are just enumeration values. We add them in a reversed order + // because when we convert the st_other to named constants when printing + // YAML we want to use a maximum number of bits on each step: + // when we have st_other == 3, we want to print it as STV_PROTECTED (3), but + // not as STV_HIDDEN (2) + STV_INTERNAL (1). + Map["STV_PROTECTED"] = ELF::STV_PROTECTED; + Map["STV_HIDDEN"] = ELF::STV_HIDDEN; + Map["STV_INTERNAL"] = ELF::STV_INTERNAL; + // STV_DEFAULT is used to represent the default visibility and has a value + // 0. We want to be able to read it from YAML documents, but there is no + // reason to print it. + if (!YamlIO.outputting()) + Map["STV_DEFAULT"] = ELF::STV_DEFAULT; + + // MIPS is not consistent. All of the STO_MIPS_* values are bit flags, + // except STO_MIPS_MIPS16 which overlaps them. It should be checked and + // consumed first when we print the output, because we do not want to print + // any other flags that have the same bits instead. + if (EMachine == ELF::EM_MIPS) { + Map["STO_MIPS_MIPS16"] = ELF::STO_MIPS_MIPS16; + Map["STO_MIPS_MICROMIPS"] = ELF::STO_MIPS_MICROMIPS; + Map["STO_MIPS_PIC"] = ELF::STO_MIPS_PIC; + Map["STO_MIPS_PLT"] = ELF::STO_MIPS_PLT; + Map["STO_MIPS_OPTIONAL"] = ELF::STO_MIPS_OPTIONAL; + } + return Map; + } + + IO &YamlIO; + Optional<std::vector<StOtherPiece>> Other; + std::string UnknownFlagsHolder; }; } // end anonymous namespace @@ -888,17 +965,21 @@ void MappingTraits<ELFYAML::Symbol>::mapping(IO &IO, ELFYAML::Symbol &Symbol) { IO.mapOptional("Binding", Symbol.Binding, ELFYAML::ELF_STB(0)); IO.mapOptional("Value", Symbol.Value, Hex64(0)); IO.mapOptional("Size", Symbol.Size, Hex64(0)); - MappingNormalization<NormalizedOther, uint8_t> Keys(IO, Symbol.Other); - IO.mapOptional("Visibility", Keys->Visibility, ELFYAML::ELF_STV(0)); - IO.mapOptional("Other", Keys->Other, ELFYAML::ELF_STO(0)); + + // Symbol's Other field is a bit special. It is usually a field that + // represents st_other and holds the symbol visibility. However, on some + // platforms, it can contain bit fields and regular values, or even sometimes a + // crazy mix of them (see comments for NormalizedOther). Because of this, we + // need special handling. + MappingNormalization<NormalizedOther, Optional<uint8_t>> Keys(IO, + Symbol.Other); + IO.mapOptional("Other", Keys->Other); } StringRef MappingTraits<ELFYAML::Symbol>::validate(IO &IO, ELFYAML::Symbol &Symbol) { if (Symbol.Index && Symbol.Section.data()) return "Index and Section cannot both be specified for Symbol"; - if (Symbol.Index && *Symbol.Index == ELFYAML::ELF_SHN(ELF::SHN_XINDEX)) - return "Large indexes are not supported"; if (Symbol.NameIndex && !Symbol.Name.empty()) return "Name and NameIndex cannot both be specified for Symbol"; return StringRef(); @@ -914,10 +995,11 @@ static void commonSectionMapping(IO &IO, ELFYAML::Section &Section) { IO.mapOptional("EntSize", Section.EntSize); // obj2yaml does not dump these fields. They are expected to be empty when we - // are producing YAML, because yaml2obj sets appropriate values for sh_offset - // and sh_size automatically when they are not explicitly defined. + // are producing YAML, because yaml2obj sets appropriate values for them + // automatically when they are not explicitly defined. assert(!IO.outputting() || (!Section.ShOffset.hasValue() && !Section.ShSize.hasValue())); + IO.mapOptional("ShName", Section.ShName); IO.mapOptional("ShOffset", Section.ShOffset); IO.mapOptional("ShSize", Section.ShSize); } @@ -935,6 +1017,21 @@ static void sectionMapping(IO &IO, ELFYAML::RawContentSection &Section) { IO.mapOptional("Info", Section.Info); } +static void sectionMapping(IO &IO, ELFYAML::StackSizesSection &Section) { + commonSectionMapping(IO, Section); + IO.mapOptional("Content", Section.Content); + IO.mapOptional("Size", Section.Size); + IO.mapOptional("Entries", Section.Entries); +} + +static void sectionMapping(IO &IO, ELFYAML::HashSection &Section) { + commonSectionMapping(IO, Section); + IO.mapOptional("Content", Section.Content); + IO.mapOptional("Bucket", Section.Bucket); + IO.mapOptional("Chain", Section.Chain); + IO.mapOptional("Size", Section.Size); +} + static void sectionMapping(IO &IO, ELFYAML::NoBitsSection &Section) { commonSectionMapping(IO, Section); IO.mapOptional("Size", Section.Size, Hex64(0)); @@ -969,6 +1066,18 @@ static void groupSectionMapping(IO &IO, ELFYAML::Group &Group) { IO.mapRequired("Members", Group.Members); } +static void sectionMapping(IO &IO, ELFYAML::SymtabShndxSection &Section) { + commonSectionMapping(IO, Section); + IO.mapRequired("Entries", Section.Entries); +} + +static void sectionMapping(IO &IO, ELFYAML::AddrsigSection &Section) { + commonSectionMapping(IO, Section); + IO.mapOptional("Content", Section.Content); + IO.mapOptional("Size", Section.Size); + IO.mapOptional("Symbols", Section.Symbols); +} + void MappingTraits<ELFYAML::SectionOrType>::mapping( IO &IO, ELFYAML::SectionOrType §ionOrType) { IO.mapRequired("SectionOrType", sectionOrType.sectionNameOrType); @@ -1029,6 +1138,11 @@ void MappingTraits<std::unique_ptr<ELFYAML::Section>>::mapping( Section.reset(new ELFYAML::NoBitsSection()); sectionMapping(IO, *cast<ELFYAML::NoBitsSection>(Section.get())); break; + case ELF::SHT_HASH: + if (!IO.outputting()) + Section.reset(new ELFYAML::HashSection()); + sectionMapping(IO, *cast<ELFYAML::HashSection>(Section.get())); + break; case ELF::SHT_MIPS_ABIFLAGS: if (!IO.outputting()) Section.reset(new ELFYAML::MipsABIFlags()); @@ -1049,21 +1163,113 @@ void MappingTraits<std::unique_ptr<ELFYAML::Section>>::mapping( Section.reset(new ELFYAML::VerneedSection()); sectionMapping(IO, *cast<ELFYAML::VerneedSection>(Section.get())); break; - default: + case ELF::SHT_SYMTAB_SHNDX: if (!IO.outputting()) - Section.reset(new ELFYAML::RawContentSection()); - sectionMapping(IO, *cast<ELFYAML::RawContentSection>(Section.get())); + Section.reset(new ELFYAML::SymtabShndxSection()); + sectionMapping(IO, *cast<ELFYAML::SymtabShndxSection>(Section.get())); + break; + case ELF::SHT_LLVM_ADDRSIG: + if (!IO.outputting()) + Section.reset(new ELFYAML::AddrsigSection()); + sectionMapping(IO, *cast<ELFYAML::AddrsigSection>(Section.get())); + break; + default: + if (!IO.outputting()) { + StringRef Name; + IO.mapOptional("Name", Name, StringRef()); + Name = ELFYAML::dropUniqueSuffix(Name); + + if (ELFYAML::StackSizesSection::nameMatches(Name)) + Section = std::make_unique<ELFYAML::StackSizesSection>(); + else + Section = std::make_unique<ELFYAML::RawContentSection>(); + } + + if (auto S = dyn_cast<ELFYAML::RawContentSection>(Section.get())) + sectionMapping(IO, *S); + else + sectionMapping(IO, *cast<ELFYAML::StackSizesSection>(Section.get())); } } StringRef MappingTraits<std::unique_ptr<ELFYAML::Section>>::validate( IO &io, std::unique_ptr<ELFYAML::Section> &Section) { - const auto *RawSection = dyn_cast<ELFYAML::RawContentSection>(Section.get()); - if (!RawSection) + if (const auto *RawSection = + dyn_cast<ELFYAML::RawContentSection>(Section.get())) { + if (RawSection->Size && RawSection->Content && + (uint64_t)(*RawSection->Size) < RawSection->Content->binary_size()) + return "Section size must be greater than or equal to the content size"; return {}; - if (RawSection->Size && RawSection->Content && - (uint64_t)(*RawSection->Size) < RawSection->Content->binary_size()) - return "Section size must be greater than or equal to the content size"; + } + + if (const auto *SS = dyn_cast<ELFYAML::StackSizesSection>(Section.get())) { + if (!SS->Entries && !SS->Content && !SS->Size) + return ".stack_sizes: one of Content, Entries and Size must be specified"; + + if (SS->Size && SS->Content && + (uint64_t)(*SS->Size) < SS->Content->binary_size()) + return ".stack_sizes: Size must be greater than or equal to the content " + "size"; + + // We accept Content, Size or both together when there are no Entries. + if (!SS->Entries) + return {}; + + if (SS->Size) + return ".stack_sizes: Size and Entries cannot be used together"; + if (SS->Content) + return ".stack_sizes: Content and Entries cannot be used together"; + return {}; + } + + if (const auto *HS = dyn_cast<ELFYAML::HashSection>(Section.get())) { + if (!HS->Content && !HS->Bucket && !HS->Chain && !HS->Size) + return "one of \"Content\", \"Size\", \"Bucket\" or \"Chain\" must be " + "specified"; + + if (HS->Content || HS->Size) { + if (HS->Size && HS->Content && + (uint64_t)*HS->Size < HS->Content->binary_size()) + return "\"Size\" must be greater than or equal to the content " + "size"; + + if (HS->Bucket) + return "\"Bucket\" cannot be used with \"Content\" or \"Size\""; + if (HS->Chain) + return "\"Chain\" cannot be used with \"Content\" or \"Size\""; + return {}; + } + + if ((HS->Bucket && !HS->Chain) || (!HS->Bucket && HS->Chain)) + return "\"Bucket\" and \"Chain\" must be used together"; + return {}; + } + + if (const auto *Sec = dyn_cast<ELFYAML::AddrsigSection>(Section.get())) { + if (!Sec->Symbols && !Sec->Content && !Sec->Size) + return "one of \"Content\", \"Size\" or \"Symbols\" must be specified"; + + if (Sec->Content || Sec->Size) { + if (Sec->Size && Sec->Content && + (uint64_t)*Sec->Size < Sec->Content->binary_size()) + return "\"Size\" must be greater than or equal to the content " + "size"; + + if (Sec->Symbols) + return "\"Symbols\" cannot be used with \"Content\" or \"Size\""; + return {}; + } + + if (!Sec->Symbols) + return {}; + + for (const ELFYAML::AddrsigSymbol &AS : *Sec->Symbols) + if (AS.Index && AS.Name) + return "\"Index\" and \"Name\" cannot be used together when defining a " + "symbol"; + return {}; + } + return {}; } @@ -1092,6 +1298,13 @@ struct NormalizedMips64RelType { } // end anonymous namespace +void MappingTraits<ELFYAML::StackSizeEntry>::mapping( + IO &IO, ELFYAML::StackSizeEntry &E) { + assert(IO.getContext() && "The IO context is not initialized"); + IO.mapOptional("Address", E.Address, Hex64(0)); + IO.mapRequired("Size", E.Size); +} + void MappingTraits<ELFYAML::DynamicEntry>::mapping(IO &IO, ELFYAML::DynamicEntry &Rel) { assert(IO.getContext() && "The IO context is not initialized"); @@ -1164,6 +1377,12 @@ void MappingTraits<ELFYAML::Object>::mapping(IO &IO, ELFYAML::Object &Object) { IO.setContext(nullptr); } +void MappingTraits<ELFYAML::AddrsigSymbol>::mapping(IO &IO, ELFYAML::AddrsigSymbol &Sym) { + assert(IO.getContext() && "The IO context is not initialized"); + IO.mapOptional("Name", Sym.Name); + IO.mapOptional("Index", Sym.Index); +} + LLVM_YAML_STRONG_TYPEDEF(uint8_t, MIPS_AFL_REG) LLVM_YAML_STRONG_TYPEDEF(uint8_t, MIPS_ABI_FP) LLVM_YAML_STRONG_TYPEDEF(uint32_t, MIPS_AFL_EXT) diff --git a/lib/ObjectYAML/MachOEmitter.cpp b/lib/ObjectYAML/MachOEmitter.cpp new file mode 100644 index 000000000000..b56f811ce67d --- /dev/null +++ b/lib/ObjectYAML/MachOEmitter.cpp @@ -0,0 +1,580 @@ +//===- yaml2macho - Convert YAML to a Mach 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 Mach component of yaml2obj. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/BinaryFormat/MachO.h" +#include "llvm/ObjectYAML/DWARFEmitter.h" +#include "llvm/ObjectYAML/ObjectYAML.h" +#include "llvm/ObjectYAML/yaml2obj.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" + +#include "llvm/Support/Format.h" + +using namespace llvm; + +namespace { + +class MachOWriter { +public: + MachOWriter(MachOYAML::Object &Obj) : Obj(Obj), is64Bit(true), fileStart(0) { + is64Bit = Obj.Header.magic == MachO::MH_MAGIC_64 || + Obj.Header.magic == MachO::MH_CIGAM_64; + memset(reinterpret_cast<void *>(&Header), 0, sizeof(MachO::mach_header_64)); + } + + void writeMachO(raw_ostream &OS); + +private: + void writeHeader(raw_ostream &OS); + void writeLoadCommands(raw_ostream &OS); + void writeSectionData(raw_ostream &OS); + void writeLinkEditData(raw_ostream &OS); + + void writeBindOpcodes(raw_ostream &OS, + std::vector<MachOYAML::BindOpcode> &BindOpcodes); + // LinkEdit writers + void writeRebaseOpcodes(raw_ostream &OS); + void writeBasicBindOpcodes(raw_ostream &OS); + void writeWeakBindOpcodes(raw_ostream &OS); + void writeLazyBindOpcodes(raw_ostream &OS); + void writeNameList(raw_ostream &OS); + void writeStringTable(raw_ostream &OS); + void writeExportTrie(raw_ostream &OS); + + void dumpExportEntry(raw_ostream &OS, MachOYAML::ExportEntry &Entry); + void ZeroToOffset(raw_ostream &OS, size_t offset); + + MachOYAML::Object &Obj; + bool is64Bit; + uint64_t fileStart; + + MachO::mach_header_64 Header; +}; + +void MachOWriter::writeMachO(raw_ostream &OS) { + fileStart = OS.tell(); + writeHeader(OS); + writeLoadCommands(OS); + writeSectionData(OS); +} + +void MachOWriter::writeHeader(raw_ostream &OS) { + Header.magic = Obj.Header.magic; + Header.cputype = Obj.Header.cputype; + Header.cpusubtype = Obj.Header.cpusubtype; + Header.filetype = Obj.Header.filetype; + Header.ncmds = Obj.Header.ncmds; + Header.sizeofcmds = Obj.Header.sizeofcmds; + Header.flags = Obj.Header.flags; + Header.reserved = Obj.Header.reserved; + + if (Obj.IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(Header); + + auto header_size = + is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); + OS.write((const char *)&Header, header_size); +} + +template <typename SectionType> +SectionType constructSection(MachOYAML::Section Sec) { + SectionType TempSec; + memcpy(reinterpret_cast<void *>(&TempSec.sectname[0]), &Sec.sectname[0], 16); + memcpy(reinterpret_cast<void *>(&TempSec.segname[0]), &Sec.segname[0], 16); + TempSec.addr = Sec.addr; + TempSec.size = Sec.size; + TempSec.offset = Sec.offset; + TempSec.align = Sec.align; + TempSec.reloff = Sec.reloff; + TempSec.nreloc = Sec.nreloc; + TempSec.flags = Sec.flags; + TempSec.reserved1 = Sec.reserved1; + TempSec.reserved2 = Sec.reserved2; + return TempSec; +} + +template <typename StructType> +size_t writeLoadCommandData(MachOYAML::LoadCommand &LC, raw_ostream &OS, + bool IsLittleEndian) { + return 0; +} + +template <> +size_t writeLoadCommandData<MachO::segment_command>(MachOYAML::LoadCommand &LC, + raw_ostream &OS, + bool IsLittleEndian) { + size_t BytesWritten = 0; + for (const auto &Sec : LC.Sections) { + auto TempSec = constructSection<MachO::section>(Sec); + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(TempSec); + OS.write(reinterpret_cast<const char *>(&(TempSec)), + sizeof(MachO::section)); + BytesWritten += sizeof(MachO::section); + } + return BytesWritten; +} + +template <> +size_t writeLoadCommandData<MachO::segment_command_64>( + MachOYAML::LoadCommand &LC, raw_ostream &OS, bool IsLittleEndian) { + size_t BytesWritten = 0; + for (const auto &Sec : LC.Sections) { + auto TempSec = constructSection<MachO::section_64>(Sec); + TempSec.reserved3 = Sec.reserved3; + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(TempSec); + OS.write(reinterpret_cast<const char *>(&(TempSec)), + sizeof(MachO::section_64)); + BytesWritten += sizeof(MachO::section_64); + } + return BytesWritten; +} + +size_t writePayloadString(MachOYAML::LoadCommand &LC, raw_ostream &OS) { + size_t BytesWritten = 0; + if (!LC.PayloadString.empty()) { + OS.write(LC.PayloadString.c_str(), LC.PayloadString.length()); + BytesWritten = LC.PayloadString.length(); + } + return BytesWritten; +} + +template <> +size_t writeLoadCommandData<MachO::dylib_command>(MachOYAML::LoadCommand &LC, + raw_ostream &OS, + bool IsLittleEndian) { + return writePayloadString(LC, OS); +} + +template <> +size_t writeLoadCommandData<MachO::dylinker_command>(MachOYAML::LoadCommand &LC, + raw_ostream &OS, + bool IsLittleEndian) { + return writePayloadString(LC, OS); +} + +template <> +size_t writeLoadCommandData<MachO::rpath_command>(MachOYAML::LoadCommand &LC, + raw_ostream &OS, + bool IsLittleEndian) { + return writePayloadString(LC, OS); +} + +template <> +size_t writeLoadCommandData<MachO::build_version_command>( + MachOYAML::LoadCommand &LC, raw_ostream &OS, bool IsLittleEndian) { + size_t BytesWritten = 0; + for (const auto &T : LC.Tools) { + struct MachO::build_tool_version tool = T; + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(tool); + OS.write(reinterpret_cast<const char *>(&tool), + sizeof(MachO::build_tool_version)); + BytesWritten += sizeof(MachO::build_tool_version); + } + return BytesWritten; +} + +void ZeroFillBytes(raw_ostream &OS, size_t Size) { + std::vector<uint8_t> FillData; + FillData.insert(FillData.begin(), Size, 0); + OS.write(reinterpret_cast<char *>(FillData.data()), Size); +} + +void Fill(raw_ostream &OS, size_t Size, uint32_t Data) { + std::vector<uint32_t> FillData; + FillData.insert(FillData.begin(), (Size / 4) + 1, Data); + OS.write(reinterpret_cast<char *>(FillData.data()), Size); +} + +void MachOWriter::ZeroToOffset(raw_ostream &OS, size_t Offset) { + auto currOffset = OS.tell() - fileStart; + if (currOffset < Offset) + ZeroFillBytes(OS, Offset - currOffset); +} + +void MachOWriter::writeLoadCommands(raw_ostream &OS) { + for (auto &LC : Obj.LoadCommands) { + size_t BytesWritten = 0; + llvm::MachO::macho_load_command Data = LC.Data; + +#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ + case MachO::LCName: \ + if (Obj.IsLittleEndian != sys::IsLittleEndianHost) \ + MachO::swapStruct(Data.LCStruct##_data); \ + OS.write(reinterpret_cast<const char *>(&(Data.LCStruct##_data)), \ + sizeof(MachO::LCStruct)); \ + BytesWritten = sizeof(MachO::LCStruct); \ + BytesWritten += \ + writeLoadCommandData<MachO::LCStruct>(LC, OS, Obj.IsLittleEndian); \ + break; + + switch (LC.Data.load_command_data.cmd) { + default: + if (Obj.IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(Data.load_command_data); + OS.write(reinterpret_cast<const char *>(&(Data.load_command_data)), + sizeof(MachO::load_command)); + BytesWritten = sizeof(MachO::load_command); + BytesWritten += + writeLoadCommandData<MachO::load_command>(LC, OS, Obj.IsLittleEndian); + break; +#include "llvm/BinaryFormat/MachO.def" + } + + if (LC.PayloadBytes.size() > 0) { + OS.write(reinterpret_cast<const char *>(LC.PayloadBytes.data()), + LC.PayloadBytes.size()); + BytesWritten += LC.PayloadBytes.size(); + } + + if (LC.ZeroPadBytes > 0) { + ZeroFillBytes(OS, LC.ZeroPadBytes); + BytesWritten += LC.ZeroPadBytes; + } + + // Fill remaining bytes with 0. This will only get hit in partially + // specified test cases. + auto BytesRemaining = LC.Data.load_command_data.cmdsize - BytesWritten; + if (BytesRemaining > 0) { + ZeroFillBytes(OS, BytesRemaining); + } + } +} + +void MachOWriter::writeSectionData(raw_ostream &OS) { + bool FoundLinkEditSeg = false; + for (auto &LC : Obj.LoadCommands) { + switch (LC.Data.load_command_data.cmd) { + case MachO::LC_SEGMENT: + case MachO::LC_SEGMENT_64: + uint64_t segOff = is64Bit ? LC.Data.segment_command_64_data.fileoff + : LC.Data.segment_command_data.fileoff; + if (0 == + strncmp(&LC.Data.segment_command_data.segname[0], "__LINKEDIT", 16)) { + FoundLinkEditSeg = true; + writeLinkEditData(OS); + } + for (auto &Sec : LC.Sections) { + ZeroToOffset(OS, Sec.offset); + // Zero Fill any data between the end of the last thing we wrote and the + // start of this section. + assert((OS.tell() - fileStart <= Sec.offset || + Sec.offset == (uint32_t)0) && + "Wrote too much data somewhere, section offsets don't line up."); + if (0 == strncmp(&Sec.segname[0], "__DWARF", 16)) { + if (0 == strncmp(&Sec.sectname[0], "__debug_str", 16)) { + DWARFYAML::EmitDebugStr(OS, Obj.DWARF); + } else if (0 == strncmp(&Sec.sectname[0], "__debug_abbrev", 16)) { + DWARFYAML::EmitDebugAbbrev(OS, Obj.DWARF); + } else if (0 == strncmp(&Sec.sectname[0], "__debug_aranges", 16)) { + DWARFYAML::EmitDebugAranges(OS, Obj.DWARF); + } else if (0 == strncmp(&Sec.sectname[0], "__debug_pubnames", 16)) { + DWARFYAML::EmitPubSection(OS, Obj.DWARF.PubNames, + Obj.IsLittleEndian); + } else if (0 == strncmp(&Sec.sectname[0], "__debug_pubtypes", 16)) { + DWARFYAML::EmitPubSection(OS, Obj.DWARF.PubTypes, + Obj.IsLittleEndian); + } else if (0 == strncmp(&Sec.sectname[0], "__debug_info", 16)) { + DWARFYAML::EmitDebugInfo(OS, Obj.DWARF); + } else if (0 == strncmp(&Sec.sectname[0], "__debug_line", 16)) { + DWARFYAML::EmitDebugLine(OS, Obj.DWARF); + } + + continue; + } + + // Skip if it's a virtual section. + if (MachO::isVirtualSection(Sec.flags & MachO::SECTION_TYPE)) + continue; + + if (Sec.content) { + yaml::BinaryRef Content = *Sec.content; + Content.writeAsBinary(OS); + ZeroFillBytes(OS, Sec.size - Content.binary_size()); + } else { + // Fill section data with 0xDEADBEEF. + Fill(OS, Sec.size, 0xDEADBEEFu); + } + } + uint64_t segSize = is64Bit ? LC.Data.segment_command_64_data.filesize + : LC.Data.segment_command_data.filesize; + ZeroToOffset(OS, segOff + segSize); + break; + } + } + // Old PPC Object Files didn't have __LINKEDIT segments, the data was just + // stuck at the end of the file. + if (!FoundLinkEditSeg) + writeLinkEditData(OS); +} + +void MachOWriter::writeBindOpcodes( + raw_ostream &OS, std::vector<MachOYAML::BindOpcode> &BindOpcodes) { + + for (auto Opcode : BindOpcodes) { + uint8_t OpByte = Opcode.Opcode | Opcode.Imm; + OS.write(reinterpret_cast<char *>(&OpByte), 1); + for (auto Data : Opcode.ULEBExtraData) { + encodeULEB128(Data, OS); + } + for (auto Data : Opcode.SLEBExtraData) { + encodeSLEB128(Data, OS); + } + if (!Opcode.Symbol.empty()) { + OS.write(Opcode.Symbol.data(), Opcode.Symbol.size()); + OS.write('\0'); + } + } +} + +void MachOWriter::dumpExportEntry(raw_ostream &OS, + MachOYAML::ExportEntry &Entry) { + encodeSLEB128(Entry.TerminalSize, OS); + if (Entry.TerminalSize > 0) { + encodeSLEB128(Entry.Flags, OS); + if (Entry.Flags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT) { + encodeSLEB128(Entry.Other, OS); + OS << Entry.ImportName; + OS.write('\0'); + } else { + encodeSLEB128(Entry.Address, OS); + if (Entry.Flags & MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) + encodeSLEB128(Entry.Other, OS); + } + } + OS.write(static_cast<uint8_t>(Entry.Children.size())); + for (auto EE : Entry.Children) { + OS << EE.Name; + OS.write('\0'); + encodeSLEB128(EE.NodeOffset, OS); + } + for (auto EE : Entry.Children) + dumpExportEntry(OS, EE); +} + +void MachOWriter::writeExportTrie(raw_ostream &OS) { + dumpExportEntry(OS, Obj.LinkEdit.ExportTrie); +} + +template <typename NListType> +void writeNListEntry(MachOYAML::NListEntry &NLE, raw_ostream &OS, + bool IsLittleEndian) { + NListType ListEntry; + ListEntry.n_strx = NLE.n_strx; + ListEntry.n_type = NLE.n_type; + ListEntry.n_sect = NLE.n_sect; + ListEntry.n_desc = NLE.n_desc; + ListEntry.n_value = NLE.n_value; + + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(ListEntry); + OS.write(reinterpret_cast<const char *>(&ListEntry), sizeof(NListType)); +} + +void MachOWriter::writeLinkEditData(raw_ostream &OS) { + typedef void (MachOWriter::*writeHandler)(raw_ostream &); + typedef std::pair<uint64_t, writeHandler> writeOperation; + std::vector<writeOperation> WriteQueue; + + MachO::dyld_info_command *DyldInfoOnlyCmd = 0; + MachO::symtab_command *SymtabCmd = 0; + for (auto &LC : Obj.LoadCommands) { + switch (LC.Data.load_command_data.cmd) { + case MachO::LC_SYMTAB: + SymtabCmd = &LC.Data.symtab_command_data; + WriteQueue.push_back( + std::make_pair(SymtabCmd->symoff, &MachOWriter::writeNameList)); + WriteQueue.push_back( + std::make_pair(SymtabCmd->stroff, &MachOWriter::writeStringTable)); + break; + case MachO::LC_DYLD_INFO_ONLY: + DyldInfoOnlyCmd = &LC.Data.dyld_info_command_data; + WriteQueue.push_back(std::make_pair(DyldInfoOnlyCmd->rebase_off, + &MachOWriter::writeRebaseOpcodes)); + WriteQueue.push_back(std::make_pair(DyldInfoOnlyCmd->bind_off, + &MachOWriter::writeBasicBindOpcodes)); + WriteQueue.push_back(std::make_pair(DyldInfoOnlyCmd->weak_bind_off, + &MachOWriter::writeWeakBindOpcodes)); + WriteQueue.push_back(std::make_pair(DyldInfoOnlyCmd->lazy_bind_off, + &MachOWriter::writeLazyBindOpcodes)); + WriteQueue.push_back(std::make_pair(DyldInfoOnlyCmd->export_off, + &MachOWriter::writeExportTrie)); + break; + } + } + + llvm::sort(WriteQueue, [](const writeOperation &a, const writeOperation &b) { + return a.first < b.first; + }); + + for (auto writeOp : WriteQueue) { + ZeroToOffset(OS, writeOp.first); + (this->*writeOp.second)(OS); + } +} + +void MachOWriter::writeRebaseOpcodes(raw_ostream &OS) { + MachOYAML::LinkEditData &LinkEdit = Obj.LinkEdit; + + for (auto Opcode : LinkEdit.RebaseOpcodes) { + uint8_t OpByte = Opcode.Opcode | Opcode.Imm; + OS.write(reinterpret_cast<char *>(&OpByte), 1); + for (auto Data : Opcode.ExtraData) + encodeULEB128(Data, OS); + } +} + +void MachOWriter::writeBasicBindOpcodes(raw_ostream &OS) { + writeBindOpcodes(OS, Obj.LinkEdit.BindOpcodes); +} + +void MachOWriter::writeWeakBindOpcodes(raw_ostream &OS) { + writeBindOpcodes(OS, Obj.LinkEdit.WeakBindOpcodes); +} + +void MachOWriter::writeLazyBindOpcodes(raw_ostream &OS) { + writeBindOpcodes(OS, Obj.LinkEdit.LazyBindOpcodes); +} + +void MachOWriter::writeNameList(raw_ostream &OS) { + for (auto NLE : Obj.LinkEdit.NameList) { + if (is64Bit) + writeNListEntry<MachO::nlist_64>(NLE, OS, Obj.IsLittleEndian); + else + writeNListEntry<MachO::nlist>(NLE, OS, Obj.IsLittleEndian); + } +} + +void MachOWriter::writeStringTable(raw_ostream &OS) { + for (auto Str : Obj.LinkEdit.StringTable) { + OS.write(Str.data(), Str.size()); + OS.write('\0'); + } +} + +class UniversalWriter { +public: + UniversalWriter(yaml::YamlObjectFile &ObjectFile) + : ObjectFile(ObjectFile), fileStart(0) {} + + void writeMachO(raw_ostream &OS); + +private: + void writeFatHeader(raw_ostream &OS); + void writeFatArchs(raw_ostream &OS); + + void ZeroToOffset(raw_ostream &OS, size_t offset); + + yaml::YamlObjectFile &ObjectFile; + uint64_t fileStart; +}; + +void UniversalWriter::writeMachO(raw_ostream &OS) { + fileStart = OS.tell(); + if (ObjectFile.MachO) { + MachOWriter Writer(*ObjectFile.MachO); + Writer.writeMachO(OS); + return; + } + + writeFatHeader(OS); + writeFatArchs(OS); + + auto &FatFile = *ObjectFile.FatMachO; + assert(FatFile.FatArchs.size() == FatFile.Slices.size()); + for (size_t i = 0; i < FatFile.Slices.size(); i++) { + ZeroToOffset(OS, FatFile.FatArchs[i].offset); + MachOWriter Writer(FatFile.Slices[i]); + Writer.writeMachO(OS); + + auto SliceEnd = FatFile.FatArchs[i].offset + FatFile.FatArchs[i].size; + ZeroToOffset(OS, SliceEnd); + } +} + +void UniversalWriter::writeFatHeader(raw_ostream &OS) { + auto &FatFile = *ObjectFile.FatMachO; + MachO::fat_header header; + header.magic = FatFile.Header.magic; + header.nfat_arch = FatFile.Header.nfat_arch; + if (sys::IsLittleEndianHost) + swapStruct(header); + OS.write(reinterpret_cast<const char *>(&header), sizeof(MachO::fat_header)); +} + +template <typename FatArchType> +FatArchType constructFatArch(MachOYAML::FatArch &Arch) { + FatArchType FatArch; + FatArch.cputype = Arch.cputype; + FatArch.cpusubtype = Arch.cpusubtype; + FatArch.offset = Arch.offset; + FatArch.size = Arch.size; + FatArch.align = Arch.align; + return FatArch; +} + +template <typename StructType> +void writeFatArch(MachOYAML::FatArch &LC, raw_ostream &OS) {} + +template <> +void writeFatArch<MachO::fat_arch>(MachOYAML::FatArch &Arch, raw_ostream &OS) { + auto FatArch = constructFatArch<MachO::fat_arch>(Arch); + if (sys::IsLittleEndianHost) + swapStruct(FatArch); + OS.write(reinterpret_cast<const char *>(&FatArch), sizeof(MachO::fat_arch)); +} + +template <> +void writeFatArch<MachO::fat_arch_64>(MachOYAML::FatArch &Arch, + raw_ostream &OS) { + auto FatArch = constructFatArch<MachO::fat_arch_64>(Arch); + FatArch.reserved = Arch.reserved; + if (sys::IsLittleEndianHost) + swapStruct(FatArch); + OS.write(reinterpret_cast<const char *>(&FatArch), + sizeof(MachO::fat_arch_64)); +} + +void UniversalWriter::writeFatArchs(raw_ostream &OS) { + auto &FatFile = *ObjectFile.FatMachO; + bool is64Bit = FatFile.Header.magic == MachO::FAT_MAGIC_64; + for (auto Arch : FatFile.FatArchs) { + if (is64Bit) + writeFatArch<MachO::fat_arch_64>(Arch, OS); + else + writeFatArch<MachO::fat_arch>(Arch, OS); + } +} + +void UniversalWriter::ZeroToOffset(raw_ostream &OS, size_t Offset) { + auto currOffset = OS.tell() - fileStart; + if (currOffset < Offset) + ZeroFillBytes(OS, Offset - currOffset); +} + +} // end anonymous namespace + +namespace llvm { +namespace yaml { + +bool yaml2macho(YamlObjectFile &Doc, raw_ostream &Out, ErrorHandler /*EH*/) { + UniversalWriter Writer(Doc); + Writer.writeMachO(Out); + return true; +} + +} // namespace yaml +} // namespace llvm diff --git a/lib/ObjectYAML/MachOYAML.cpp b/lib/ObjectYAML/MachOYAML.cpp index d12f12cf4435..0f7cd1e1495c 100644 --- a/lib/ObjectYAML/MachOYAML.cpp +++ b/lib/ObjectYAML/MachOYAML.cpp @@ -287,6 +287,15 @@ void MappingTraits<MachOYAML::Section>::mapping(IO &IO, IO.mapRequired("reserved1", Section.reserved1); IO.mapRequired("reserved2", Section.reserved2); IO.mapOptional("reserved3", Section.reserved3); + IO.mapOptional("content", Section.content); +} + +StringRef +MappingTraits<MachOYAML::Section>::validate(IO &IO, + MachOYAML::Section &Section) { + if (Section.content && Section.size < Section.content->binary_size()) + return "Section size must be greater than or equal to the content size"; + return {}; } void MappingTraits<MachO::build_tool_version>::mapping( diff --git a/lib/ObjectYAML/MinidumpEmitter.cpp b/lib/ObjectYAML/MinidumpEmitter.cpp new file mode 100644 index 000000000000..bbfd2cd8cbab --- /dev/null +++ b/lib/ObjectYAML/MinidumpEmitter.cpp @@ -0,0 +1,247 @@ +//===- yaml2minidump.cpp - Convert a YAML file to a minidump 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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/ObjectYAML/MinidumpYAML.h" +#include "llvm/ObjectYAML/yaml2obj.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::minidump; +using namespace llvm::MinidumpYAML; + +namespace { +/// A helper class to manage the placement of various structures into the final +/// minidump binary. Space for objects can be allocated via various allocate*** +/// methods, while the final minidump file is written by calling the writeTo +/// method. The plain versions of allocation functions take a reference to the +/// data which is to be written (and hence the data must be available until +/// writeTo is called), while the "New" versions allocate the data in an +/// allocator-managed buffer, which is available until the allocator object is +/// destroyed. For both kinds of functions, it is possible to modify the +/// data for which the space has been "allocated" until the final writeTo call. +/// This is useful for "linking" the allocated structures via their offsets. +class BlobAllocator { +public: + size_t tell() const { return NextOffset; } + + size_t allocateCallback(size_t Size, + std::function<void(raw_ostream &)> Callback) { + size_t Offset = NextOffset; + NextOffset += Size; + Callbacks.push_back(std::move(Callback)); + return Offset; + } + + size_t allocateBytes(ArrayRef<uint8_t> Data) { + return allocateCallback( + Data.size(), [Data](raw_ostream &OS) { OS << toStringRef(Data); }); + } + + size_t allocateBytes(yaml::BinaryRef Data) { + return allocateCallback(Data.binary_size(), [Data](raw_ostream &OS) { + Data.writeAsBinary(OS); + }); + } + + template <typename T> size_t allocateArray(ArrayRef<T> Data) { + return allocateBytes({reinterpret_cast<const uint8_t *>(Data.data()), + sizeof(T) * Data.size()}); + } + + template <typename T, typename RangeType> + std::pair<size_t, MutableArrayRef<T>> + allocateNewArray(const iterator_range<RangeType> &Range); + + template <typename T> size_t allocateObject(const T &Data) { + return allocateArray(makeArrayRef(Data)); + } + + template <typename T, typename... Types> + std::pair<size_t, T *> allocateNewObject(Types &&... Args) { + T *Object = new (Temporaries.Allocate<T>()) T(std::forward<Types>(Args)...); + return {allocateObject(*Object), Object}; + } + + size_t allocateString(StringRef Str); + + void writeTo(raw_ostream &OS) const; + +private: + size_t NextOffset = 0; + + BumpPtrAllocator Temporaries; + std::vector<std::function<void(raw_ostream &)>> Callbacks; +}; +} // namespace + +template <typename T, typename RangeType> +std::pair<size_t, MutableArrayRef<T>> +BlobAllocator::allocateNewArray(const iterator_range<RangeType> &Range) { + size_t Num = std::distance(Range.begin(), Range.end()); + MutableArrayRef<T> Array(Temporaries.Allocate<T>(Num), Num); + std::uninitialized_copy(Range.begin(), Range.end(), Array.begin()); + return {allocateArray(Array), Array}; +} + +size_t BlobAllocator::allocateString(StringRef Str) { + SmallVector<UTF16, 32> WStr; + bool OK = convertUTF8ToUTF16String(Str, WStr); + assert(OK && "Invalid UTF8 in Str?"); + (void)OK; + + // The utf16 string is null-terminated, but the terminator is not counted in + // the string size. + WStr.push_back(0); + size_t Result = + allocateNewObject<support::ulittle32_t>(2 * (WStr.size() - 1)).first; + allocateNewArray<support::ulittle16_t>(make_range(WStr.begin(), WStr.end())); + return Result; +} + +void BlobAllocator::writeTo(raw_ostream &OS) const { + size_t BeginOffset = OS.tell(); + for (const auto &Callback : Callbacks) + Callback(OS); + assert(OS.tell() == BeginOffset + NextOffset && + "Callbacks wrote an unexpected number of bytes."); + (void)BeginOffset; +} + +static LocationDescriptor layout(BlobAllocator &File, yaml::BinaryRef Data) { + return {support::ulittle32_t(Data.binary_size()), + support::ulittle32_t(File.allocateBytes(Data))}; +} + +static size_t layout(BlobAllocator &File, MinidumpYAML::ExceptionStream &S) { + File.allocateObject(S.MDExceptionStream); + + size_t DataEnd = File.tell(); + + // Lay out the thread context data, (which is not a part of the stream). + // TODO: This usually (always?) matches the thread context of the + // corresponding thread, and may overlap memory regions as well. We could + // add a level of indirection to the MinidumpYAML format (like an array of + // Blobs that the LocationDescriptors index into) to be able to distinguish + // the cases where location descriptions overlap vs happen to reference + // identical data. + S.MDExceptionStream.ThreadContext = layout(File, S.ThreadContext); + + return DataEnd; +} + +static void layout(BlobAllocator &File, MemoryListStream::entry_type &Range) { + Range.Entry.Memory = layout(File, Range.Content); +} + +static void layout(BlobAllocator &File, ModuleListStream::entry_type &M) { + M.Entry.ModuleNameRVA = File.allocateString(M.Name); + + M.Entry.CvRecord = layout(File, M.CvRecord); + M.Entry.MiscRecord = layout(File, M.MiscRecord); +} + +static void layout(BlobAllocator &File, ThreadListStream::entry_type &T) { + T.Entry.Stack.Memory = layout(File, T.Stack); + T.Entry.Context = layout(File, T.Context); +} + +template <typename EntryT> +static size_t layout(BlobAllocator &File, + MinidumpYAML::detail::ListStream<EntryT> &S) { + + File.allocateNewObject<support::ulittle32_t>(S.Entries.size()); + for (auto &E : S.Entries) + File.allocateObject(E.Entry); + + size_t DataEnd = File.tell(); + + // Lay out the auxiliary data, (which is not a part of the stream). + DataEnd = File.tell(); + for (auto &E : S.Entries) + layout(File, E); + + return DataEnd; +} + +static Directory layout(BlobAllocator &File, Stream &S) { + Directory Result; + Result.Type = S.Type; + Result.Location.RVA = File.tell(); + Optional<size_t> DataEnd; + switch (S.Kind) { + case Stream::StreamKind::Exception: + DataEnd = layout(File, cast<MinidumpYAML::ExceptionStream>(S)); + break; + case Stream::StreamKind::MemoryInfoList: { + MemoryInfoListStream &InfoList = cast<MemoryInfoListStream>(S); + File.allocateNewObject<minidump::MemoryInfoListHeader>( + sizeof(minidump::MemoryInfoListHeader), sizeof(minidump::MemoryInfo), + InfoList.Infos.size()); + File.allocateArray(makeArrayRef(InfoList.Infos)); + break; + } + case Stream::StreamKind::MemoryList: + DataEnd = layout(File, cast<MemoryListStream>(S)); + break; + case Stream::StreamKind::ModuleList: + DataEnd = layout(File, cast<ModuleListStream>(S)); + break; + case Stream::StreamKind::RawContent: { + RawContentStream &Raw = cast<RawContentStream>(S); + File.allocateCallback(Raw.Size, [&Raw](raw_ostream &OS) { + Raw.Content.writeAsBinary(OS); + assert(Raw.Content.binary_size() <= Raw.Size); + OS << std::string(Raw.Size - Raw.Content.binary_size(), '\0'); + }); + break; + } + case Stream::StreamKind::SystemInfo: { + SystemInfoStream &SystemInfo = cast<SystemInfoStream>(S); + File.allocateObject(SystemInfo.Info); + // The CSD string is not a part of the stream. + DataEnd = File.tell(); + SystemInfo.Info.CSDVersionRVA = File.allocateString(SystemInfo.CSDVersion); + break; + } + case Stream::StreamKind::TextContent: + File.allocateArray(arrayRefFromStringRef(cast<TextContentStream>(S).Text)); + break; + case Stream::StreamKind::ThreadList: + DataEnd = layout(File, cast<ThreadListStream>(S)); + break; + } + // If DataEnd is not set, we assume everything we generated is a part of the + // stream. + Result.Location.DataSize = + DataEnd.getValueOr(File.tell()) - Result.Location.RVA; + return Result; +} + +namespace llvm { +namespace yaml { + +bool yaml2minidump(MinidumpYAML::Object &Obj, raw_ostream &Out, + ErrorHandler /*EH*/) { + BlobAllocator File; + File.allocateObject(Obj.Header); + + std::vector<Directory> StreamDirectory(Obj.Streams.size()); + Obj.Header.StreamDirectoryRVA = + File.allocateArray(makeArrayRef(StreamDirectory)); + Obj.Header.NumberOfStreams = StreamDirectory.size(); + + for (auto &Stream : enumerate(Obj.Streams)) + StreamDirectory[Stream.index()] = layout(File, *Stream.value()); + + File.writeTo(Out); + return true; +} + +} // namespace yaml +} // namespace llvm diff --git a/lib/ObjectYAML/MinidumpYAML.cpp b/lib/ObjectYAML/MinidumpYAML.cpp index f5f2acd0cc4b..21b2a4d78629 100644 --- a/lib/ObjectYAML/MinidumpYAML.cpp +++ b/lib/ObjectYAML/MinidumpYAML.cpp @@ -8,110 +8,11 @@ #include "llvm/ObjectYAML/MinidumpYAML.h" #include "llvm/Support/Allocator.h" -#include "llvm/Support/ConvertUTF.h" using namespace llvm; using namespace llvm::MinidumpYAML; using namespace llvm::minidump; -namespace { -/// A helper class to manage the placement of various structures into the final -/// minidump binary. Space for objects can be allocated via various allocate*** -/// methods, while the final minidump file is written by calling the writeTo -/// method. The plain versions of allocation functions take a reference to the -/// data which is to be written (and hence the data must be available until -/// writeTo is called), while the "New" versions allocate the data in an -/// allocator-managed buffer, which is available until the allocator object is -/// destroyed. For both kinds of functions, it is possible to modify the -/// data for which the space has been "allocated" until the final writeTo call. -/// This is useful for "linking" the allocated structures via their offsets. -class BlobAllocator { -public: - size_t tell() const { return NextOffset; } - - size_t allocateCallback(size_t Size, - std::function<void(raw_ostream &)> Callback) { - size_t Offset = NextOffset; - NextOffset += Size; - Callbacks.push_back(std::move(Callback)); - return Offset; - } - - size_t allocateBytes(ArrayRef<uint8_t> Data) { - return allocateCallback( - Data.size(), [Data](raw_ostream &OS) { OS << toStringRef(Data); }); - } - - size_t allocateBytes(yaml::BinaryRef Data) { - return allocateCallback(Data.binary_size(), [Data](raw_ostream &OS) { - Data.writeAsBinary(OS); - }); - } - - template <typename T> size_t allocateArray(ArrayRef<T> Data) { - return allocateBytes({reinterpret_cast<const uint8_t *>(Data.data()), - sizeof(T) * Data.size()}); - } - - template <typename T, typename RangeType> - std::pair<size_t, MutableArrayRef<T>> - allocateNewArray(const iterator_range<RangeType> &Range); - - template <typename T> size_t allocateObject(const T &Data) { - return allocateArray(makeArrayRef(Data)); - } - - template <typename T, typename... Types> - std::pair<size_t, T *> allocateNewObject(Types &&... Args) { - T *Object = new (Temporaries.Allocate<T>()) T(std::forward<Types>(Args)...); - return {allocateObject(*Object), Object}; - } - - size_t allocateString(StringRef Str); - - void writeTo(raw_ostream &OS) const; - -private: - size_t NextOffset = 0; - - BumpPtrAllocator Temporaries; - std::vector<std::function<void(raw_ostream &)>> Callbacks; -}; -} // namespace - -template <typename T, typename RangeType> -std::pair<size_t, MutableArrayRef<T>> -BlobAllocator::allocateNewArray(const iterator_range<RangeType> &Range) { - size_t Num = std::distance(Range.begin(), Range.end()); - MutableArrayRef<T> Array(Temporaries.Allocate<T>(Num), Num); - std::uninitialized_copy(Range.begin(), Range.end(), Array.begin()); - return {allocateArray(Array), Array}; -} - -size_t BlobAllocator::allocateString(StringRef Str) { - SmallVector<UTF16, 32> WStr; - bool OK = convertUTF8ToUTF16String(Str, WStr); - assert(OK && "Invalid UTF8 in Str?"); - (void)OK; - - // The utf16 string is null-terminated, but the terminator is not counted in - // the string size. - WStr.push_back(0); - size_t Result = - allocateNewObject<support::ulittle32_t>(2 * (WStr.size() - 1)).first; - allocateNewArray<support::ulittle16_t>(make_range(WStr.begin(), WStr.end())); - return Result; -} - -void BlobAllocator::writeTo(raw_ostream &OS) const { - size_t BeginOffset = OS.tell(); - for (const auto &Callback : Callbacks) - Callback(OS); - assert(OS.tell() == BeginOffset + NextOffset && - "Callbacks wrote an unexpected number of bytes."); - (void)BeginOffset; -} - /// Perform an optional yaml-mapping of an endian-aware type EndianType. The /// only purpose of this function is to avoid casting the Default value to the /// endian type; @@ -168,6 +69,10 @@ Stream::~Stream() = default; Stream::StreamKind Stream::getKind(StreamType Type) { switch (Type) { + case StreamType::Exception: + return StreamKind::Exception; + case StreamType::MemoryInfoList: + return StreamKind::MemoryInfoList; case StreamType::MemoryList: return StreamKind::MemoryList; case StreamType::ModuleList: @@ -192,22 +97,45 @@ Stream::StreamKind Stream::getKind(StreamType Type) { std::unique_ptr<Stream> Stream::create(StreamType Type) { StreamKind Kind = getKind(Type); switch (Kind) { + case StreamKind::Exception: + return std::make_unique<ExceptionStream>(); + case StreamKind::MemoryInfoList: + return std::make_unique<MemoryInfoListStream>(); case StreamKind::MemoryList: - return llvm::make_unique<MemoryListStream>(); + return std::make_unique<MemoryListStream>(); case StreamKind::ModuleList: - return llvm::make_unique<ModuleListStream>(); + return std::make_unique<ModuleListStream>(); case StreamKind::RawContent: - return llvm::make_unique<RawContentStream>(Type); + return std::make_unique<RawContentStream>(Type); case StreamKind::SystemInfo: - return llvm::make_unique<SystemInfoStream>(); + return std::make_unique<SystemInfoStream>(); case StreamKind::TextContent: - return llvm::make_unique<TextContentStream>(Type); + return std::make_unique<TextContentStream>(Type); case StreamKind::ThreadList: - return llvm::make_unique<ThreadListStream>(); + return std::make_unique<ThreadListStream>(); } llvm_unreachable("Unhandled stream kind!"); } +void yaml::ScalarBitSetTraits<MemoryProtection>::bitset( + IO &IO, MemoryProtection &Protect) { +#define HANDLE_MDMP_PROTECT(CODE, NAME, NATIVENAME) \ + IO.bitSetCase(Protect, #NATIVENAME, MemoryProtection::NAME); +#include "llvm/BinaryFormat/MinidumpConstants.def" +} + +void yaml::ScalarBitSetTraits<MemoryState>::bitset(IO &IO, MemoryState &State) { +#define HANDLE_MDMP_MEMSTATE(CODE, NAME, NATIVENAME) \ + IO.bitSetCase(State, #NATIVENAME, MemoryState::NAME); +#include "llvm/BinaryFormat/MinidumpConstants.def" +} + +void yaml::ScalarBitSetTraits<MemoryType>::bitset(IO &IO, MemoryType &Type) { +#define HANDLE_MDMP_MEMTYPE(CODE, NAME, NATIVENAME) \ + IO.bitSetCase(Type, #NATIVENAME, MemoryType::NAME); +#include "llvm/BinaryFormat/MinidumpConstants.def" +} + void yaml::ScalarEnumerationTraits<ProcessorArchitecture>::enumeration( IO &IO, ProcessorArchitecture &Arch) { #define HANDLE_MDMP_ARCH(CODE, NAME) \ @@ -314,6 +242,20 @@ void yaml::MappingTraits<CPUInfo::X86Info>::mapping(IO &IO, mapOptionalHex(IO, "AMD Extended Features", Info.AMDExtendedFeatures, 0); } +void yaml::MappingTraits<MemoryInfo>::mapping(IO &IO, MemoryInfo &Info) { + mapRequiredHex(IO, "Base Address", Info.BaseAddress); + mapOptionalHex(IO, "Allocation Base", Info.AllocationBase, Info.BaseAddress); + mapRequiredAs<MemoryProtection>(IO, "Allocation Protect", + Info.AllocationProtect); + mapOptionalHex(IO, "Reserved0", Info.Reserved0, 0); + mapRequiredHex(IO, "Region Size", Info.RegionSize); + mapRequiredAs<MemoryState>(IO, "State", Info.State); + mapOptionalAs<MemoryProtection>(IO, "Protect", Info.Protect, + Info.AllocationProtect); + mapRequiredAs<MemoryType>(IO, "Type", Info.Type); + mapOptionalHex(IO, "Reserved1", Info.Reserved1, 0); +} + void yaml::MappingTraits<VSFixedFileInfo>::mapping(IO &IO, VSFixedFileInfo &Info) { mapOptionalHex(IO, "Signature", Info.Signature, 0); @@ -336,8 +278,7 @@ void yaml::MappingTraits<ModuleListStream::entry_type>::mapping( mapRequiredHex(IO, "Base of Image", M.Entry.BaseOfImage); mapRequiredHex(IO, "Size of Image", M.Entry.SizeOfImage); mapOptionalHex(IO, "Checksum", M.Entry.Checksum, 0); - IO.mapOptional("Time Date Stamp", M.Entry.TimeDateStamp, - support::ulittle32_t(0)); + mapOptional(IO, "Time Date Stamp", M.Entry.TimeDateStamp, 0); IO.mapRequired("Module Name", M.Name); IO.mapOptional("Version Info", M.Entry.VersionInfo, VSFixedFileInfo()); IO.mapRequired("CodeView Record", M.CvRecord); @@ -363,6 +304,10 @@ void yaml::MappingTraits<MemoryListStream::entry_type>::mapping( IO, Range.Entry, Range.Content); } +static void streamMapping(yaml::IO &IO, MemoryInfoListStream &Stream) { + IO.mapRequired("Memory Ranges", Stream.Infos); +} + static void streamMapping(yaml::IO &IO, MemoryListStream &Stream) { IO.mapRequired("Memory Ranges", Stream.Entries); } @@ -425,6 +370,32 @@ static void streamMapping(yaml::IO &IO, ThreadListStream &Stream) { IO.mapRequired("Threads", Stream.Entries); } +static void streamMapping(yaml::IO &IO, MinidumpYAML::ExceptionStream &Stream) { + mapRequiredHex(IO, "Thread ID", Stream.MDExceptionStream.ThreadId); + IO.mapRequired("Exception Record", Stream.MDExceptionStream.ExceptionRecord); + IO.mapRequired("Thread Context", Stream.ThreadContext); +} + +void yaml::MappingTraits<minidump::Exception>::mapping( + yaml::IO &IO, minidump::Exception &Exception) { + mapRequiredHex(IO, "Exception Code", Exception.ExceptionCode); + mapOptionalHex(IO, "Exception Flags", Exception.ExceptionFlags, 0); + mapOptionalHex(IO, "Exception Record", Exception.ExceptionRecord, 0); + mapOptionalHex(IO, "Exception Address", Exception.ExceptionAddress, 0); + mapOptional(IO, "Number of Parameters", Exception.NumberParameters, 0); + + for (size_t Index = 0; Index < Exception.MaxParameters; ++Index) { + SmallString<16> Name("Parameter "); + Twine(Index).toVector(Name); + support::ulittle64_t &Field = Exception.ExceptionInformation[Index]; + + if (Index < Exception.NumberParameters) + mapRequiredHex(IO, Name.c_str(), Field); + else + mapOptionalHex(IO, Name.c_str(), Field, 0); + } +} + void yaml::MappingTraits<std::unique_ptr<Stream>>::mapping( yaml::IO &IO, std::unique_ptr<MinidumpYAML::Stream> &S) { StreamType Type; @@ -435,6 +406,12 @@ void yaml::MappingTraits<std::unique_ptr<Stream>>::mapping( if (!IO.outputting()) S = MinidumpYAML::Stream::create(Type); switch (S->Kind) { + case MinidumpYAML::Stream::StreamKind::Exception: + streamMapping(IO, llvm::cast<MinidumpYAML::ExceptionStream>(*S)); + break; + case MinidumpYAML::Stream::StreamKind::MemoryInfoList: + streamMapping(IO, llvm::cast<MemoryInfoListStream>(*S)); + break; case MinidumpYAML::Stream::StreamKind::MemoryList: streamMapping(IO, llvm::cast<MemoryListStream>(*S)); break; @@ -461,6 +438,8 @@ StringRef yaml::MappingTraits<std::unique_ptr<Stream>>::validate( switch (S->Kind) { case MinidumpYAML::Stream::StreamKind::RawContent: return streamValidate(cast<RawContentStream>(*S)); + case MinidumpYAML::Stream::StreamKind::Exception: + case MinidumpYAML::Stream::StreamKind::MemoryInfoList: case MinidumpYAML::Stream::StreamKind::MemoryList: case MinidumpYAML::Stream::StreamKind::ModuleList: case MinidumpYAML::Stream::StreamKind::SystemInfo: @@ -479,118 +458,28 @@ void yaml::MappingTraits<Object>::mapping(IO &IO, Object &O) { IO.mapRequired("Streams", O.Streams); } -static LocationDescriptor layout(BlobAllocator &File, yaml::BinaryRef Data) { - return {support::ulittle32_t(Data.binary_size()), - support::ulittle32_t(File.allocateBytes(Data))}; -} - -static void layout(BlobAllocator &File, MemoryListStream::entry_type &Range) { - Range.Entry.Memory = layout(File, Range.Content); -} - -static void layout(BlobAllocator &File, ModuleListStream::entry_type &M) { - M.Entry.ModuleNameRVA = File.allocateString(M.Name); - - M.Entry.CvRecord = layout(File, M.CvRecord); - M.Entry.MiscRecord = layout(File, M.MiscRecord); -} - -static void layout(BlobAllocator &File, ThreadListStream::entry_type &T) { - T.Entry.Stack.Memory = layout(File, T.Stack); - T.Entry.Context = layout(File, T.Context); -} - -template <typename EntryT> -static size_t layout(BlobAllocator &File, - MinidumpYAML::detail::ListStream<EntryT> &S) { - - File.allocateNewObject<support::ulittle32_t>(S.Entries.size()); - for (auto &E : S.Entries) - File.allocateObject(E.Entry); - - size_t DataEnd = File.tell(); - - // Lay out the auxiliary data, (which is not a part of the stream). - DataEnd = File.tell(); - for (auto &E : S.Entries) - layout(File, E); - - return DataEnd; -} - -static Directory layout(BlobAllocator &File, Stream &S) { - Directory Result; - Result.Type = S.Type; - Result.Location.RVA = File.tell(); - Optional<size_t> DataEnd; - switch (S.Kind) { - case Stream::StreamKind::MemoryList: - DataEnd = layout(File, cast<MemoryListStream>(S)); - break; - case Stream::StreamKind::ModuleList: - DataEnd = layout(File, cast<ModuleListStream>(S)); - break; - case Stream::StreamKind::RawContent: { - RawContentStream &Raw = cast<RawContentStream>(S); - File.allocateCallback(Raw.Size, [&Raw](raw_ostream &OS) { - Raw.Content.writeAsBinary(OS); - assert(Raw.Content.binary_size() <= Raw.Size); - OS << std::string(Raw.Size - Raw.Content.binary_size(), '\0'); - }); - break; - } - case Stream::StreamKind::SystemInfo: { - SystemInfoStream &SystemInfo = cast<SystemInfoStream>(S); - File.allocateObject(SystemInfo.Info); - // The CSD string is not a part of the stream. - DataEnd = File.tell(); - SystemInfo.Info.CSDVersionRVA = File.allocateString(SystemInfo.CSDVersion); - break; - } - case Stream::StreamKind::TextContent: - File.allocateArray(arrayRefFromStringRef(cast<TextContentStream>(S).Text)); - break; - case Stream::StreamKind::ThreadList: - DataEnd = layout(File, cast<ThreadListStream>(S)); - break; - } - // If DataEnd is not set, we assume everything we generated is a part of the - // stream. - Result.Location.DataSize = - DataEnd.getValueOr(File.tell()) - Result.Location.RVA; - return Result; -} - -void MinidumpYAML::writeAsBinary(Object &Obj, raw_ostream &OS) { - BlobAllocator File; - File.allocateObject(Obj.Header); - - std::vector<Directory> StreamDirectory(Obj.Streams.size()); - Obj.Header.StreamDirectoryRVA = - File.allocateArray(makeArrayRef(StreamDirectory)); - Obj.Header.NumberOfStreams = StreamDirectory.size(); - - for (auto &Stream : enumerate(Obj.Streams)) - StreamDirectory[Stream.index()] = layout(File, *Stream.value()); - - File.writeTo(OS); -} - -Error MinidumpYAML::writeAsBinary(StringRef Yaml, raw_ostream &OS) { - yaml::Input Input(Yaml); - Object Obj; - Input >> Obj; - if (std::error_code EC = Input.error()) - return errorCodeToError(EC); - - writeAsBinary(Obj, OS); - return Error::success(); -} - Expected<std::unique_ptr<Stream>> Stream::create(const Directory &StreamDesc, const object::MinidumpFile &File) { StreamKind Kind = getKind(StreamDesc.Type); switch (Kind) { + case StreamKind::Exception: { + Expected<const minidump::ExceptionStream &> ExpectedExceptionStream = + File.getExceptionStream(); + if (!ExpectedExceptionStream) + return ExpectedExceptionStream.takeError(); + Expected<ArrayRef<uint8_t>> ExpectedThreadContext = + File.getRawData(ExpectedExceptionStream->ThreadContext); + if (!ExpectedThreadContext) + return ExpectedThreadContext.takeError(); + return std::make_unique<ExceptionStream>(*ExpectedExceptionStream, + *ExpectedThreadContext); + } + case StreamKind::MemoryInfoList: { + if (auto ExpectedList = File.getMemoryInfoList()) + return std::make_unique<MemoryInfoListStream>(*ExpectedList); + else + return ExpectedList.takeError(); + } case StreamKind::MemoryList: { auto ExpectedList = File.getMemoryList(); if (!ExpectedList) @@ -602,7 +491,7 @@ Stream::create(const Directory &StreamDesc, const object::MinidumpFile &File) { return ExpectedContent.takeError(); Ranges.push_back({MD, *ExpectedContent}); } - return llvm::make_unique<MemoryListStream>(std::move(Ranges)); + return std::make_unique<MemoryListStream>(std::move(Ranges)); } case StreamKind::ModuleList: { auto ExpectedList = File.getModuleList(); @@ -622,10 +511,10 @@ Stream::create(const Directory &StreamDesc, const object::MinidumpFile &File) { Modules.push_back( {M, std::move(*ExpectedName), *ExpectedCv, *ExpectedMisc}); } - return llvm::make_unique<ModuleListStream>(std::move(Modules)); + return std::make_unique<ModuleListStream>(std::move(Modules)); } case StreamKind::RawContent: - return llvm::make_unique<RawContentStream>(StreamDesc.Type, + return std::make_unique<RawContentStream>(StreamDesc.Type, File.getRawStream(StreamDesc)); case StreamKind::SystemInfo: { auto ExpectedInfo = File.getSystemInfo(); @@ -634,11 +523,11 @@ Stream::create(const Directory &StreamDesc, const object::MinidumpFile &File) { auto ExpectedCSDVersion = File.getString(ExpectedInfo->CSDVersionRVA); if (!ExpectedCSDVersion) return ExpectedInfo.takeError(); - return llvm::make_unique<SystemInfoStream>(*ExpectedInfo, + return std::make_unique<SystemInfoStream>(*ExpectedInfo, std::move(*ExpectedCSDVersion)); } case StreamKind::TextContent: - return llvm::make_unique<TextContentStream>( + return std::make_unique<TextContentStream>( StreamDesc.Type, toStringRef(File.getRawStream(StreamDesc))); case StreamKind::ThreadList: { auto ExpectedList = File.getThreadList(); @@ -654,7 +543,7 @@ Stream::create(const Directory &StreamDesc, const object::MinidumpFile &File) { return ExpectedContext.takeError(); Threads.push_back({T, *ExpectedStack, *ExpectedContext}); } - return llvm::make_unique<ThreadListStream>(std::move(Threads)); + return std::make_unique<ThreadListStream>(std::move(Threads)); } } llvm_unreachable("Unhandled stream kind!"); diff --git a/lib/ObjectYAML/WasmEmitter.cpp b/lib/ObjectYAML/WasmEmitter.cpp new file mode 100644 index 000000000000..debc040587a8 --- /dev/null +++ b/lib/ObjectYAML/WasmEmitter.cpp @@ -0,0 +1,633 @@ +//===- yaml2wasm - Convert YAML to a Wasm 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 Wasm component of yaml2obj. +/// +//===----------------------------------------------------------------------===// +// + +#include "llvm/Object/Wasm.h" +#include "llvm/ObjectYAML/ObjectYAML.h" +#include "llvm/ObjectYAML/yaml2obj.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/LEB128.h" + +using namespace llvm; + +namespace { +/// This parses a yaml stream that represents a Wasm object file. +/// See docs/yaml2obj for the yaml scheema. +class WasmWriter { +public: + WasmWriter(WasmYAML::Object &Obj, yaml::ErrorHandler EH) + : Obj(Obj), ErrHandler(EH) {} + bool writeWasm(raw_ostream &OS); + +private: + void writeRelocSection(raw_ostream &OS, WasmYAML::Section &Sec, + uint32_t SectionIndex); + + void writeInitExpr(raw_ostream &OS, const wasm::WasmInitExpr &InitExpr); + + void writeSectionContent(raw_ostream &OS, WasmYAML::CustomSection &Section); + void writeSectionContent(raw_ostream &OS, WasmYAML::TypeSection &Section); + void writeSectionContent(raw_ostream &OS, WasmYAML::ImportSection &Section); + void writeSectionContent(raw_ostream &OS, WasmYAML::FunctionSection &Section); + void writeSectionContent(raw_ostream &OS, WasmYAML::TableSection &Section); + void writeSectionContent(raw_ostream &OS, WasmYAML::MemorySection &Section); + void writeSectionContent(raw_ostream &OS, WasmYAML::GlobalSection &Section); + void writeSectionContent(raw_ostream &OS, WasmYAML::EventSection &Section); + void writeSectionContent(raw_ostream &OS, WasmYAML::ExportSection &Section); + void writeSectionContent(raw_ostream &OS, WasmYAML::StartSection &Section); + void writeSectionContent(raw_ostream &OS, WasmYAML::ElemSection &Section); + void writeSectionContent(raw_ostream &OS, WasmYAML::CodeSection &Section); + void writeSectionContent(raw_ostream &OS, WasmYAML::DataSection &Section); + void writeSectionContent(raw_ostream &OS, WasmYAML::DataCountSection &Section); + + // Custom section types + void writeSectionContent(raw_ostream &OS, WasmYAML::DylinkSection &Section); + void writeSectionContent(raw_ostream &OS, WasmYAML::NameSection &Section); + void writeSectionContent(raw_ostream &OS, WasmYAML::LinkingSection &Section); + void writeSectionContent(raw_ostream &OS, WasmYAML::ProducersSection &Section); + void writeSectionContent(raw_ostream &OS, + WasmYAML::TargetFeaturesSection &Section); + WasmYAML::Object &Obj; + uint32_t NumImportedFunctions = 0; + uint32_t NumImportedGlobals = 0; + uint32_t NumImportedEvents = 0; + + bool HasError = false; + yaml::ErrorHandler ErrHandler; + void reportError(const Twine &Msg); +}; + +class SubSectionWriter { + raw_ostream &OS; + std::string OutString; + raw_string_ostream StringStream; + +public: + SubSectionWriter(raw_ostream &OS) : OS(OS), StringStream(OutString) {} + + void done() { + StringStream.flush(); + encodeULEB128(OutString.size(), OS); + OS << OutString; + OutString.clear(); + } + + raw_ostream &getStream() { return StringStream; } +}; + +} // end anonymous namespace + +static int writeUint64(raw_ostream &OS, uint64_t Value) { + char Data[sizeof(Value)]; + support::endian::write64le(Data, Value); + OS.write(Data, sizeof(Data)); + return 0; +} + +static int writeUint32(raw_ostream &OS, uint32_t Value) { + char Data[sizeof(Value)]; + support::endian::write32le(Data, Value); + OS.write(Data, sizeof(Data)); + return 0; +} + +static int writeUint8(raw_ostream &OS, uint8_t Value) { + char Data[sizeof(Value)]; + memcpy(Data, &Value, sizeof(Data)); + OS.write(Data, sizeof(Data)); + return 0; +} + +static int writeStringRef(const StringRef &Str, raw_ostream &OS) { + encodeULEB128(Str.size(), OS); + OS << Str; + return 0; +} + +static int writeLimits(const WasmYAML::Limits &Lim, raw_ostream &OS) { + writeUint8(OS, Lim.Flags); + encodeULEB128(Lim.Initial, OS); + if (Lim.Flags & wasm::WASM_LIMITS_FLAG_HAS_MAX) + encodeULEB128(Lim.Maximum, OS); + return 0; +} + +void WasmWriter::reportError(const Twine &Msg) { + ErrHandler(Msg); + HasError = true; +} + +void WasmWriter::writeInitExpr(raw_ostream &OS, + const wasm::WasmInitExpr &InitExpr) { + writeUint8(OS, InitExpr.Opcode); + switch (InitExpr.Opcode) { + case wasm::WASM_OPCODE_I32_CONST: + encodeSLEB128(InitExpr.Value.Int32, OS); + break; + case wasm::WASM_OPCODE_I64_CONST: + encodeSLEB128(InitExpr.Value.Int64, OS); + break; + case wasm::WASM_OPCODE_F32_CONST: + writeUint32(OS, InitExpr.Value.Float32); + break; + case wasm::WASM_OPCODE_F64_CONST: + writeUint64(OS, InitExpr.Value.Float64); + break; + case wasm::WASM_OPCODE_GLOBAL_GET: + encodeULEB128(InitExpr.Value.Global, OS); + break; + default: + reportError("unknown opcode in init_expr: " + Twine(InitExpr.Opcode)); + return; + } + writeUint8(OS, wasm::WASM_OPCODE_END); +} + +void WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::DylinkSection &Section) { + writeStringRef(Section.Name, OS); + encodeULEB128(Section.MemorySize, OS); + encodeULEB128(Section.MemoryAlignment, OS); + encodeULEB128(Section.TableSize, OS); + encodeULEB128(Section.TableAlignment, OS); + encodeULEB128(Section.Needed.size(), OS); + for (StringRef Needed : Section.Needed) + writeStringRef(Needed, OS); +} + +void WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::LinkingSection &Section) { + writeStringRef(Section.Name, OS); + encodeULEB128(Section.Version, OS); + + SubSectionWriter SubSection(OS); + + // SYMBOL_TABLE subsection + if (Section.SymbolTable.size()) { + writeUint8(OS, wasm::WASM_SYMBOL_TABLE); + + encodeULEB128(Section.SymbolTable.size(), SubSection.getStream()); +#ifndef NDEBUG + uint32_t SymbolIndex = 0; +#endif + for (const WasmYAML::SymbolInfo &Info : Section.SymbolTable) { + assert(Info.Index == SymbolIndex++); + writeUint8(SubSection.getStream(), Info.Kind); + encodeULEB128(Info.Flags, SubSection.getStream()); + switch (Info.Kind) { + case wasm::WASM_SYMBOL_TYPE_FUNCTION: + case wasm::WASM_SYMBOL_TYPE_GLOBAL: + case wasm::WASM_SYMBOL_TYPE_EVENT: + encodeULEB128(Info.ElementIndex, SubSection.getStream()); + if ((Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) == 0 || + (Info.Flags & wasm::WASM_SYMBOL_EXPLICIT_NAME) != 0) + writeStringRef(Info.Name, SubSection.getStream()); + break; + case wasm::WASM_SYMBOL_TYPE_DATA: + writeStringRef(Info.Name, SubSection.getStream()); + if ((Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) == 0) { + encodeULEB128(Info.DataRef.Segment, SubSection.getStream()); + encodeULEB128(Info.DataRef.Offset, SubSection.getStream()); + encodeULEB128(Info.DataRef.Size, SubSection.getStream()); + } + break; + case wasm::WASM_SYMBOL_TYPE_SECTION: + encodeULEB128(Info.ElementIndex, SubSection.getStream()); + break; + default: + llvm_unreachable("unexpected kind"); + } + } + + SubSection.done(); + } + + // SEGMENT_NAMES subsection + if (Section.SegmentInfos.size()) { + writeUint8(OS, wasm::WASM_SEGMENT_INFO); + encodeULEB128(Section.SegmentInfos.size(), SubSection.getStream()); + for (const WasmYAML::SegmentInfo &SegmentInfo : Section.SegmentInfos) { + writeStringRef(SegmentInfo.Name, SubSection.getStream()); + encodeULEB128(SegmentInfo.Alignment, SubSection.getStream()); + encodeULEB128(SegmentInfo.Flags, SubSection.getStream()); + } + SubSection.done(); + } + + // INIT_FUNCS subsection + if (Section.InitFunctions.size()) { + writeUint8(OS, wasm::WASM_INIT_FUNCS); + encodeULEB128(Section.InitFunctions.size(), SubSection.getStream()); + for (const WasmYAML::InitFunction &Func : Section.InitFunctions) { + encodeULEB128(Func.Priority, SubSection.getStream()); + encodeULEB128(Func.Symbol, SubSection.getStream()); + } + SubSection.done(); + } + + // COMDAT_INFO subsection + if (Section.Comdats.size()) { + writeUint8(OS, wasm::WASM_COMDAT_INFO); + encodeULEB128(Section.Comdats.size(), SubSection.getStream()); + for (const auto &C : Section.Comdats) { + writeStringRef(C.Name, SubSection.getStream()); + encodeULEB128(0, SubSection.getStream()); // flags for future use + encodeULEB128(C.Entries.size(), SubSection.getStream()); + for (const WasmYAML::ComdatEntry &Entry : C.Entries) { + writeUint8(SubSection.getStream(), Entry.Kind); + encodeULEB128(Entry.Index, SubSection.getStream()); + } + } + SubSection.done(); + } +} + +void WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::NameSection &Section) { + writeStringRef(Section.Name, OS); + if (Section.FunctionNames.size()) { + writeUint8(OS, wasm::WASM_NAMES_FUNCTION); + + SubSectionWriter SubSection(OS); + + encodeULEB128(Section.FunctionNames.size(), SubSection.getStream()); + for (const WasmYAML::NameEntry &NameEntry : Section.FunctionNames) { + encodeULEB128(NameEntry.Index, SubSection.getStream()); + writeStringRef(NameEntry.Name, SubSection.getStream()); + } + + SubSection.done(); + } +} + +void WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::ProducersSection &Section) { + writeStringRef(Section.Name, OS); + int Fields = int(!Section.Languages.empty()) + int(!Section.Tools.empty()) + + int(!Section.SDKs.empty()); + if (Fields == 0) + return; + encodeULEB128(Fields, OS); + for (auto &Field : {std::make_pair(StringRef("language"), &Section.Languages), + std::make_pair(StringRef("processed-by"), &Section.Tools), + std::make_pair(StringRef("sdk"), &Section.SDKs)}) { + if (Field.second->empty()) + continue; + writeStringRef(Field.first, OS); + encodeULEB128(Field.second->size(), OS); + for (auto &Entry : *Field.second) { + writeStringRef(Entry.Name, OS); + writeStringRef(Entry.Version, OS); + } + } +} + +void WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::TargetFeaturesSection &Section) { + writeStringRef(Section.Name, OS); + encodeULEB128(Section.Features.size(), OS); + for (auto &E : Section.Features) { + writeUint8(OS, E.Prefix); + writeStringRef(E.Name, OS); + } +} + +void WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::CustomSection &Section) { + if (auto S = dyn_cast<WasmYAML::DylinkSection>(&Section)) { + writeSectionContent(OS, *S); + } else if (auto S = dyn_cast<WasmYAML::NameSection>(&Section)) { + writeSectionContent(OS, *S); + } else if (auto S = dyn_cast<WasmYAML::LinkingSection>(&Section)) { + writeSectionContent(OS, *S); + } else if (auto S = dyn_cast<WasmYAML::ProducersSection>(&Section)) { + writeSectionContent(OS, *S); + } else if (auto S = dyn_cast<WasmYAML::TargetFeaturesSection>(&Section)) { + writeSectionContent(OS, *S); + } else { + writeStringRef(Section.Name, OS); + Section.Payload.writeAsBinary(OS); + } +} + +void WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::TypeSection &Section) { + encodeULEB128(Section.Signatures.size(), OS); + uint32_t ExpectedIndex = 0; + for (const WasmYAML::Signature &Sig : Section.Signatures) { + if (Sig.Index != ExpectedIndex) { + reportError("unexpected type index: " + Twine(Sig.Index)); + return; + } + ++ExpectedIndex; + writeUint8(OS, Sig.Form); + encodeULEB128(Sig.ParamTypes.size(), OS); + for (auto ParamType : Sig.ParamTypes) + writeUint8(OS, ParamType); + encodeULEB128(Sig.ReturnTypes.size(), OS); + for (auto ReturnType : Sig.ReturnTypes) + writeUint8(OS, ReturnType); + } +} + +void WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::ImportSection &Section) { + encodeULEB128(Section.Imports.size(), OS); + for (const WasmYAML::Import &Import : Section.Imports) { + writeStringRef(Import.Module, OS); + writeStringRef(Import.Field, OS); + writeUint8(OS, Import.Kind); + switch (Import.Kind) { + case wasm::WASM_EXTERNAL_FUNCTION: + encodeULEB128(Import.SigIndex, OS); + NumImportedFunctions++; + break; + case wasm::WASM_EXTERNAL_GLOBAL: + writeUint8(OS, Import.GlobalImport.Type); + writeUint8(OS, Import.GlobalImport.Mutable); + NumImportedGlobals++; + break; + case wasm::WASM_EXTERNAL_EVENT: + writeUint32(OS, Import.EventImport.Attribute); + writeUint32(OS, Import.EventImport.SigIndex); + NumImportedGlobals++; + break; + case wasm::WASM_EXTERNAL_MEMORY: + writeLimits(Import.Memory, OS); + break; + case wasm::WASM_EXTERNAL_TABLE: + writeUint8(OS, Import.TableImport.ElemType); + writeLimits(Import.TableImport.TableLimits, OS); + break; + default: + reportError("unknown import type: " +Twine(Import.Kind)); + return; + } + } +} + +void WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::FunctionSection &Section) { + encodeULEB128(Section.FunctionTypes.size(), OS); + for (uint32_t FuncType : Section.FunctionTypes) + encodeULEB128(FuncType, OS); +} + +void WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::ExportSection &Section) { + encodeULEB128(Section.Exports.size(), OS); + for (const WasmYAML::Export &Export : Section.Exports) { + writeStringRef(Export.Name, OS); + writeUint8(OS, Export.Kind); + encodeULEB128(Export.Index, OS); + } +} + +void WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::StartSection &Section) { + encodeULEB128(Section.StartFunction, OS); +} + +void WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::TableSection &Section) { + encodeULEB128(Section.Tables.size(), OS); + for (auto &Table : Section.Tables) { + writeUint8(OS, Table.ElemType); + writeLimits(Table.TableLimits, OS); + } +} + +void WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::MemorySection &Section) { + encodeULEB128(Section.Memories.size(), OS); + for (const WasmYAML::Limits &Mem : Section.Memories) + writeLimits(Mem, OS); +} + +void WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::GlobalSection &Section) { + encodeULEB128(Section.Globals.size(), OS); + uint32_t ExpectedIndex = NumImportedGlobals; + for (auto &Global : Section.Globals) { + if (Global.Index != ExpectedIndex) { + reportError("unexpected global index: " + Twine(Global.Index)); + return; + } + ++ExpectedIndex; + writeUint8(OS, Global.Type); + writeUint8(OS, Global.Mutable); + writeInitExpr(OS, Global.InitExpr); + } +} + +void WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::EventSection &Section) { + encodeULEB128(Section.Events.size(), OS); + uint32_t ExpectedIndex = NumImportedEvents; + for (auto &Event : Section.Events) { + if (Event.Index != ExpectedIndex) { + reportError("unexpected event index: " + Twine(Event.Index)); + return; + } + ++ExpectedIndex; + encodeULEB128(Event.Attribute, OS); + encodeULEB128(Event.SigIndex, OS); + } +} + +void WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::ElemSection &Section) { + encodeULEB128(Section.Segments.size(), OS); + for (auto &Segment : Section.Segments) { + encodeULEB128(Segment.TableIndex, OS); + writeInitExpr(OS, Segment.Offset); + + encodeULEB128(Segment.Functions.size(), OS); + for (auto &Function : Segment.Functions) + encodeULEB128(Function, OS); + } +} + +void WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::CodeSection &Section) { + encodeULEB128(Section.Functions.size(), OS); + uint32_t ExpectedIndex = NumImportedFunctions; + for (auto &Func : Section.Functions) { + std::string OutString; + raw_string_ostream StringStream(OutString); + if (Func.Index != ExpectedIndex) { + reportError("unexpected function index: " + Twine(Func.Index)); + return; + } + ++ExpectedIndex; + + encodeULEB128(Func.Locals.size(), StringStream); + for (auto &LocalDecl : Func.Locals) { + encodeULEB128(LocalDecl.Count, StringStream); + writeUint8(StringStream, LocalDecl.Type); + } + + Func.Body.writeAsBinary(StringStream); + + // Write the section size followed by the content + StringStream.flush(); + encodeULEB128(OutString.size(), OS); + OS << OutString; + } +} + +void WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::DataSection &Section) { + encodeULEB128(Section.Segments.size(), OS); + for (auto &Segment : Section.Segments) { + encodeULEB128(Segment.InitFlags, OS); + if (Segment.InitFlags & wasm::WASM_SEGMENT_HAS_MEMINDEX) + encodeULEB128(Segment.MemoryIndex, OS); + if ((Segment.InitFlags & wasm::WASM_SEGMENT_IS_PASSIVE) == 0) + writeInitExpr(OS, Segment.Offset); + encodeULEB128(Segment.Content.binary_size(), OS); + Segment.Content.writeAsBinary(OS); + } +} + +void WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::DataCountSection &Section) { + encodeULEB128(Section.Count, OS); +} + +void WasmWriter::writeRelocSection(raw_ostream &OS, WasmYAML::Section &Sec, + uint32_t SectionIndex) { + switch (Sec.Type) { + case wasm::WASM_SEC_CODE: + writeStringRef("reloc.CODE", OS); + break; + case wasm::WASM_SEC_DATA: + writeStringRef("reloc.DATA", OS); + break; + case wasm::WASM_SEC_CUSTOM: { + auto *CustomSection = cast<WasmYAML::CustomSection>(&Sec); + writeStringRef(("reloc." + CustomSection->Name).str(), OS); + break; + } + default: + llvm_unreachable("not yet implemented"); + } + + encodeULEB128(SectionIndex, OS); + encodeULEB128(Sec.Relocations.size(), OS); + + for (auto Reloc : Sec.Relocations) { + writeUint8(OS, Reloc.Type); + encodeULEB128(Reloc.Offset, OS); + encodeULEB128(Reloc.Index, OS); + switch (Reloc.Type) { + case wasm::R_WASM_MEMORY_ADDR_LEB: + case wasm::R_WASM_MEMORY_ADDR_SLEB: + case wasm::R_WASM_MEMORY_ADDR_I32: + case wasm::R_WASM_FUNCTION_OFFSET_I32: + case wasm::R_WASM_SECTION_OFFSET_I32: + encodeULEB128(Reloc.Addend, OS); + } + } +} + +bool WasmWriter::writeWasm(raw_ostream &OS) { + // Write headers + OS.write(wasm::WasmMagic, sizeof(wasm::WasmMagic)); + writeUint32(OS, Obj.Header.Version); + + // Write each section + llvm::object::WasmSectionOrderChecker Checker; + for (const std::unique_ptr<WasmYAML::Section> &Sec : Obj.Sections) { + StringRef SecName = ""; + if (auto S = dyn_cast<WasmYAML::CustomSection>(Sec.get())) + SecName = S->Name; + if (!Checker.isValidSectionOrder(Sec->Type, SecName)) { + reportError("out of order section type: " + Twine(Sec->Type)); + return false; + } + encodeULEB128(Sec->Type, OS); + std::string OutString; + raw_string_ostream StringStream(OutString); + if (auto S = dyn_cast<WasmYAML::CustomSection>(Sec.get())) + writeSectionContent(StringStream, *S); + else if (auto S = dyn_cast<WasmYAML::TypeSection>(Sec.get())) + writeSectionContent(StringStream, *S); + else if (auto S = dyn_cast<WasmYAML::ImportSection>(Sec.get())) + writeSectionContent(StringStream, *S); + else if (auto S = dyn_cast<WasmYAML::FunctionSection>(Sec.get())) + writeSectionContent(StringStream, *S); + else if (auto S = dyn_cast<WasmYAML::TableSection>(Sec.get())) + writeSectionContent(StringStream, *S); + else if (auto S = dyn_cast<WasmYAML::MemorySection>(Sec.get())) + writeSectionContent(StringStream, *S); + else if (auto S = dyn_cast<WasmYAML::GlobalSection>(Sec.get())) + writeSectionContent(StringStream, *S); + else if (auto S = dyn_cast<WasmYAML::EventSection>(Sec.get())) + writeSectionContent(StringStream, *S); + else if (auto S = dyn_cast<WasmYAML::ExportSection>(Sec.get())) + writeSectionContent(StringStream, *S); + else if (auto S = dyn_cast<WasmYAML::StartSection>(Sec.get())) + writeSectionContent(StringStream, *S); + else if (auto S = dyn_cast<WasmYAML::ElemSection>(Sec.get())) + writeSectionContent(StringStream, *S); + else if (auto S = dyn_cast<WasmYAML::CodeSection>(Sec.get())) + writeSectionContent(StringStream, *S); + else if (auto S = dyn_cast<WasmYAML::DataSection>(Sec.get())) + writeSectionContent(StringStream, *S); + else if (auto S = dyn_cast<WasmYAML::DataCountSection>(Sec.get())) + writeSectionContent(StringStream, *S); + else + reportError("unknown section type: " + Twine(Sec->Type)); + + if (HasError) + return false; + + StringStream.flush(); + + // Write the section size followed by the content + encodeULEB128(OutString.size(), OS); + OS << OutString; + } + + // write reloc sections for any section that have relocations + uint32_t SectionIndex = 0; + for (const std::unique_ptr<WasmYAML::Section> &Sec : Obj.Sections) { + if (Sec->Relocations.empty()) { + SectionIndex++; + continue; + } + + writeUint8(OS, wasm::WASM_SEC_CUSTOM); + std::string OutString; + raw_string_ostream StringStream(OutString); + writeRelocSection(StringStream, *Sec, SectionIndex++); + StringStream.flush(); + + encodeULEB128(OutString.size(), OS); + OS << OutString; + } + + return true; +} + +namespace llvm { +namespace yaml { + +bool yaml2wasm(WasmYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH) { + WasmWriter Writer(Doc, EH); + return Writer.writeWasm(Out); +} + +} // namespace yaml +} // namespace llvm diff --git a/lib/ObjectYAML/WasmYAML.cpp b/lib/ObjectYAML/WasmYAML.cpp index 88491d955c49..232d5122004a 100644 --- a/lib/ObjectYAML/WasmYAML.cpp +++ b/lib/ObjectYAML/WasmYAML.cpp @@ -295,8 +295,8 @@ void ScalarEnumerationTraits<WasmYAML::SectionType>::enumeration( void MappingTraits<WasmYAML::Signature>::mapping( IO &IO, WasmYAML::Signature &Signature) { IO.mapRequired("Index", Signature.Index); - IO.mapRequired("ReturnType", Signature.ReturnType); IO.mapRequired("ParamTypes", Signature.ParamTypes); + IO.mapRequired("ReturnTypes", Signature.ReturnTypes); } void MappingTraits<WasmYAML::Table>::mapping(IO &IO, WasmYAML::Table &Table) { @@ -535,6 +535,7 @@ void ScalarBitSetTraits<WasmYAML::SymbolFlags>::bitset( BCaseMask(UNDEFINED, UNDEFINED); BCaseMask(EXPORTED, EXPORTED); BCaseMask(EXPLICIT_NAME, EXPLICIT_NAME); + BCaseMask(NO_STRIP, NO_STRIP); #undef BCaseMask } @@ -559,7 +560,6 @@ void ScalarEnumerationTraits<WasmYAML::ValueType>::enumeration( ECase(V128); ECase(FUNCREF); ECase(FUNC); - ECase(NORESULT); #undef ECase } diff --git a/lib/ObjectYAML/yaml2obj.cpp b/lib/ObjectYAML/yaml2obj.cpp new file mode 100644 index 000000000000..c18fa5cfdb5e --- /dev/null +++ b/lib/ObjectYAML/yaml2obj.cpp @@ -0,0 +1,77 @@ +//===-- yaml2obj.cpp ------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/ObjectYAML/yaml2obj.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/ObjectYAML/ObjectYAML.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/YAMLTraits.h" + +namespace llvm { +namespace yaml { + +bool convertYAML(yaml::Input &YIn, raw_ostream &Out, ErrorHandler ErrHandler, + unsigned DocNum) { + unsigned CurDocNum = 0; + do { + if (++CurDocNum != DocNum) + continue; + + yaml::YamlObjectFile Doc; + YIn >> Doc; + if (std::error_code EC = YIn.error()) { + ErrHandler("failed to parse YAML input: " + EC.message()); + return false; + } + + if (Doc.Elf) + return yaml2elf(*Doc.Elf, Out, ErrHandler); + if (Doc.Coff) + return yaml2coff(*Doc.Coff, Out, ErrHandler); + if (Doc.MachO || Doc.FatMachO) + return yaml2macho(Doc, Out, ErrHandler); + if (Doc.Minidump) + return yaml2minidump(*Doc.Minidump, Out, ErrHandler); + if (Doc.Wasm) + return yaml2wasm(*Doc.Wasm, Out, ErrHandler); + + ErrHandler("unknown document type"); + return false; + + } while (YIn.nextDocument()); + + ErrHandler("cannot find the " + Twine(DocNum) + + getOrdinalSuffix(DocNum).data() + " document"); + return false; +} + +std::unique_ptr<object::ObjectFile> +yaml2ObjectFile(SmallVectorImpl<char> &Storage, StringRef Yaml, + ErrorHandler ErrHandler) { + Storage.clear(); + raw_svector_ostream OS(Storage); + + yaml::Input YIn(Yaml); + if (!convertYAML(YIn, OS, ErrHandler)) + return {}; + + Expected<std::unique_ptr<object::ObjectFile>> ObjOrErr = + object::ObjectFile::createObjectFile( + MemoryBufferRef(OS.str(), "YamlObject")); + if (ObjOrErr) + return std::move(*ObjOrErr); + + ErrHandler(toString(ObjOrErr.takeError())); + return {}; +} + +} // namespace yaml +} // namespace llvm |