Index: llvm/trunk/include/llvm/ObjectYAML/yaml2obj.h =================================================================== --- llvm/trunk/include/llvm/ObjectYAML/yaml2obj.h +++ llvm/trunk/include/llvm/ObjectYAML/yaml2obj.h @@ -0,0 +1,62 @@ +//===--- yaml2obj.h - -------------------------------------------*- C++ -*-===// +// +// 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 +/// Common declarations for yaml2obj +//===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_YAML2OBJ_YAML2OBJ_H +#define LLVM_TOOLS_YAML2OBJ_YAML2OBJ_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include + +namespace llvm { +class raw_ostream; +template class SmallVectorImpl; +template class Expected; + +namespace object { +class ObjectFile; +} + +namespace COFFYAML { +struct Object; +} + +namespace ELFYAML { +struct Object; +} + +namespace MinidumpYAML { +struct Object; +} + +namespace WasmYAML { +struct Object; +} + +namespace yaml { +class Input; +struct YamlObjectFile; + +int yaml2coff(COFFYAML::Object &Doc, raw_ostream &Out); +int yaml2elf(ELFYAML::Object &Doc, raw_ostream &Out); +int yaml2macho(YamlObjectFile &Doc, raw_ostream &Out); +int yaml2minidump(MinidumpYAML::Object &Doc, raw_ostream &Out); +int yaml2wasm(WasmYAML::Object &Doc, raw_ostream &Out); + +Error convertYAML(Input &YIn, raw_ostream &Out, unsigned DocNum = 1); + +/// Convenience function for tests. +Expected> +yaml2ObjectFile(SmallVectorImpl &Storage, StringRef Yaml); + +} // namespace yaml +} // namespace llvm + +#endif Index: llvm/trunk/lib/ObjectYAML/CMakeLists.txt =================================================================== --- llvm/trunk/lib/ObjectYAML/CMakeLists.txt +++ llvm/trunk/lib/ObjectYAML/CMakeLists.txt @@ -3,15 +3,24 @@ CodeViewYAMLSymbols.cpp CodeViewYAMLTypeHashing.cpp CodeViewYAMLTypes.cpp + COFFEmitter.cpp COFFYAML.cpp DWARFEmitter.cpp DWARFVisitor.cpp DWARFYAML.cpp + ELFEmitter.cpp ELFYAML.cpp + MachOEmitter.cpp MachOYAML.cpp ObjectYAML.cpp + MinidumpEmitter.cpp MinidumpYAML.cpp + WasmEmitter.cpp WasmYAML.cpp XCOFFYAML.cpp YAML.cpp + yaml2obj.cpp + + ADDITIONAL_HEADER_DIRS + ${LLVM_MAIN_INCLUDE_DIR}/llvm/ObjectYAML ) Index: llvm/trunk/lib/ObjectYAML/COFFEmitter.cpp =================================================================== --- llvm/trunk/lib/ObjectYAML/COFFEmitter.cpp +++ llvm/trunk/lib/ObjectYAML/COFFEmitter.cpp @@ -0,0 +1,619 @@ +//===- 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 + +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) + : Obj(Obj), SectionTableStart(0), SectionTableSize(0) { + // 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(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::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) { + errs() << "String table got too large\n"; + return false; + } + Sec.Header.Name[0] = '/'; + std::copy(str.begin(), str.end(), Sec.Header.Name + 1); + } + + if (Sec.Alignment) { + if (Sec.Alignment > 8192) { + errs() << "Section alignment is too large\n"; + return false; + } + if (!isPowerOf2_32(Sec.Alignment)) { + errs() << "Section alignment is not a power of 2\n"; + return false; + } + Sec.Header.Characteristics |= (Log2_32(Sec.Alignment) + 1) << 20; + } + } + return true; + } + + bool parseSymbols() { + for (std::vector::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(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::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 StringTableMap; + std::string StringTable; + uint32_t SectionTableStart; + uint32_t SectionTableSize; +}; + +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 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 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(Size); + MutableArrayRef Output(Buffer, Size); + BinaryStreamWriter Writer(Output, support::little); + + Err(Writer.writeInteger(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::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(&CP.StringTable[0]) = + CP.StringTable.size(); + + return true; +} + +template struct binary_le_impl { + value_type Value; + binary_le_impl(value_type V) : Value(V) {} +}; + +template +raw_ostream &operator<<(raw_ostream &OS, + const binary_le_impl &BLE) { + char Buffer[sizeof(BLE.Value)]; + support::endian::write( + Buffer, BLE.Value); + OS.write(Buffer, sizeof(BLE.Value)); + return OS; +} + +template +binary_le_impl binary_le(value_type V) { + return binary_le_impl(V); +} + +template struct zeros_impl {}; + +template +raw_ostream &operator<<(raw_ostream &OS, const zeros_impl &) { + char Buffer[NumBytes]; + memset(Buffer, 0, sizeof(Buffer)); + OS.write(Buffer, sizeof(Buffer)); + return OS; +} + +template zeros_impl zeros(const T &) { + return zeros_impl(); +} + +template +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(&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(COFF::IMAGE_FILE_MACHINE_UNKNOWN)) + << binary_le(static_cast(0xffff)) + << binary_le( + static_cast(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(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(&PEH), sizeof(PEH)); + } else { + object::pe32_header PEH; + uint32_t BaseOfData = + initializeOptionalHeader(CP, COFF::PE32Header::PE32, &PEH); + PEH.BaseOfData = BaseOfData; + OS.write(reinterpret_cast(&PEH), sizeof(PEH)); + } + for (const Optional &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::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 SymbolTableIndexMap; + for (std::vector::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::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(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(i->SectionDefinition->Number)) + << binary_le(i->SectionDefinition->Selection) + << zeros(i->SectionDefinition->unused) + << binary_le(static_cast(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 { + +int yaml2coff(llvm::COFFYAML::Object &Doc, raw_ostream &Out) { + COFFParser CP(Doc); + if (!CP.parse()) { + errs() << "yaml2obj: Failed to parse YAML file!\n"; + return 1; + } + + if (!layoutOptionalHeader(CP)) { + errs() << "yaml2obj: Failed to layout optional header for COFF file!\n"; + return 1; + } + + if (!layoutCOFF(CP)) { + errs() << "yaml2obj: Failed to layout COFF file!\n"; + return 1; + } + if (!writeCOFF(CP, Out)) { + errs() << "yaml2obj: Failed to write COFF file!\n"; + return 1; + } + return 0; +} + +} // namespace yaml +} // namespace llvm Index: llvm/trunk/lib/ObjectYAML/ELFEmitter.cpp =================================================================== --- llvm/trunk/lib/ObjectYAML/ELFEmitter.cpp +++ llvm/trunk/lib/ObjectYAML/ELFEmitter.cpp @@ -0,0 +1,1083 @@ +//===- 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/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 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 + 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 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 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; + ELFYAML::Object &Doc; + + bool buildSectionIndex(); + bool buildSymbolIndex(ArrayRef Symbols); + void initELFHeader(Elf_Ehdr &Header); + void initProgramHeaders(std::vector &PHeaders); + bool initImplicitHeader(ELFState &State, ContiguousBlobAccumulator &CBA, + Elf_Shdr &Header, StringRef SecName, + ELFYAML::Section *YAMLSec); + bool initSectionHeaders(ELFState &State, + std::vector &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 &PHeaders, + std::vector &SHeaders); + bool writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::RawContentSection &Section, + ContiguousBlobAccumulator &CBA); + bool writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::RelocationSection &Section, + ContiguousBlobAccumulator &CBA); + bool writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::Group &Group, + ContiguousBlobAccumulator &CBA); + bool writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::SymverSection &Section, + ContiguousBlobAccumulator &CBA); + bool writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::VerneedSection &Section, + ContiguousBlobAccumulator &CBA); + bool writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::VerdefSection &Section, + ContiguousBlobAccumulator &CBA); + bool writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::MipsABIFlags &Section, + ContiguousBlobAccumulator &CBA); + bool writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::DynamicSection &Section, + ContiguousBlobAccumulator &CBA); + ELFState(ELFYAML::Object &D); + +public: + static int writeELF(raw_ostream &OS, ELFYAML::Object &Doc); + +private: + void finalizeStrings(); +}; +} // end anonymous namespace + +template static size_t arrayDataSize(ArrayRef A) { + return A.size() * sizeof(T); +} + +template static void writeArrayData(raw_ostream &OS, ArrayRef A) { + OS.write((const char *)A.data(), arrayDataSize(A)); +} + +template static void zero(T &Obj) { memset(&Obj, 0, sizeof(Obj)); } + +template ELFState::ELFState(ELFYAML::Object &D) : Doc(D) { + StringSet<> DocSections; + for (std::unique_ptr &D : Doc.Sections) + if (!D->Name.empty()) + DocSections.insert(D->Name); + + // 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(), + llvm::make_unique( + ELFYAML::Section::SectionKind::RawContent, /*IsImplicit=*/true)); + + std::vector ImplicitSections = {".symtab", ".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 Sec = llvm::make_unique( + ELFYAML::Section::SectionKind::RawContent, true /*IsImplicit*/); + Sec->Name = SecName; + Doc.Sections.push_back(std::move(Sec)); + } +} + +template void ELFState::initELFHeader(Elf_Ehdr &Header) { + using namespace llvm::ELF; + 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 = sizeof(Header); + Header.e_flags = Doc.Header.Flags; + Header.e_ehsize = sizeof(Elf_Ehdr); + Header.e_phentsize = sizeof(Elf_Phdr); + 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. + Header.e_shoff = + Doc.Header.SHOffset + ? (typename ELFT::uint)(*Doc.Header.SHOffset) + : sizeof(Header) + sizeof(Elf_Phdr) * Doc.ProgramHeaders.size(); + 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"); +} + +template +void ELFState::initProgramHeaders(std::vector &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); + } +} + +static bool convertSectionIndex(NameToIdxMap &SN2I, StringRef SecName, + StringRef IndexSrc, unsigned &IndexDest) { + if (!SN2I.lookup(IndexSrc, IndexDest) && !to_integer(IndexSrc, IndexDest)) { + WithColor::error() << "Unknown section referenced: '" << IndexSrc + << "' at YAML section '" << SecName << "'.\n"; + return false; + } + return true; +} + +template +bool ELFState::initImplicitHeader(ELFState &State, + 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") + State.initSymtabSectionHeader(Header, SymtabType::Static, CBA, YAMLSec); + else if (SecName == ".strtab") + State.initStrtabSectionHeader(Header, SecName, State.DotStrtab, CBA, + YAMLSec); + else if (SecName == ".shstrtab") + State.initStrtabSectionHeader(Header, SecName, State.DotShStrtab, CBA, + YAMLSec); + + else if (SecName == ".dynsym") + State.initSymtabSectionHeader(Header, SymtabType::Dynamic, CBA, YAMLSec); + else if (SecName == ".dynstr") + State.initStrtabSectionHeader(Header, SecName, State.DotDynstr, CBA, + YAMLSec); + else + return false; + + // Override the sh_offset/sh_size fields if requested. + if (YAMLSec) { + if (YAMLSec->ShOffset) + Header.sh_offset = *YAMLSec->ShOffset; + if (YAMLSec->ShSize) + Header.sh_size = *YAMLSec->ShSize; + } + + return true; +} + +static StringRef dropUniqueSuffix(StringRef S) { + size_t SuffixPos = S.rfind(" ["); + if (SuffixPos == StringRef::npos) + return S; + return S.substr(0, SuffixPos); +} + +template +bool ELFState::initSectionHeaders(ELFState &State, + std::vector &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(State, 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(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()) { + unsigned Index; + if (!convertSectionIndex(SN2I, Sec->Name, Sec->Link, Index)) + return false; + SHeader.sh_link = Index; + } + + if (I == 0) { + if (auto RawSec = dyn_cast(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(Sec)) { + if (!writeSectionContent(SHeader, *S, CBA)) + return false; + } else if (auto S = dyn_cast(Sec)) { + if (!writeSectionContent(SHeader, *S, CBA)) + return false; + } else if (auto S = dyn_cast(Sec)) { + if (!writeSectionContent(SHeader, *S, CBA)) + return false; + } else if (auto S = dyn_cast(Sec)) { + if (!writeSectionContent(SHeader, *S, CBA)) + return false; + } else if (auto S = dyn_cast(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(Sec)) { + if (!writeSectionContent(SHeader, *S, CBA)) + return false; + } else if (auto S = dyn_cast(Sec)) { + if (!writeSectionContent(SHeader, *S, CBA)) + return false; + } else if (auto S = dyn_cast(Sec)) { + if (!writeSectionContent(SHeader, *S, CBA)) + return false; + } else if (auto S = dyn_cast(Sec)) { + if (!writeSectionContent(SHeader, *S, CBA)) + return false; + } else + llvm_unreachable("Unknown section type"); + + // Override the sh_offset/sh_size fields if requested. + if (Sec) { + if (Sec->ShOffset) + SHeader.sh_offset = *Sec->ShOffset; + if (Sec->ShSize) + SHeader.sh_size = *Sec->ShSize; + } + } + + return true; +} + +static size_t findFirstNonGlobal(ArrayRef 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 writeRawSectionData(raw_ostream &OS, + const ELFYAML::RawContentSection &RawSec) { + size_t ContentSize = 0; + if (RawSec.Content) { + RawSec.Content->writeAsBinary(OS); + ContentSize = RawSec.Content->binary_size(); + } + + if (!RawSec.Size) + return ContentSize; + + OS.write_zeros(*RawSec.Size - ContentSize); + return *RawSec.Size; +} + +template +static std::vector +toELFSymbols(NameToIdxMap &SN2I, ArrayRef Symbols, + const StringTableBuilder &Strtab) { + using Elf_Sym = typename ELFT::Sym; + + std::vector 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(dropUniqueSuffix(Sym.Name)); + + Symbol.setBindingAndType(Sym.Binding, Sym.Type); + if (!Sym.Section.empty()) { + unsigned Index; + if (!SN2I.lookup(Sym.Section, Index)) { + WithColor::error() << "Unknown section referenced: '" << Sym.Section + << "' by YAML symbol " << Sym.Name << ".\n"; + exit(1); + } + Symbol.st_shndx = Index; + } else if (Sym.Index) { + Symbol.st_shndx = *Sym.Index; + } + // else Symbol.st_shndex == SHN_UNDEF (== 0), since it was zero'd earlier. + Symbol.st_value = Sym.Value; + Symbol.st_other = Sym.Other; + Symbol.st_size = Sym.Size; + } + + return Ret; +} + +template +void ELFState::initSymtabSectionHeader(Elf_Shdr &SHeader, + SymtabType STType, + ContiguousBlobAccumulator &CBA, + ELFYAML::Section *YAMLSec) { + + bool IsStatic = STType == SymtabType::Static; + const auto &Symbols = IsStatic ? Doc.Symbols : Doc.DynamicSymbols; + + ELFYAML::RawContentSection *RawSec = + dyn_cast_or_null(YAMLSec); + if (RawSec && !Symbols.empty() && (RawSec->Content || RawSec->Size)) { + if (RawSec->Content) + WithColor::error() << "Cannot specify both `Content` and " + + (IsStatic ? Twine("`Symbols`") + : Twine("`DynamicSymbols`")) + + " for symbol table section '" + << RawSec->Name << "'.\n"; + if (RawSec->Size) + WithColor::error() << "Cannot specify both `Size` and " + + (IsStatic ? Twine("`Symbols`") + : Twine("`DynamicSymbols`")) + + " for symbol table section '" + << RawSec->Name << "'.\n"; + exit(1); + } + + 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. + unsigned Index; + if (!convertSectionIndex(SN2I, RawSec->Name, RawSec->Link, Index)) + return; + SHeader.sh_link = Index; + } 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 = writeRawSectionData(OS, *RawSec); + return; + } + + std::vector Syms = + toELFSymbols(SN2I, Symbols, IsStatic ? DotStrtab : DotDynstr); + writeArrayData(OS, makeArrayRef(Syms)); + SHeader.sh_size = arrayDataSize(makeArrayRef(Syms)); +} + +template +void ELFState::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(YAMLSec); + + auto &OS = CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + if (RawSec && (RawSec->Content || RawSec->Size)) { + SHeader.sh_size = writeRawSectionData(OS, *RawSec); + } 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 +void ELFState::setProgramHeaderLayout(std::vector &PHeaders, + std::vector &SHeaders) { + uint32_t PhdrIdx = 0; + for (auto &YamlPhdr : Doc.ProgramHeaders) { + Elf_Phdr &PHeader = PHeaders[PhdrIdx++]; + + std::vector Sections; + for (const ELFYAML::SectionName &SecName : YamlPhdr.Sections) { + unsigned Index; + if (!SN2I.lookup(SecName.Section, Index)) { + WithColor::error() << "Unknown section referenced: '" << SecName.Section + << "' by program header.\n"; + exit(1); + } + 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, + // if not set explicitly. + if (YamlPhdr.FileSize) { + PHeader.p_filesz = *YamlPhdr.FileSize; + } else { + PHeader.p_filesz = 0; + for (Elf_Shdr *SHeader : Sections) { + uint64_t EndOfSection; + if (SHeader->sh_type == llvm::ELF::SHT_NOBITS) + EndOfSection = SHeader->sh_offset; + else + EndOfSection = SHeader->sh_offset + SHeader->sh_size; + uint64_t EndOfSegment = PHeader.p_offset + PHeader.p_filesz; + EndOfSegment = std::max(EndOfSegment, EndOfSection); + PHeader.p_filesz = EndOfSegment - PHeader.p_offset; + } + } + + // If not set explicitly, find the memory size by adding the size of + // sections at the end of the segment. These should be empty (size of zero) + // and NOBITS sections. + if (YamlPhdr.MemSize) { + PHeader.p_memsz = *YamlPhdr.MemSize; + } else { + PHeader.p_memsz = PHeader.p_filesz; + for (Elf_Shdr *SHeader : Sections) + if (SHeader->sh_offset == PHeader.p_offset + PHeader.p_filesz) + PHeader.p_memsz += SHeader->sh_size; + } + + // Set the alignment of the segment to be the same as the maximum alignment + // of the sections with the same offset so that by default the segment + // has a valid and sensible alignment. + if (YamlPhdr.Align) { + PHeader.p_align = *YamlPhdr.Align; + } else { + PHeader.p_align = 1; + for (Elf_Shdr *SHeader : Sections) + if (SHeader->sh_offset == PHeader.p_offset) + PHeader.p_align = std::max(PHeader.p_align, SHeader->sh_addralign); + } + } +} + +template +bool ELFState::writeSectionContent( + Elf_Shdr &SHeader, const ELFYAML::RawContentSection &Section, + ContiguousBlobAccumulator &CBA) { + raw_ostream &OS = + CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + SHeader.sh_size = writeRawSectionData(OS, Section); + + 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; + + return true; +} + +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 +bool ELFState::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"); + + unsigned Index = 0; + if (!Section.RelocatableSec.empty() && + !convertSectionIndex(SN2I, Section.Name, Section.RelocatableSec, Index)) + return false; + SHeader.sh_info = Index; + + auto &OS = CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + + for (const auto &Rel : Section.Relocations) { + unsigned SymIdx = 0; + // If a relocation references a symbol, try to look one up in the symbol + // table. If it is not there, treat the value as a symbol index. + if (Rel.Symbol && !SymN2I.lookup(*Rel.Symbol, SymIdx) && + !to_integer(*Rel.Symbol, SymIdx)) { + WithColor::error() << "Unknown symbol referenced: '" << *Rel.Symbol + << "' at YAML section '" << Section.Name << "'.\n"; + return false; + } + + 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)); + } + } + return true; +} + +template +bool ELFState::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(); + + unsigned SymIdx; + if (!SymN2I.lookup(Section.Signature, SymIdx) && + !to_integer(Section.Signature, SymIdx)) { + WithColor::error() << "Unknown symbol referenced: '" << Section.Signature + << "' at YAML section '" << Section.Name << "'.\n"; + return false; + } + SHeader.sh_info = SymIdx; + + 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 if (!convertSectionIndex(SN2I, Section.Name, Member.sectionNameOrType, + SectionIndex)) + return false; + support::endian::write(OS, SectionIndex, ELFT::TargetEndianness); + } + return true; +} + +template +bool ELFState::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(OS, Version, ELFT::TargetEndianness); + + SHeader.sh_entsize = Section.EntSize ? (uint64_t)*Section.EntSize : 2; + SHeader.sh_size = Section.Entries.size() * SHeader.sh_entsize; + return true; +} + +template +bool ELFState::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; + + return true; +} + +template +bool ELFState::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; + + return true; +} + +template +bool ELFState::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 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)); + + return true; +} + +template +bool ELFState::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) { + WithColor::error() + << "Cannot specify both raw content and explicit entries " + "for dynamic section '" + << Section.Name << "'.\n"; + return false; + } + + 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(OS, DE.Tag, ELFT::TargetEndianness); + support::endian::write(OS, DE.Val, ELFT::TargetEndianness); + } + if (Section.Content) + Section.Content->writeAsBinary(OS); + + return true; +} + +template bool ELFState::buildSectionIndex() { + for (unsigned I = 0, E = Doc.Sections.size(); I != E; ++I) { + StringRef Name = Doc.Sections[I]->Name; + if (Name.empty()) + continue; + + DotShStrtab.add(dropUniqueSuffix(Name)); + if (!SN2I.addName(Name, I)) { + WithColor::error() << "Repeated section name: '" << Name + << "' at YAML section number " << I << ".\n"; + return false; + } + } + + DotShStrtab.finalize(); + return true; +} + +template +bool ELFState::buildSymbolIndex(ArrayRef Symbols) { + bool GlobalSymbolSeen = false; + std::size_t I = 0; + for (const auto &Sym : Symbols) { + ++I; + + StringRef Name = Sym.Name; + if (Sym.Binding.value == ELF::STB_LOCAL && GlobalSymbolSeen) { + WithColor::error() << "Local symbol '" + Name + + "' after global in Symbols list.\n"; + return false; + } + if (Sym.Binding.value != ELF::STB_LOCAL) + GlobalSymbolSeen = true; + + if (!Name.empty() && !SymN2I.addName(Name, I)) { + WithColor::error() << "Repeated symbol name: '" << Name << "'.\n"; + return false; + } + } + return true; +} + +template void ELFState::finalizeStrings() { + // Add the regular symbol names to .strtab section. + for (const ELFYAML::Symbol &Sym : Doc.Symbols) + DotStrtab.add(dropUniqueSuffix(Sym.Name)); + DotStrtab.finalize(); + + // Add the dynamic symbol names to .dynstr section. + for (const ELFYAML::Symbol &Sym : Doc.DynamicSymbols) + DotDynstr.add(dropUniqueSuffix(Sym.Name)); + + // SHT_GNU_verdef and SHT_GNU_verneed sections might also + // add strings to .dynstr section. + for (const std::unique_ptr &Sec : Doc.Sections) { + if (auto VerNeed = dyn_cast(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(Sec.get())) { + for (const ELFYAML::VerdefEntry &E : VerDef->Entries) + for (StringRef Name : E.VerNames) + DotDynstr.add(Name); + } + } + + DotDynstr.finalize(); +} + +template +int ELFState::writeELF(raw_ostream &OS, ELFYAML::Object &Doc) { + ELFState State(Doc); + + // 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(); + + if (!State.buildSectionIndex()) + return 1; + + if (!State.buildSymbolIndex(Doc.Symbols)) + return 1; + + Elf_Ehdr Header; + State.initELFHeader(Header); + + // TODO: Flesh out section header support. + + std::vector PHeaders; + State.initProgramHeaders(PHeaders); + + // XXX: This offset is tightly coupled with the order that we write + // things to `OS`. + const size_t SectionContentBeginOffset = Header.e_ehsize + + Header.e_phentsize * Header.e_phnum + + Header.e_shentsize * Header.e_shnum; + ContiguousBlobAccumulator CBA(SectionContentBeginOffset); + + std::vector SHeaders; + if (!State.initSectionHeaders(State, SHeaders, CBA)) + return 1; + + // Now we can decide segment offsets + State.setProgramHeaderLayout(PHeaders, SHeaders); + + OS.write((const char *)&Header, sizeof(Header)); + writeArrayData(OS, makeArrayRef(PHeaders)); + writeArrayData(OS, makeArrayRef(SHeaders)); + CBA.writeBlobToStream(OS); + return 0; +} + +namespace llvm { +namespace yaml { + +int yaml2elf(llvm::ELFYAML::Object &Doc, raw_ostream &Out) { + 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::writeELF(Out, Doc); + return ELFState::writeELF(Out, Doc); + } + if (IsLE) + return ELFState::writeELF(Out, Doc); + return ELFState::writeELF(Out, Doc); +} + +} // namespace yaml +} // namespace llvm Index: llvm/trunk/lib/ObjectYAML/LLVMBuild.txt =================================================================== --- llvm/trunk/lib/ObjectYAML/LLVMBuild.txt +++ llvm/trunk/lib/ObjectYAML/LLVMBuild.txt @@ -10,4 +10,4 @@ type = Library name = ObjectYAML parent = Libraries -required_libraries = Object Support DebugInfoCodeView +required_libraries = Object Support DebugInfoCodeView MC Index: llvm/trunk/lib/ObjectYAML/MachOEmitter.cpp =================================================================== --- llvm/trunk/lib/ObjectYAML/MachOEmitter.cpp +++ llvm/trunk/lib/ObjectYAML/MachOEmitter.cpp @@ -0,0 +1,608 @@ +//===- 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/Error.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(&Header), 0, sizeof(MachO::mach_header_64)); + } + + Error writeMachO(raw_ostream &OS); + +private: + Error writeHeader(raw_ostream &OS); + Error writeLoadCommands(raw_ostream &OS); + Error writeSectionData(raw_ostream &OS); + Error writeLinkEditData(raw_ostream &OS); + + void writeBindOpcodes(raw_ostream &OS, + std::vector &BindOpcodes); + // LinkEdit writers + Error writeRebaseOpcodes(raw_ostream &OS); + Error writeBasicBindOpcodes(raw_ostream &OS); + Error writeWeakBindOpcodes(raw_ostream &OS); + Error writeLazyBindOpcodes(raw_ostream &OS); + Error writeNameList(raw_ostream &OS); + Error writeStringTable(raw_ostream &OS); + Error 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; +}; + +Error MachOWriter::writeMachO(raw_ostream &OS) { + fileStart = OS.tell(); + if (auto Err = writeHeader(OS)) + return Err; + if (auto Err = writeLoadCommands(OS)) + return Err; + if (auto Err = writeSectionData(OS)) + return Err; + return Error::success(); +} + +Error 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); + + return Error::success(); +} + +template +SectionType constructSection(MachOYAML::Section Sec) { + SectionType TempSec; + memcpy(reinterpret_cast(&TempSec.sectname[0]), &Sec.sectname[0], 16); + memcpy(reinterpret_cast(&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 +size_t writeLoadCommandData(MachOYAML::LoadCommand &LC, raw_ostream &OS, + bool IsLittleEndian) { + return 0; +} + +template <> +size_t writeLoadCommandData(MachOYAML::LoadCommand &LC, + raw_ostream &OS, + bool IsLittleEndian) { + size_t BytesWritten = 0; + for (const auto &Sec : LC.Sections) { + auto TempSec = constructSection(Sec); + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(TempSec); + OS.write(reinterpret_cast(&(TempSec)), + sizeof(MachO::section)); + BytesWritten += sizeof(MachO::section); + } + return BytesWritten; +} + +template <> +size_t writeLoadCommandData( + MachOYAML::LoadCommand &LC, raw_ostream &OS, bool IsLittleEndian) { + size_t BytesWritten = 0; + for (const auto &Sec : LC.Sections) { + auto TempSec = constructSection(Sec); + TempSec.reserved3 = Sec.reserved3; + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(TempSec); + OS.write(reinterpret_cast(&(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(MachOYAML::LoadCommand &LC, + raw_ostream &OS, + bool IsLittleEndian) { + return writePayloadString(LC, OS); +} + +template <> +size_t writeLoadCommandData(MachOYAML::LoadCommand &LC, + raw_ostream &OS, + bool IsLittleEndian) { + return writePayloadString(LC, OS); +} + +template <> +size_t writeLoadCommandData(MachOYAML::LoadCommand &LC, + raw_ostream &OS, + bool IsLittleEndian) { + return writePayloadString(LC, OS); +} + +template <> +size_t writeLoadCommandData( + 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(&tool), + sizeof(MachO::build_tool_version)); + BytesWritten += sizeof(MachO::build_tool_version); + } + return BytesWritten; +} + +void ZeroFillBytes(raw_ostream &OS, size_t Size) { + std::vector FillData; + FillData.insert(FillData.begin(), Size, 0); + OS.write(reinterpret_cast(FillData.data()), Size); +} + +void Fill(raw_ostream &OS, size_t Size, uint32_t Data) { + std::vector FillData; + FillData.insert(FillData.begin(), (Size / 4) + 1, Data); + OS.write(reinterpret_cast(FillData.data()), Size); +} + +void MachOWriter::ZeroToOffset(raw_ostream &OS, size_t Offset) { + auto currOffset = OS.tell() - fileStart; + if (currOffset < Offset) + ZeroFillBytes(OS, Offset - currOffset); +} + +Error 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(&(Data.LCStruct##_data)), \ + sizeof(MachO::LCStruct)); \ + BytesWritten = sizeof(MachO::LCStruct); \ + BytesWritten += \ + writeLoadCommandData(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(&(Data.load_command_data)), + sizeof(MachO::load_command)); + BytesWritten = sizeof(MachO::load_command); + BytesWritten += + writeLoadCommandData(LC, OS, Obj.IsLittleEndian); + break; +#include "llvm/BinaryFormat/MachO.def" + } + + if (LC.PayloadBytes.size() > 0) { + OS.write(reinterpret_cast(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); + } + } + return Error::success(); +} + +static bool isVirtualSection(uint8_t type) { + return (type == MachO::S_ZEROFILL || type == MachO::S_GB_ZEROFILL || + type == MachO::S_THREAD_LOCAL_ZEROFILL); +} + +Error 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; + if (auto Err = writeLinkEditData(OS)) + return Err; + } + 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 (isVirtualSection(Sec.flags & MachO::SECTION_TYPE)) + continue; + + // 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) { + if (auto Err = writeLinkEditData(OS)) + return Err; + } + return Error::success(); +} + +void MachOWriter::writeBindOpcodes( + raw_ostream &OS, std::vector &BindOpcodes) { + + for (auto Opcode : BindOpcodes) { + uint8_t OpByte = Opcode.Opcode | Opcode.Imm; + OS.write(reinterpret_cast(&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(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); +} + +Error MachOWriter::writeExportTrie(raw_ostream &OS) { + dumpExportEntry(OS, Obj.LinkEdit.ExportTrie); + return Error::success(); +} + +template +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(&ListEntry), sizeof(NListType)); +} + +Error MachOWriter::writeLinkEditData(raw_ostream &OS) { + typedef Error (MachOWriter::*writeHandler)(raw_ostream &); + typedef std::pair writeOperation; + std::vector 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); + if (auto Err = (this->*writeOp.second)(OS)) + return Err; + } + + return Error::success(); +} + +Error 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(&OpByte), 1); + for (auto Data : Opcode.ExtraData) { + encodeULEB128(Data, OS); + } + } + return Error::success(); +} + +Error MachOWriter::writeBasicBindOpcodes(raw_ostream &OS) { + writeBindOpcodes(OS, Obj.LinkEdit.BindOpcodes); + return Error::success(); +} + +Error MachOWriter::writeWeakBindOpcodes(raw_ostream &OS) { + writeBindOpcodes(OS, Obj.LinkEdit.WeakBindOpcodes); + return Error::success(); +} + +Error MachOWriter::writeLazyBindOpcodes(raw_ostream &OS) { + writeBindOpcodes(OS, Obj.LinkEdit.LazyBindOpcodes); + return Error::success(); +} + +Error MachOWriter::writeNameList(raw_ostream &OS) { + for (auto NLE : Obj.LinkEdit.NameList) { + if (is64Bit) + writeNListEntry(NLE, OS, Obj.IsLittleEndian); + else + writeNListEntry(NLE, OS, Obj.IsLittleEndian); + } + return Error::success(); +} + +Error MachOWriter::writeStringTable(raw_ostream &OS) { + for (auto Str : Obj.LinkEdit.StringTable) { + OS.write(Str.data(), Str.size()); + OS.write('\0'); + } + return Error::success(); +} + +class UniversalWriter { +public: + UniversalWriter(yaml::YamlObjectFile &ObjectFile) + : ObjectFile(ObjectFile), fileStart(0) {} + + Error writeMachO(raw_ostream &OS); + +private: + Error writeFatHeader(raw_ostream &OS); + Error writeFatArchs(raw_ostream &OS); + + void ZeroToOffset(raw_ostream &OS, size_t offset); + + yaml::YamlObjectFile &ObjectFile; + uint64_t fileStart; +}; + +Error UniversalWriter::writeMachO(raw_ostream &OS) { + fileStart = OS.tell(); + if (ObjectFile.MachO) { + MachOWriter Writer(*ObjectFile.MachO); + return Writer.writeMachO(OS); + } + if (auto Err = writeFatHeader(OS)) + return Err; + if (auto Err = writeFatArchs(OS)) + return Err; + 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]); + if (auto Err = Writer.writeMachO(OS)) + return Err; + auto SliceEnd = FatFile.FatArchs[i].offset + FatFile.FatArchs[i].size; + ZeroToOffset(OS, SliceEnd); + } + return Error::success(); +} + +Error 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(&header), sizeof(MachO::fat_header)); + return Error::success(); +} + +template +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 +void writeFatArch(MachOYAML::FatArch &LC, raw_ostream &OS) {} + +template <> +void writeFatArch(MachOYAML::FatArch &Arch, raw_ostream &OS) { + auto FatArch = constructFatArch(Arch); + if (sys::IsLittleEndianHost) + swapStruct(FatArch); + OS.write(reinterpret_cast(&FatArch), sizeof(MachO::fat_arch)); +} + +template <> +void writeFatArch(MachOYAML::FatArch &Arch, + raw_ostream &OS) { + auto FatArch = constructFatArch(Arch); + FatArch.reserved = Arch.reserved; + if (sys::IsLittleEndianHost) + swapStruct(FatArch); + OS.write(reinterpret_cast(&FatArch), + sizeof(MachO::fat_arch_64)); +} + +Error 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(Arch, OS); + else + writeFatArch(Arch, OS); + } + + return Error::success(); +} + +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 { + +int yaml2macho(YamlObjectFile &Doc, raw_ostream &Out) { + UniversalWriter Writer(Doc); + if (auto Err = Writer.writeMachO(Out)) { + errs() << toString(std::move(Err)); + return 1; + } + return 0; +} + +} // namespace yaml +} // namespace llvm Index: llvm/trunk/lib/ObjectYAML/MinidumpEmitter.cpp =================================================================== --- llvm/trunk/lib/ObjectYAML/MinidumpEmitter.cpp +++ llvm/trunk/lib/ObjectYAML/MinidumpEmitter.cpp @@ -0,0 +1,24 @@ +//===- 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/raw_ostream.h" + +using namespace llvm; + +namespace llvm { +namespace yaml { + +int yaml2minidump(MinidumpYAML::Object &Doc, raw_ostream &Out) { + writeAsBinary(Doc, Out); + return 0; +} + +} // namespace yaml +} // namespace llvm Index: llvm/trunk/lib/ObjectYAML/WasmEmitter.cpp =================================================================== --- llvm/trunk/lib/ObjectYAML/WasmEmitter.cpp +++ llvm/trunk/lib/ObjectYAML/WasmEmitter.cpp @@ -0,0 +1,668 @@ +//===- 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) : Obj(Obj) {} + int writeWasm(raw_ostream &OS); + +private: + int writeRelocSection(raw_ostream &OS, WasmYAML::Section &Sec, + uint32_t SectionIndex); + + int writeSectionContent(raw_ostream &OS, WasmYAML::CustomSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::TypeSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::ImportSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::FunctionSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::TableSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::MemorySection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::GlobalSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::EventSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::ExportSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::StartSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::ElemSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::CodeSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::DataSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::DataCountSection &Section); + + // Custom section types + int writeSectionContent(raw_ostream &OS, WasmYAML::DylinkSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::NameSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::LinkingSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::ProducersSection &Section); + int writeSectionContent(raw_ostream &OS, + WasmYAML::TargetFeaturesSection &Section); + WasmYAML::Object &Obj; + uint32_t NumImportedFunctions = 0; + uint32_t NumImportedGlobals = 0; + uint32_t NumImportedEvents = 0; +}; + +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; +} + +static int writeInitExpr(const wasm::WasmInitExpr &InitExpr, raw_ostream &OS) { + 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: + errs() << "Unknown opcode in init_expr: " << InitExpr.Opcode << "\n"; + return 1; + } + writeUint8(OS, wasm::WASM_OPCODE_END); + return 0; +} + +int 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); + } + return 0; +} + +int 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(); + } + + return 0; +} + +int 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(); + } + return 0; +} + +int 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 0; + 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); + } + } + return 0; +} + +int 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); + } + return 0; +} + +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::CustomSection &Section) { + if (auto S = dyn_cast(&Section)) { + if (auto Err = writeSectionContent(OS, *S)) + return Err; + } else if (auto S = dyn_cast(&Section)) { + if (auto Err = writeSectionContent(OS, *S)) + return Err; + } else if (auto S = dyn_cast(&Section)) { + if (auto Err = writeSectionContent(OS, *S)) + return Err; + } else if (auto S = dyn_cast(&Section)) { + if (auto Err = writeSectionContent(OS, *S)) + return Err; + } else if (auto S = dyn_cast(&Section)) { + if (auto Err = writeSectionContent(OS, *S)) + return Err; + } else { + writeStringRef(Section.Name, OS); + Section.Payload.writeAsBinary(OS); + } + return 0; +} + +int 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) { + errs() << "Unexpected type index: " << Sig.Index << "\n"; + return 1; + } + ++ExpectedIndex; + writeUint8(OS, Sig.Form); + encodeULEB128(Sig.ParamTypes.size(), OS); + for (auto ParamType : Sig.ParamTypes) + writeUint8(OS, ParamType); + if (Sig.ReturnType == wasm::WASM_TYPE_NORESULT) { + encodeULEB128(0, OS); + } else { + encodeULEB128(1, OS); + writeUint8(OS, Sig.ReturnType); + } + } + return 0; +} + +int 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: + errs() << "Unknown import type: " << Import.Kind << "\n"; + return 1; + } + } + return 0; +} + +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::FunctionSection &Section) { + encodeULEB128(Section.FunctionTypes.size(), OS); + for (uint32_t FuncType : Section.FunctionTypes) { + encodeULEB128(FuncType, OS); + } + return 0; +} + +int 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); + } + return 0; +} + +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::StartSection &Section) { + encodeULEB128(Section.StartFunction, OS); + return 0; +} + +int 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); + } + return 0; +} + +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::MemorySection &Section) { + encodeULEB128(Section.Memories.size(), OS); + for (const WasmYAML::Limits &Mem : Section.Memories) { + writeLimits(Mem, OS); + } + return 0; +} + +int 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) { + errs() << "Unexpected global index: " << Global.Index << "\n"; + return 1; + } + ++ExpectedIndex; + writeUint8(OS, Global.Type); + writeUint8(OS, Global.Mutable); + writeInitExpr(Global.InitExpr, OS); + } + return 0; +} + +int 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) { + errs() << "Unexpected event index: " << Event.Index << "\n"; + return 1; + } + ++ExpectedIndex; + encodeULEB128(Event.Attribute, OS); + encodeULEB128(Event.SigIndex, OS); + } + return 0; +} + +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::ElemSection &Section) { + encodeULEB128(Section.Segments.size(), OS); + for (auto &Segment : Section.Segments) { + encodeULEB128(Segment.TableIndex, OS); + writeInitExpr(Segment.Offset, OS); + + encodeULEB128(Segment.Functions.size(), OS); + for (auto &Function : Segment.Functions) { + encodeULEB128(Function, OS); + } + } + return 0; +} + +int 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) { + errs() << "Unexpected function index: " << Func.Index << "\n"; + return 1; + } + ++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; + } + return 0; +} + +int 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(Segment.Offset, OS); + encodeULEB128(Segment.Content.binary_size(), OS); + Segment.Content.writeAsBinary(OS); + } + return 0; +} + +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::DataCountSection &Section) { + encodeULEB128(Section.Count, OS); + return 0; +} + +int 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 = dyn_cast(&Sec); + writeStringRef(("reloc." + CustomSection->Name).str(), OS); + break; + } + default: + llvm_unreachable("not yet implemented"); + return 1; + } + + 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); + } + } + return 0; +} + +int 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 &Sec : Obj.Sections) { + StringRef SecName = ""; + if (auto S = dyn_cast(Sec.get())) + SecName = S->Name; + if (!Checker.isValidSectionOrder(Sec->Type, SecName)) { + errs() << "Out of order section type: " << Sec->Type << "\n"; + return 1; + } + encodeULEB128(Sec->Type, OS); + std::string OutString; + raw_string_ostream StringStream(OutString); + if (auto S = dyn_cast(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else if (auto S = dyn_cast(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; + } else { + errs() << "Unknown section type: " << Sec->Type << "\n"; + return 1; + } + 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 &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 0; +} + +namespace llvm { +namespace yaml { + +int yaml2wasm(WasmYAML::Object &Doc, raw_ostream &Out) { + WasmWriter Writer(Doc); + + return Writer.writeWasm(Out); +} + +} // namespace yaml +} // namespace llvm Index: llvm/trunk/lib/ObjectYAML/yaml2obj.cpp =================================================================== --- llvm/trunk/lib/ObjectYAML/yaml2obj.cpp +++ llvm/trunk/lib/ObjectYAML/yaml2obj.cpp @@ -0,0 +1,68 @@ +//===-- 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/Object/ObjectFile.h" +#include "llvm/ObjectYAML/ObjectYAML.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/YAMLTraits.h" + +namespace llvm { +namespace yaml { + +Error convertYAML(yaml::Input &YIn, raw_ostream &Out, unsigned DocNum) { + // TODO: make yaml2* functions return Error instead of int. + auto IntToErr = [](int Ret) -> Error { + if (Ret) + return createStringError(errc::invalid_argument, "yaml2obj failed"); + return Error::success(); + }; + + unsigned CurDocNum = 0; + do { + if (++CurDocNum == DocNum) { + yaml::YamlObjectFile Doc; + YIn >> Doc; + if (std::error_code EC = YIn.error()) + return createStringError(EC, "Failed to parse YAML input!"); + if (Doc.Elf) + return IntToErr(yaml2elf(*Doc.Elf, Out)); + if (Doc.Coff) + return IntToErr(yaml2coff(*Doc.Coff, Out)); + if (Doc.MachO || Doc.FatMachO) + return IntToErr(yaml2macho(Doc, Out)); + if (Doc.Minidump) + return IntToErr(yaml2minidump(*Doc.Minidump, Out)); + if (Doc.Wasm) + return IntToErr(yaml2wasm(*Doc.Wasm, Out)); + return createStringError(errc::invalid_argument, + "Unknown document type!"); + } + } while (YIn.nextDocument()); + + return createStringError(errc::invalid_argument, + "Cannot find the %u%s document", DocNum, + getOrdinalSuffix(DocNum).data()); +} + +Expected> +yaml2ObjectFile(SmallVectorImpl &Storage, StringRef Yaml) { + Storage.clear(); + raw_svector_ostream OS(Storage); + + yaml::Input YIn(Yaml); + if (Error E = convertYAML(YIn, OS)) + return std::move(E); + + return object::ObjectFile::createObjectFile( + MemoryBufferRef(OS.str(), "YamlObject")); +} + +} // namespace yaml +} // namespace llvm Index: llvm/trunk/test/tools/yaml2obj/empty-or-invalid-doc.yaml =================================================================== --- llvm/trunk/test/tools/yaml2obj/empty-or-invalid-doc.yaml +++ llvm/trunk/test/tools/yaml2obj/empty-or-invalid-doc.yaml @@ -2,7 +2,7 @@ # RUN: echo -n "" | not yaml2obj 2>&1 | FileCheck %s # RUN: echo " " | not yaml2obj 2>&1 | FileCheck %s # RUN: echo " " | not yaml2obj 2>&1 | FileCheck %s -# CHECK: yaml2obj: Unknown document type! +# CHECK: yaml2obj: error: Unknown document type! # RUN: echo -e -n "\xff" | not yaml2obj 2>&1 | FileCheck %s --check-prefix=INVALID -# INVALID: yaml2obj: Failed to parse YAML file! +# INVALID: yaml2obj: error: Failed to parse YAML input! Index: llvm/trunk/test/tools/yaml2obj/invalid-docnum.test =================================================================== --- llvm/trunk/test/tools/yaml2obj/invalid-docnum.test +++ llvm/trunk/test/tools/yaml2obj/invalid-docnum.test @@ -0,0 +1,22 @@ +## Test that an error is reported when a docnum is specified, which is +## greater than the number of YAML inputs in the file. + +# RUN: not yaml2obj %s --docnum=3 2>&1 | FileCheck %s +# CHECK: yaml2obj: error: Cannot find the 3rd document + +# RUN: not yaml2obj %s --docnum=76768677 2>&1 | FileCheck %s --check-prefix=TWO +# TWO: yaml2obj: error: Cannot find the 76768677th document + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 Index: llvm/trunk/test/tools/yaml2obj/missing_document_tag.yaml =================================================================== --- llvm/trunk/test/tools/yaml2obj/missing_document_tag.yaml +++ llvm/trunk/test/tools/yaml2obj/missing_document_tag.yaml @@ -6,4 +6,4 @@ ... # CHECK: YAML:4:1: error: YAML Object File missing document type tag! -# CHECK: yaml2obj: Failed to parse YAML file! +# CHECK: yaml2obj: error: Failed to parse YAML input! Index: llvm/trunk/tools/yaml2obj/CMakeLists.txt =================================================================== --- llvm/trunk/tools/yaml2obj/CMakeLists.txt +++ llvm/trunk/tools/yaml2obj/CMakeLists.txt @@ -1,16 +1,8 @@ set(LLVM_LINK_COMPONENTS - DebugInfoCodeView - MC - Object ObjectYAML Support ) add_llvm_tool(yaml2obj yaml2obj.cpp - yaml2coff.cpp - yaml2elf.cpp - yaml2macho.cpp - yaml2minidump.cpp - yaml2wasm.cpp ) Index: llvm/trunk/tools/yaml2obj/yaml2coff.cpp =================================================================== --- llvm/trunk/tools/yaml2obj/yaml2coff.cpp +++ llvm/trunk/tools/yaml2obj/yaml2coff.cpp @@ -1,616 +0,0 @@ -//===- 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 "yaml2obj.h" -#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/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 - -using namespace llvm; - -/// This parses a yaml stream that represents a COFF object file. -/// See docs/yaml2obj for the yaml scheema. -struct COFFParser { - COFFParser(COFFYAML::Object &Obj) - : Obj(Obj), SectionTableStart(0), SectionTableSize(0) { - // 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(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::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) { - errs() << "String table got too large\n"; - return false; - } - Sec.Header.Name[0] = '/'; - std::copy(str.begin(), str.end(), Sec.Header.Name + 1); - } - - if (Sec.Alignment) { - if (Sec.Alignment > 8192) { - errs() << "Section alignment is too large\n"; - return false; - } - if (!isPowerOf2_32(Sec.Alignment)) { - errs() << "Section alignment is not a power of 2\n"; - return false; - } - Sec.Header.Characteristics |= (Log2_32(Sec.Alignment) + 1) << 20; - } - } - return true; - } - - bool parseSymbols() { - for (std::vector::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( - 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::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 StringTableMap; - std::string StringTable; - uint32_t SectionTableStart; - uint32_t SectionTableSize; -}; - -// 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; -} - -namespace { -enum { DOSStubSize = 128 }; -} - -static yaml::BinaryRef -toDebugS(ArrayRef 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 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(Size); - MutableArrayRef Output(Buffer, Size); - BinaryStreamWriter Writer(Output, support::little); - - Err(Writer.writeInteger(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::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(&CP.StringTable[0]) - = CP.StringTable.size(); - - return true; -} - -template -struct binary_le_impl { - value_type Value; - binary_le_impl(value_type V) : Value(V) {} -}; - -template -raw_ostream &operator <<( raw_ostream &OS - , const binary_le_impl &BLE) { - char Buffer[sizeof(BLE.Value)]; - support::endian::write( - Buffer, BLE.Value); - OS.write(Buffer, sizeof(BLE.Value)); - return OS; -} - -template -binary_le_impl binary_le(value_type V) { - return binary_le_impl(V); -} - -template struct zeros_impl {}; - -template -raw_ostream &operator<<(raw_ostream &OS, const zeros_impl &) { - char Buffer[NumBytes]; - memset(Buffer, 0, sizeof(Buffer)); - OS.write(Buffer, sizeof(Buffer)); - return OS; -} - -template -zeros_impl zeros(const T &) { - return zeros_impl(); -} - -template -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(&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(COFF::IMAGE_FILE_MACHINE_UNKNOWN)) - << binary_le(static_cast(0xffff)) - << binary_le(static_cast(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(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(&PEH), sizeof(PEH)); - } else { - object::pe32_header PEH; - uint32_t BaseOfData = initializeOptionalHeader(CP, COFF::PE32Header::PE32, &PEH); - PEH.BaseOfData = BaseOfData; - OS.write(reinterpret_cast(&PEH), sizeof(PEH)); - } - for (const Optional &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::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 SymbolTableIndexMap; - for (std::vector::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::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(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(i->SectionDefinition->Number)) - << binary_le(i->SectionDefinition->Selection) - << zeros(i->SectionDefinition->unused) - << binary_le(static_cast(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; -} - -int yaml2coff(llvm::COFFYAML::Object &Doc, raw_ostream &Out) { - COFFParser CP(Doc); - if (!CP.parse()) { - errs() << "yaml2obj: Failed to parse YAML file!\n"; - return 1; - } - - if (!layoutOptionalHeader(CP)) { - errs() << "yaml2obj: Failed to layout optional header for COFF file!\n"; - return 1; - } - - if (!layoutCOFF(CP)) { - errs() << "yaml2obj: Failed to layout COFF file!\n"; - return 1; - } - if (!writeCOFF(CP, Out)) { - errs() << "yaml2obj: Failed to write COFF file!\n"; - return 1; - } - return 0; -} Index: llvm/trunk/tools/yaml2obj/yaml2elf.cpp =================================================================== --- llvm/trunk/tools/yaml2obj/yaml2elf.cpp +++ llvm/trunk/tools/yaml2obj/yaml2elf.cpp @@ -1,1089 +0,0 @@ -//===- 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 "yaml2obj.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/BinaryFormat/ELF.h" -#include "llvm/MC/StringTableBuilder.h" -#include "llvm/Object/ELFObjectFile.h" -#include "llvm/ObjectYAML/ELFYAML.h" -#include "llvm/ADT/StringSet.h" -#include "llvm/Support/EndianStream.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 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 - raw_ostream &getOSAndAlignedOffset(Integer &Offset, unsigned Align) { - Offset = padToAlignment(Align); - return OS; - } - void writeBlobToStream(raw_ostream &Out) { Out << OS.str(); } -}; -} // end anonymous namespace - -// 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. -namespace { -class NameToIdxMap { - StringMap 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(); } -}; -} // end anonymous namespace - -template -static size_t arrayDataSize(ArrayRef A) { - return A.size() * sizeof(T); -} - -template -static void writeArrayData(raw_ostream &OS, ArrayRef A) { - OS.write((const char *)A.data(), arrayDataSize(A)); -} - -template -static void zero(T &Obj) { - memset(&Obj, 0, sizeof(Obj)); -} - -namespace { -/// "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 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; - ELFYAML::Object &Doc; - - bool buildSectionIndex(); - bool buildSymbolIndex(ArrayRef Symbols); - void initELFHeader(Elf_Ehdr &Header); - void initProgramHeaders(std::vector &PHeaders); - bool initImplicitHeader(ELFState &State, ContiguousBlobAccumulator &CBA, - Elf_Shdr &Header, StringRef SecName, - ELFYAML::Section *YAMLSec); - bool initSectionHeaders(ELFState &State, - std::vector &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 &PHeaders, - std::vector &SHeaders); - bool writeSectionContent(Elf_Shdr &SHeader, - const ELFYAML::RawContentSection &Section, - ContiguousBlobAccumulator &CBA); - bool writeSectionContent(Elf_Shdr &SHeader, - const ELFYAML::RelocationSection &Section, - ContiguousBlobAccumulator &CBA); - bool writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::Group &Group, - ContiguousBlobAccumulator &CBA); - bool writeSectionContent(Elf_Shdr &SHeader, - const ELFYAML::SymverSection &Section, - ContiguousBlobAccumulator &CBA); - bool writeSectionContent(Elf_Shdr &SHeader, - const ELFYAML::VerneedSection &Section, - ContiguousBlobAccumulator &CBA); - bool writeSectionContent(Elf_Shdr &SHeader, - const ELFYAML::VerdefSection &Section, - ContiguousBlobAccumulator &CBA); - bool writeSectionContent(Elf_Shdr &SHeader, - const ELFYAML::MipsABIFlags &Section, - ContiguousBlobAccumulator &CBA); - bool writeSectionContent(Elf_Shdr &SHeader, - const ELFYAML::DynamicSection &Section, - ContiguousBlobAccumulator &CBA); - ELFState(ELFYAML::Object &D); - -public: - static int writeELF(raw_ostream &OS, ELFYAML::Object &Doc); - -private: - void finalizeStrings(); -}; -} // end anonymous namespace - -template -ELFState::ELFState(ELFYAML::Object &D) : Doc(D) { - StringSet<> DocSections; - for (std::unique_ptr &D : Doc.Sections) - if (!D->Name.empty()) - DocSections.insert(D->Name); - - // 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(), - llvm::make_unique( - ELFYAML::Section::SectionKind::RawContent, /*IsImplicit=*/true)); - - std::vector ImplicitSections = {".symtab", ".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 Sec = llvm::make_unique( - ELFYAML::Section::SectionKind::RawContent, true /*IsImplicit*/); - Sec->Name = SecName; - Doc.Sections.push_back(std::move(Sec)); - } -} - -template -void ELFState::initELFHeader(Elf_Ehdr &Header) { - using namespace llvm::ELF; - 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 = sizeof(Header); - Header.e_flags = Doc.Header.Flags; - Header.e_ehsize = sizeof(Elf_Ehdr); - Header.e_phentsize = sizeof(Elf_Phdr); - 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. - Header.e_shoff = - Doc.Header.SHOffset - ? (typename ELFT::uint)(*Doc.Header.SHOffset) - : sizeof(Header) + sizeof(Elf_Phdr) * Doc.ProgramHeaders.size(); - 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"); -} - -template -void ELFState::initProgramHeaders(std::vector &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); - } -} - -static bool convertSectionIndex(NameToIdxMap &SN2I, StringRef SecName, - StringRef IndexSrc, unsigned &IndexDest) { - if (!SN2I.lookup(IndexSrc, IndexDest) && !to_integer(IndexSrc, IndexDest)) { - WithColor::error() << "Unknown section referenced: '" << IndexSrc - << "' at YAML section '" << SecName << "'.\n"; - return false; - } - return true; -} - -template -bool ELFState::initImplicitHeader(ELFState &State, - 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") - State.initSymtabSectionHeader(Header, SymtabType::Static, CBA, YAMLSec); - else if (SecName == ".strtab") - State.initStrtabSectionHeader(Header, SecName, State.DotStrtab, CBA, - YAMLSec); - else if (SecName == ".shstrtab") - State.initStrtabSectionHeader(Header, SecName, State.DotShStrtab, CBA, - YAMLSec); - - else if (SecName == ".dynsym") - State.initSymtabSectionHeader(Header, SymtabType::Dynamic, CBA, YAMLSec); - else if (SecName == ".dynstr") - State.initStrtabSectionHeader(Header, SecName, State.DotDynstr, CBA, - YAMLSec); - else - return false; - - // Override the sh_offset/sh_size fields if requested. - if (YAMLSec) { - if (YAMLSec->ShOffset) - Header.sh_offset = *YAMLSec->ShOffset; - if (YAMLSec->ShSize) - Header.sh_size = *YAMLSec->ShSize; - } - - return true; -} - -static StringRef dropUniqueSuffix(StringRef S) { - size_t SuffixPos = S.rfind(" ["); - if (SuffixPos == StringRef::npos) - return S; - return S.substr(0, SuffixPos); -} - -template -bool ELFState::initSectionHeaders(ELFState &State, - std::vector &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(State, 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(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()) { - unsigned Index; - if (!convertSectionIndex(SN2I, Sec->Name, Sec->Link, Index)) - return false; - SHeader.sh_link = Index; - } - - if (I == 0) { - if (auto RawSec = dyn_cast(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(Sec)) { - if (!writeSectionContent(SHeader, *S, CBA)) - return false; - } else if (auto S = dyn_cast(Sec)) { - if (!writeSectionContent(SHeader, *S, CBA)) - return false; - } else if (auto S = dyn_cast(Sec)) { - if (!writeSectionContent(SHeader, *S, CBA)) - return false; - } else if (auto S = dyn_cast(Sec)) { - if (!writeSectionContent(SHeader, *S, CBA)) - return false; - } else if (auto S = dyn_cast(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(Sec)) { - if (!writeSectionContent(SHeader, *S, CBA)) - return false; - } else if (auto S = dyn_cast(Sec)) { - if (!writeSectionContent(SHeader, *S, CBA)) - return false; - } else if (auto S = dyn_cast(Sec)) { - if (!writeSectionContent(SHeader, *S, CBA)) - return false; - } else if (auto S = dyn_cast(Sec)) { - if (!writeSectionContent(SHeader, *S, CBA)) - return false; - } else - llvm_unreachable("Unknown section type"); - - // Override the sh_offset/sh_size fields if requested. - if (Sec) { - if (Sec->ShOffset) - SHeader.sh_offset = *Sec->ShOffset; - if (Sec->ShSize) - SHeader.sh_size = *Sec->ShSize; - } - } - - return true; -} - -static size_t findFirstNonGlobal(ArrayRef 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 writeRawSectionData(raw_ostream &OS, - const ELFYAML::RawContentSection &RawSec) { - size_t ContentSize = 0; - if (RawSec.Content) { - RawSec.Content->writeAsBinary(OS); - ContentSize = RawSec.Content->binary_size(); - } - - if (!RawSec.Size) - return ContentSize; - - OS.write_zeros(*RawSec.Size - ContentSize); - return *RawSec.Size; -} - -template -static std::vector -toELFSymbols(NameToIdxMap &SN2I, ArrayRef Symbols, - const StringTableBuilder &Strtab) { - using Elf_Sym = typename ELFT::Sym; - - std::vector 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(dropUniqueSuffix(Sym.Name)); - - Symbol.setBindingAndType(Sym.Binding, Sym.Type); - if (!Sym.Section.empty()) { - unsigned Index; - if (!SN2I.lookup(Sym.Section, Index)) { - WithColor::error() << "Unknown section referenced: '" << Sym.Section - << "' by YAML symbol " << Sym.Name << ".\n"; - exit(1); - } - Symbol.st_shndx = Index; - } else if (Sym.Index) { - Symbol.st_shndx = *Sym.Index; - } - // else Symbol.st_shndex == SHN_UNDEF (== 0), since it was zero'd earlier. - Symbol.st_value = Sym.Value; - Symbol.st_other = Sym.Other; - Symbol.st_size = Sym.Size; - } - - return Ret; -} - -template -void ELFState::initSymtabSectionHeader(Elf_Shdr &SHeader, - SymtabType STType, - ContiguousBlobAccumulator &CBA, - ELFYAML::Section *YAMLSec) { - - bool IsStatic = STType == SymtabType::Static; - const auto &Symbols = IsStatic ? Doc.Symbols : Doc.DynamicSymbols; - - ELFYAML::RawContentSection *RawSec = - dyn_cast_or_null(YAMLSec); - if (RawSec && !Symbols.empty() && (RawSec->Content || RawSec->Size)) { - if (RawSec->Content) - WithColor::error() << "Cannot specify both `Content` and " + - (IsStatic ? Twine("`Symbols`") - : Twine("`DynamicSymbols`")) + - " for symbol table section '" - << RawSec->Name << "'.\n"; - if (RawSec->Size) - WithColor::error() << "Cannot specify both `Size` and " + - (IsStatic ? Twine("`Symbols`") - : Twine("`DynamicSymbols`")) + - " for symbol table section '" - << RawSec->Name << "'.\n"; - exit(1); - } - - 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. - unsigned Index; - if (!convertSectionIndex(SN2I, RawSec->Name, RawSec->Link, Index)) - return; - SHeader.sh_link = Index; - } 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 = writeRawSectionData(OS, *RawSec); - return; - } - - std::vector Syms = - toELFSymbols(SN2I, Symbols, IsStatic ? DotStrtab : DotDynstr); - writeArrayData(OS, makeArrayRef(Syms)); - SHeader.sh_size = arrayDataSize(makeArrayRef(Syms)); -} - -template -void ELFState::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(YAMLSec); - - auto &OS = CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); - if (RawSec && (RawSec->Content || RawSec->Size)) { - SHeader.sh_size = writeRawSectionData(OS, *RawSec); - } 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 -void ELFState::setProgramHeaderLayout(std::vector &PHeaders, - std::vector &SHeaders) { - uint32_t PhdrIdx = 0; - for (auto &YamlPhdr : Doc.ProgramHeaders) { - Elf_Phdr &PHeader = PHeaders[PhdrIdx++]; - - std::vector Sections; - for (const ELFYAML::SectionName &SecName : YamlPhdr.Sections) { - unsigned Index; - if (!SN2I.lookup(SecName.Section, Index)) { - WithColor::error() << "Unknown section referenced: '" << SecName.Section - << "' by program header.\n"; - exit(1); - } - 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, - // if not set explicitly. - if (YamlPhdr.FileSize) { - PHeader.p_filesz = *YamlPhdr.FileSize; - } else { - PHeader.p_filesz = 0; - for (Elf_Shdr *SHeader : Sections) { - uint64_t EndOfSection; - if (SHeader->sh_type == llvm::ELF::SHT_NOBITS) - EndOfSection = SHeader->sh_offset; - else - EndOfSection = SHeader->sh_offset + SHeader->sh_size; - uint64_t EndOfSegment = PHeader.p_offset + PHeader.p_filesz; - EndOfSegment = std::max(EndOfSegment, EndOfSection); - PHeader.p_filesz = EndOfSegment - PHeader.p_offset; - } - } - - // If not set explicitly, find the memory size by adding the size of - // sections at the end of the segment. These should be empty (size of zero) - // and NOBITS sections. - if (YamlPhdr.MemSize) { - PHeader.p_memsz = *YamlPhdr.MemSize; - } else { - PHeader.p_memsz = PHeader.p_filesz; - for (Elf_Shdr *SHeader : Sections) - if (SHeader->sh_offset == PHeader.p_offset + PHeader.p_filesz) - PHeader.p_memsz += SHeader->sh_size; - } - - // Set the alignment of the segment to be the same as the maximum alignment - // of the sections with the same offset so that by default the segment - // has a valid and sensible alignment. - if (YamlPhdr.Align) { - PHeader.p_align = *YamlPhdr.Align; - } else { - PHeader.p_align = 1; - for (Elf_Shdr *SHeader : Sections) - if (SHeader->sh_offset == PHeader.p_offset) - PHeader.p_align = std::max(PHeader.p_align, SHeader->sh_addralign); - } - } -} - -template -bool ELFState::writeSectionContent( - Elf_Shdr &SHeader, const ELFYAML::RawContentSection &Section, - ContiguousBlobAccumulator &CBA) { - raw_ostream &OS = - CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); - SHeader.sh_size = writeRawSectionData(OS, Section); - - 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; - - return true; -} - -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 -bool -ELFState::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"); - - unsigned Index = 0; - if (!Section.RelocatableSec.empty() && - !convertSectionIndex(SN2I, Section.Name, Section.RelocatableSec, Index)) - return false; - SHeader.sh_info = Index; - - auto &OS = CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); - - for (const auto &Rel : Section.Relocations) { - unsigned SymIdx = 0; - // If a relocation references a symbol, try to look one up in the symbol - // table. If it is not there, treat the value as a symbol index. - if (Rel.Symbol && !SymN2I.lookup(*Rel.Symbol, SymIdx) && - !to_integer(*Rel.Symbol, SymIdx)) { - WithColor::error() << "Unknown symbol referenced: '" << *Rel.Symbol - << "' at YAML section '" << Section.Name << "'.\n"; - return false; - } - - 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)); - } - } - return true; -} - -template -bool ELFState::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(); - - unsigned SymIdx; - if (!SymN2I.lookup(Section.Signature, SymIdx) && - !to_integer(Section.Signature, SymIdx)) { - WithColor::error() << "Unknown symbol referenced: '" << Section.Signature - << "' at YAML section '" << Section.Name << "'.\n"; - return false; - } - SHeader.sh_info = SymIdx; - - 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 if (!convertSectionIndex(SN2I, Section.Name, Member.sectionNameOrType, - SectionIndex)) - return false; - support::endian::write(OS, SectionIndex, ELFT::TargetEndianness); - } - return true; -} - -template -bool ELFState::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(OS, Version, ELFT::TargetEndianness); - - SHeader.sh_entsize = Section.EntSize ? (uint64_t)*Section.EntSize : 2; - SHeader.sh_size = Section.Entries.size() * SHeader.sh_entsize; - return true; -} - -template -bool ELFState::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; - - return true; -} - -template -bool ELFState::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; - - return true; -} - -template -bool ELFState::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 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)); - - return true; -} - -template -bool ELFState::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) { - WithColor::error() - << "Cannot specify both raw content and explicit entries " - "for dynamic section '" - << Section.Name << "'.\n"; - return false; - } - - 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(OS, DE.Tag, ELFT::TargetEndianness); - support::endian::write(OS, DE.Val, ELFT::TargetEndianness); - } - if (Section.Content) - Section.Content->writeAsBinary(OS); - - return true; -} - -template bool ELFState::buildSectionIndex() { - for (unsigned I = 0, E = Doc.Sections.size(); I != E; ++I) { - StringRef Name = Doc.Sections[I]->Name; - if (Name.empty()) - continue; - - DotShStrtab.add(dropUniqueSuffix(Name)); - if (!SN2I.addName(Name, I)) { - WithColor::error() << "Repeated section name: '" << Name - << "' at YAML section number " << I << ".\n"; - return false; - } - } - - DotShStrtab.finalize(); - return true; -} - -template -bool ELFState::buildSymbolIndex(ArrayRef Symbols) { - bool GlobalSymbolSeen = false; - std::size_t I = 0; - for (const auto &Sym : Symbols) { - ++I; - - StringRef Name = Sym.Name; - if (Sym.Binding.value == ELF::STB_LOCAL && GlobalSymbolSeen) { - WithColor::error() << "Local symbol '" + Name + - "' after global in Symbols list.\n"; - return false; - } - if (Sym.Binding.value != ELF::STB_LOCAL) - GlobalSymbolSeen = true; - - if (!Name.empty() && !SymN2I.addName(Name, I)) { - WithColor::error() << "Repeated symbol name: '" << Name << "'.\n"; - return false; - } - } - return true; -} - -template void ELFState::finalizeStrings() { - // Add the regular symbol names to .strtab section. - for (const ELFYAML::Symbol &Sym : Doc.Symbols) - DotStrtab.add(dropUniqueSuffix(Sym.Name)); - DotStrtab.finalize(); - - // Add the dynamic symbol names to .dynstr section. - for (const ELFYAML::Symbol &Sym : Doc.DynamicSymbols) - DotDynstr.add(dropUniqueSuffix(Sym.Name)); - - // SHT_GNU_verdef and SHT_GNU_verneed sections might also - // add strings to .dynstr section. - for (const std::unique_ptr &Sec : Doc.Sections) { - if (auto VerNeed = dyn_cast(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(Sec.get())) { - for (const ELFYAML::VerdefEntry &E : VerDef->Entries) - for (StringRef Name : E.VerNames) - DotDynstr.add(Name); - } - } - - DotDynstr.finalize(); -} - -template -int ELFState::writeELF(raw_ostream &OS, ELFYAML::Object &Doc) { - ELFState State(Doc); - - // 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(); - - if (!State.buildSectionIndex()) - return 1; - - if (!State.buildSymbolIndex(Doc.Symbols)) - return 1; - - Elf_Ehdr Header; - State.initELFHeader(Header); - - // TODO: Flesh out section header support. - - std::vector PHeaders; - State.initProgramHeaders(PHeaders); - - // XXX: This offset is tightly coupled with the order that we write - // things to `OS`. - const size_t SectionContentBeginOffset = Header.e_ehsize + - Header.e_phentsize * Header.e_phnum + - Header.e_shentsize * Header.e_shnum; - ContiguousBlobAccumulator CBA(SectionContentBeginOffset); - - std::vector SHeaders; - if (!State.initSectionHeaders(State, SHeaders, CBA)) - return 1; - - // Now we can decide segment offsets - State.setProgramHeaderLayout(PHeaders, SHeaders); - - OS.write((const char *)&Header, sizeof(Header)); - writeArrayData(OS, makeArrayRef(PHeaders)); - writeArrayData(OS, makeArrayRef(SHeaders)); - CBA.writeBlobToStream(OS); - return 0; -} - -int yaml2elf(llvm::ELFYAML::Object &Doc, raw_ostream &Out) { - 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::writeELF(Out, Doc); - return ELFState::writeELF(Out, Doc); - } - if (IsLE) - return ELFState::writeELF(Out, Doc); - return ELFState::writeELF(Out, Doc); -} Index: llvm/trunk/tools/yaml2obj/yaml2macho.cpp =================================================================== --- llvm/trunk/tools/yaml2obj/yaml2macho.cpp +++ llvm/trunk/tools/yaml2obj/yaml2macho.cpp @@ -1,602 +0,0 @@ -//===- 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 "yaml2obj.h" -#include "llvm/BinaryFormat/MachO.h" -#include "llvm/ObjectYAML/DWARFEmitter.h" -#include "llvm/ObjectYAML/ObjectYAML.h" -#include "llvm/Support/Error.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(&Header), 0, sizeof(MachO::mach_header_64)); - } - - Error writeMachO(raw_ostream &OS); - -private: - Error writeHeader(raw_ostream &OS); - Error writeLoadCommands(raw_ostream &OS); - Error writeSectionData(raw_ostream &OS); - Error writeLinkEditData(raw_ostream &OS); - - void writeBindOpcodes(raw_ostream &OS, - std::vector &BindOpcodes); - // LinkEdit writers - Error writeRebaseOpcodes(raw_ostream &OS); - Error writeBasicBindOpcodes(raw_ostream &OS); - Error writeWeakBindOpcodes(raw_ostream &OS); - Error writeLazyBindOpcodes(raw_ostream &OS); - Error writeNameList(raw_ostream &OS); - Error writeStringTable(raw_ostream &OS); - Error 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; -}; - -Error MachOWriter::writeMachO(raw_ostream &OS) { - fileStart = OS.tell(); - if (auto Err = writeHeader(OS)) - return Err; - if (auto Err = writeLoadCommands(OS)) - return Err; - if (auto Err = writeSectionData(OS)) - return Err; - return Error::success(); -} - -Error 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); - - return Error::success(); -} - -template -SectionType constructSection(MachOYAML::Section Sec) { - SectionType TempSec; - memcpy(reinterpret_cast(&TempSec.sectname[0]), &Sec.sectname[0], 16); - memcpy(reinterpret_cast(&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 -size_t writeLoadCommandData(MachOYAML::LoadCommand &LC, raw_ostream &OS, - bool IsLittleEndian) { - return 0; -} - -template <> -size_t writeLoadCommandData(MachOYAML::LoadCommand &LC, - raw_ostream &OS, - bool IsLittleEndian) { - size_t BytesWritten = 0; - for (const auto &Sec : LC.Sections) { - auto TempSec = constructSection(Sec); - if (IsLittleEndian != sys::IsLittleEndianHost) - MachO::swapStruct(TempSec); - OS.write(reinterpret_cast(&(TempSec)), - sizeof(MachO::section)); - BytesWritten += sizeof(MachO::section); - } - return BytesWritten; -} - -template <> -size_t writeLoadCommandData( - MachOYAML::LoadCommand &LC, raw_ostream &OS, bool IsLittleEndian) { - size_t BytesWritten = 0; - for (const auto &Sec : LC.Sections) { - auto TempSec = constructSection(Sec); - TempSec.reserved3 = Sec.reserved3; - if (IsLittleEndian != sys::IsLittleEndianHost) - MachO::swapStruct(TempSec); - OS.write(reinterpret_cast(&(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(MachOYAML::LoadCommand &LC, - raw_ostream &OS, - bool IsLittleEndian) { - return writePayloadString(LC, OS); -} - -template <> -size_t writeLoadCommandData(MachOYAML::LoadCommand &LC, - raw_ostream &OS, - bool IsLittleEndian) { - return writePayloadString(LC, OS); -} - -template <> -size_t writeLoadCommandData(MachOYAML::LoadCommand &LC, - raw_ostream &OS, - bool IsLittleEndian) { - return writePayloadString(LC, OS); -} - -template <> -size_t writeLoadCommandData( - 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(&tool), - sizeof(MachO::build_tool_version)); - BytesWritten += sizeof(MachO::build_tool_version); - } - return BytesWritten; -} - -void ZeroFillBytes(raw_ostream &OS, size_t Size) { - std::vector FillData; - FillData.insert(FillData.begin(), Size, 0); - OS.write(reinterpret_cast(FillData.data()), Size); -} - -void Fill(raw_ostream &OS, size_t Size, uint32_t Data) { - std::vector FillData; - FillData.insert(FillData.begin(), (Size / 4) + 1, Data); - OS.write(reinterpret_cast(FillData.data()), Size); -} - -void MachOWriter::ZeroToOffset(raw_ostream &OS, size_t Offset) { - auto currOffset = OS.tell() - fileStart; - if (currOffset < Offset) - ZeroFillBytes(OS, Offset - currOffset); -} - -Error 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(&(Data.LCStruct##_data)), \ - sizeof(MachO::LCStruct)); \ - BytesWritten = sizeof(MachO::LCStruct); \ - BytesWritten += \ - writeLoadCommandData(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(&(Data.load_command_data)), - sizeof(MachO::load_command)); - BytesWritten = sizeof(MachO::load_command); - BytesWritten += - writeLoadCommandData(LC, OS, Obj.IsLittleEndian); - break; -#include "llvm/BinaryFormat/MachO.def" - } - - if (LC.PayloadBytes.size() > 0) { - OS.write(reinterpret_cast(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); - } - } - return Error::success(); -} - -static bool isVirtualSection(uint8_t type) { - return (type == MachO::S_ZEROFILL || - type == MachO::S_GB_ZEROFILL || - type == MachO::S_THREAD_LOCAL_ZEROFILL); -} - -Error 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; - if (auto Err = writeLinkEditData(OS)) - return Err; - } - 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 (isVirtualSection(Sec.flags & MachO::SECTION_TYPE)) - continue; - - // 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) { - if (auto Err = writeLinkEditData(OS)) - return Err; - } - return Error::success(); -} - -void MachOWriter::writeBindOpcodes( - raw_ostream &OS, std::vector &BindOpcodes) { - - for (auto Opcode : BindOpcodes) { - uint8_t OpByte = Opcode.Opcode | Opcode.Imm; - OS.write(reinterpret_cast(&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(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); -} - -Error MachOWriter::writeExportTrie(raw_ostream &OS) { - dumpExportEntry(OS, Obj.LinkEdit.ExportTrie); - return Error::success(); -} - -template -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(&ListEntry), sizeof(NListType)); -} - -Error MachOWriter::writeLinkEditData(raw_ostream &OS) { - typedef Error (MachOWriter::*writeHandler)(raw_ostream &); - typedef std::pair writeOperation; - std::vector 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); - if (auto Err = (this->*writeOp.second)(OS)) - return Err; - } - - return Error::success(); -} - -Error 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(&OpByte), 1); - for (auto Data : Opcode.ExtraData) { - encodeULEB128(Data, OS); - } - } - return Error::success(); -} - -Error MachOWriter::writeBasicBindOpcodes(raw_ostream &OS) { - writeBindOpcodes(OS, Obj.LinkEdit.BindOpcodes); - return Error::success(); -} - -Error MachOWriter::writeWeakBindOpcodes(raw_ostream &OS) { - writeBindOpcodes(OS, Obj.LinkEdit.WeakBindOpcodes); - return Error::success(); -} - -Error MachOWriter::writeLazyBindOpcodes(raw_ostream &OS) { - writeBindOpcodes(OS, Obj.LinkEdit.LazyBindOpcodes); - return Error::success(); -} - -Error MachOWriter::writeNameList(raw_ostream &OS) { - for (auto NLE : Obj.LinkEdit.NameList) { - if (is64Bit) - writeNListEntry(NLE, OS, Obj.IsLittleEndian); - else - writeNListEntry(NLE, OS, Obj.IsLittleEndian); - } - return Error::success(); -} - -Error MachOWriter::writeStringTable(raw_ostream &OS) { - for (auto Str : Obj.LinkEdit.StringTable) { - OS.write(Str.data(), Str.size()); - OS.write('\0'); - } - return Error::success(); -} - -class UniversalWriter { -public: - UniversalWriter(yaml::YamlObjectFile &ObjectFile) - : ObjectFile(ObjectFile), fileStart(0) {} - - Error writeMachO(raw_ostream &OS); - -private: - Error writeFatHeader(raw_ostream &OS); - Error writeFatArchs(raw_ostream &OS); - - void ZeroToOffset(raw_ostream &OS, size_t offset); - - yaml::YamlObjectFile &ObjectFile; - uint64_t fileStart; -}; - -Error UniversalWriter::writeMachO(raw_ostream &OS) { - fileStart = OS.tell(); - if (ObjectFile.MachO) { - MachOWriter Writer(*ObjectFile.MachO); - return Writer.writeMachO(OS); - } - if (auto Err = writeFatHeader(OS)) - return Err; - if (auto Err = writeFatArchs(OS)) - return Err; - 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]); - if (auto Err = Writer.writeMachO(OS)) - return Err; - auto SliceEnd = FatFile.FatArchs[i].offset + FatFile.FatArchs[i].size; - ZeroToOffset(OS, SliceEnd); - } - return Error::success(); -} - -Error 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(&header), sizeof(MachO::fat_header)); - return Error::success(); -} - -template -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 -void writeFatArch(MachOYAML::FatArch &LC, raw_ostream &OS) {} - -template <> -void writeFatArch(MachOYAML::FatArch &Arch, raw_ostream &OS) { - auto FatArch = constructFatArch(Arch); - if (sys::IsLittleEndianHost) - swapStruct(FatArch); - OS.write(reinterpret_cast(&FatArch), sizeof(MachO::fat_arch)); -} - -template <> -void writeFatArch(MachOYAML::FatArch &Arch, - raw_ostream &OS) { - auto FatArch = constructFatArch(Arch); - FatArch.reserved = Arch.reserved; - if (sys::IsLittleEndianHost) - swapStruct(FatArch); - OS.write(reinterpret_cast(&FatArch), - sizeof(MachO::fat_arch_64)); -} - -Error 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(Arch, OS); - else - writeFatArch(Arch, OS); - } - - return Error::success(); -} - -void UniversalWriter::ZeroToOffset(raw_ostream &OS, size_t Offset) { - auto currOffset = OS.tell() - fileStart; - if (currOffset < Offset) - ZeroFillBytes(OS, Offset - currOffset); -} - -} // end anonymous namespace - -int yaml2macho(yaml::YamlObjectFile &Doc, raw_ostream &Out) { - UniversalWriter Writer(Doc); - if (auto Err = Writer.writeMachO(Out)) { - errs() << toString(std::move(Err)); - return 1; - } - return 0; -} Index: llvm/trunk/tools/yaml2obj/yaml2minidump.cpp =================================================================== --- llvm/trunk/tools/yaml2obj/yaml2minidump.cpp +++ llvm/trunk/tools/yaml2obj/yaml2minidump.cpp @@ -1,18 +0,0 @@ -//===- 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 "yaml2obj.h" -#include "llvm/ObjectYAML/MinidumpYAML.h" -#include "llvm/Support/raw_ostream.h" - -using namespace llvm; - -int yaml2minidump(MinidumpYAML::Object &Doc, raw_ostream &Out) { - writeAsBinary(Doc, Out); - return 0; -} Index: llvm/trunk/tools/yaml2obj/yaml2obj.h =================================================================== --- llvm/trunk/tools/yaml2obj/yaml2obj.h +++ llvm/trunk/tools/yaml2obj/yaml2obj.h @@ -1,45 +0,0 @@ -//===--- yaml2obj.h - -------------------------------------------*- C++ -*-===// -// -// 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 -/// Common declarations for yaml2obj -//===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_YAML2OBJ_YAML2OBJ_H -#define LLVM_TOOLS_YAML2OBJ_YAML2OBJ_H - -namespace llvm { -class raw_ostream; - -namespace COFFYAML { -struct Object; -} - -namespace ELFYAML { -struct Object; -} - -namespace MinidumpYAML { -struct Object; -} - -namespace WasmYAML { -struct Object; -} - -namespace yaml { -class Input; -struct YamlObjectFile; -} -} - -int yaml2coff(llvm::COFFYAML::Object &Doc, llvm::raw_ostream &Out); -int yaml2elf(llvm::ELFYAML::Object &Doc, llvm::raw_ostream &Out); -int yaml2macho(llvm::yaml::YamlObjectFile &Doc, llvm::raw_ostream &Out); -int yaml2minidump(llvm::MinidumpYAML::Object &Doc, llvm::raw_ostream &Out); -int yaml2wasm(llvm::WasmYAML::Object &Doc, llvm::raw_ostream &Out); - -#endif Index: llvm/trunk/tools/yaml2obj/yaml2obj.cpp =================================================================== --- llvm/trunk/tools/yaml2obj/yaml2obj.cpp +++ llvm/trunk/tools/yaml2obj/yaml2obj.cpp @@ -13,7 +13,7 @@ // //===----------------------------------------------------------------------===// -#include "yaml2obj.h" +#include "llvm/ObjectYAML/yaml2obj.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ObjectYAML/ObjectYAML.h" #include "llvm/Support/CommandLine.h" @@ -21,6 +21,7 @@ #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" #include @@ -42,32 +43,6 @@ exit(1); } -static int convertYAML(yaml::Input &YIn, raw_ostream &Out) { - unsigned CurDocNum = 0; - do { - if (++CurDocNum == DocNum) { - yaml::YamlObjectFile Doc; - YIn >> Doc; - if (YIn.error()) - error("yaml2obj: Failed to parse YAML file!"); - if (Doc.Elf) - return yaml2elf(*Doc.Elf, Out); - if (Doc.Coff) - return yaml2coff(*Doc.Coff, Out); - if (Doc.MachO || Doc.FatMachO) - return yaml2macho(Doc, Out); - if (Doc.Minidump) - return yaml2minidump(*Doc.Minidump, Out); - if (Doc.Wasm) - return yaml2wasm(*Doc.Wasm, Out); - error("yaml2obj: Unknown document type!"); - } - } while (YIn.nextDocument()); - - error("yaml2obj: Cannot find the " + Twine(DocNum) + - llvm::getOrdinalSuffix(DocNum) + " document"); -} - int main(int argc, char **argv) { InitLLVM X(argc, argv); cl::ParseCommandLineOptions(argc, argv); @@ -87,10 +62,12 @@ return 1; yaml::Input YIn(Buf.get()->getBuffer()); - int Res = convertYAML(YIn, Out->os()); - if (Res == 0) - Out->keep(); + if (Error E = convertYAML(YIn, Out->os(), DocNum)) { + logAllUnhandledErrors(std::move(E), WithColor::error(errs(), argv[0])); + return 1; + } + Out->keep(); Out->os().flush(); - return Res; + return 0; } Index: llvm/trunk/tools/yaml2obj/yaml2wasm.cpp =================================================================== --- llvm/trunk/tools/yaml2obj/yaml2wasm.cpp +++ llvm/trunk/tools/yaml2obj/yaml2wasm.cpp @@ -1,658 +0,0 @@ -//===- 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/Support/Endian.h" -#include "llvm/Support/LEB128.h" - -using namespace llvm; - -/// 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) : Obj(Obj) {} - int writeWasm(raw_ostream &OS); - -private: - int writeRelocSection(raw_ostream &OS, WasmYAML::Section &Sec, - uint32_t SectionIndex); - - int writeSectionContent(raw_ostream &OS, WasmYAML::CustomSection &Section); - int writeSectionContent(raw_ostream &OS, WasmYAML::TypeSection &Section); - int writeSectionContent(raw_ostream &OS, WasmYAML::ImportSection &Section); - int writeSectionContent(raw_ostream &OS, WasmYAML::FunctionSection &Section); - int writeSectionContent(raw_ostream &OS, WasmYAML::TableSection &Section); - int writeSectionContent(raw_ostream &OS, WasmYAML::MemorySection &Section); - int writeSectionContent(raw_ostream &OS, WasmYAML::GlobalSection &Section); - int writeSectionContent(raw_ostream &OS, WasmYAML::EventSection &Section); - int writeSectionContent(raw_ostream &OS, WasmYAML::ExportSection &Section); - int writeSectionContent(raw_ostream &OS, WasmYAML::StartSection &Section); - int writeSectionContent(raw_ostream &OS, WasmYAML::ElemSection &Section); - int writeSectionContent(raw_ostream &OS, WasmYAML::CodeSection &Section); - int writeSectionContent(raw_ostream &OS, WasmYAML::DataSection &Section); - int writeSectionContent(raw_ostream &OS, WasmYAML::DataCountSection &Section); - - // Custom section types - int writeSectionContent(raw_ostream &OS, WasmYAML::DylinkSection &Section); - int writeSectionContent(raw_ostream &OS, WasmYAML::NameSection &Section); - int writeSectionContent(raw_ostream &OS, WasmYAML::LinkingSection &Section); - int writeSectionContent(raw_ostream &OS, WasmYAML::ProducersSection &Section); - int writeSectionContent(raw_ostream &OS, - WasmYAML::TargetFeaturesSection &Section); - WasmYAML::Object &Obj; - uint32_t NumImportedFunctions = 0; - uint32_t NumImportedGlobals = 0; - uint32_t NumImportedEvents = 0; -}; - -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; -} - -static int writeInitExpr(const wasm::WasmInitExpr &InitExpr, raw_ostream &OS) { - 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: - errs() << "Unknown opcode in init_expr: " << InitExpr.Opcode << "\n"; - return 1; - } - writeUint8(OS, wasm::WASM_OPCODE_END); - return 0; -} - -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; } -}; - -int 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); - } - return 0; -} - -int 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(); - } - - return 0; -} - -int 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(); - } - return 0; -} - -int 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 0; - 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); - } - } - return 0; -} - -int 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); - } - return 0; -} - -int WasmWriter::writeSectionContent(raw_ostream &OS, - WasmYAML::CustomSection &Section) { - if (auto S = dyn_cast(&Section)) { - if (auto Err = writeSectionContent(OS, *S)) - return Err; - } else if (auto S = dyn_cast(&Section)) { - if (auto Err = writeSectionContent(OS, *S)) - return Err; - } else if (auto S = dyn_cast(&Section)) { - if (auto Err = writeSectionContent(OS, *S)) - return Err; - } else if (auto S = dyn_cast(&Section)) { - if (auto Err = writeSectionContent(OS, *S)) - return Err; - } else if (auto S = dyn_cast(&Section)) { - if (auto Err = writeSectionContent(OS, *S)) - return Err; - } else { - writeStringRef(Section.Name, OS); - Section.Payload.writeAsBinary(OS); - } - return 0; -} - -int 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) { - errs() << "Unexpected type index: " << Sig.Index << "\n"; - return 1; - } - ++ExpectedIndex; - writeUint8(OS, Sig.Form); - encodeULEB128(Sig.ParamTypes.size(), OS); - for (auto ParamType : Sig.ParamTypes) - writeUint8(OS, ParamType); - if (Sig.ReturnType == wasm::WASM_TYPE_NORESULT) { - encodeULEB128(0, OS); - } else { - encodeULEB128(1, OS); - writeUint8(OS, Sig.ReturnType); - } - } - return 0; -} - -int 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: - errs() << "Unknown import type: " << Import.Kind << "\n"; - return 1; - } - } - return 0; -} - -int WasmWriter::writeSectionContent(raw_ostream &OS, - WasmYAML::FunctionSection &Section) { - encodeULEB128(Section.FunctionTypes.size(), OS); - for (uint32_t FuncType : Section.FunctionTypes) { - encodeULEB128(FuncType, OS); - } - return 0; -} - -int 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); - } - return 0; -} - -int WasmWriter::writeSectionContent(raw_ostream &OS, - WasmYAML::StartSection &Section) { - encodeULEB128(Section.StartFunction, OS); - return 0; -} - -int 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); - } - return 0; -} - -int WasmWriter::writeSectionContent(raw_ostream &OS, - WasmYAML::MemorySection &Section) { - encodeULEB128(Section.Memories.size(), OS); - for (const WasmYAML::Limits &Mem : Section.Memories) { - writeLimits(Mem, OS); - } - return 0; -} - -int 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) { - errs() << "Unexpected global index: " << Global.Index << "\n"; - return 1; - } - ++ExpectedIndex; - writeUint8(OS, Global.Type); - writeUint8(OS, Global.Mutable); - writeInitExpr(Global.InitExpr, OS); - } - return 0; -} - -int 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) { - errs() << "Unexpected event index: " << Event.Index << "\n"; - return 1; - } - ++ExpectedIndex; - encodeULEB128(Event.Attribute, OS); - encodeULEB128(Event.SigIndex, OS); - } - return 0; -} - -int WasmWriter::writeSectionContent(raw_ostream &OS, - WasmYAML::ElemSection &Section) { - encodeULEB128(Section.Segments.size(), OS); - for (auto &Segment : Section.Segments) { - encodeULEB128(Segment.TableIndex, OS); - writeInitExpr(Segment.Offset, OS); - - encodeULEB128(Segment.Functions.size(), OS); - for (auto &Function : Segment.Functions) { - encodeULEB128(Function, OS); - } - } - return 0; -} - -int 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) { - errs() << "Unexpected function index: " << Func.Index << "\n"; - return 1; - } - ++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; - } - return 0; -} - -int 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(Segment.Offset, OS); - encodeULEB128(Segment.Content.binary_size(), OS); - Segment.Content.writeAsBinary(OS); - } - return 0; -} - -int WasmWriter::writeSectionContent(raw_ostream &OS, - WasmYAML::DataCountSection &Section) { - encodeULEB128(Section.Count, OS); - return 0; -} - -int 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 = dyn_cast(&Sec); - writeStringRef(("reloc." + CustomSection->Name).str(), OS); - break; - } - default: - llvm_unreachable("not yet implemented"); - return 1; - } - - 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); - } - } - return 0; -} - -int 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 &Sec : Obj.Sections) { - StringRef SecName = ""; - if (auto S = dyn_cast(Sec.get())) - SecName = S->Name; - if (!Checker.isValidSectionOrder(Sec->Type, SecName)) { - errs() << "Out of order section type: " << Sec->Type << "\n"; - return 1; - } - encodeULEB128(Sec->Type, OS); - std::string OutString; - raw_string_ostream StringStream(OutString); - if (auto S = dyn_cast(Sec.get())) { - if (auto Err = writeSectionContent(StringStream, *S)) - return Err; - } else if (auto S = dyn_cast(Sec.get())) { - if (auto Err = writeSectionContent(StringStream, *S)) - return Err; - } else if (auto S = dyn_cast(Sec.get())) { - if (auto Err = writeSectionContent(StringStream, *S)) - return Err; - } else if (auto S = dyn_cast(Sec.get())) { - if (auto Err = writeSectionContent(StringStream, *S)) - return Err; - } else if (auto S = dyn_cast(Sec.get())) { - if (auto Err = writeSectionContent(StringStream, *S)) - return Err; - } else if (auto S = dyn_cast(Sec.get())) { - if (auto Err = writeSectionContent(StringStream, *S)) - return Err; - } else if (auto S = dyn_cast(Sec.get())) { - if (auto Err = writeSectionContent(StringStream, *S)) - return Err; - } else if (auto S = dyn_cast(Sec.get())) { - if (auto Err = writeSectionContent(StringStream, *S)) - return Err; - } else if (auto S = dyn_cast(Sec.get())) { - if (auto Err = writeSectionContent(StringStream, *S)) - return Err; - } else if (auto S = dyn_cast(Sec.get())) { - if (auto Err = writeSectionContent(StringStream, *S)) - return Err; - } else if (auto S = dyn_cast(Sec.get())) { - if (auto Err = writeSectionContent(StringStream, *S)) - return Err; - } else if (auto S = dyn_cast(Sec.get())) { - if (auto Err = writeSectionContent(StringStream, *S)) - return Err; - } else if (auto S = dyn_cast(Sec.get())) { - if (auto Err = writeSectionContent(StringStream, *S)) - return Err; - } else if (auto S = dyn_cast(Sec.get())) { - if (auto Err = writeSectionContent(StringStream, *S)) - return Err; - } else { - errs() << "Unknown section type: " << Sec->Type << "\n"; - return 1; - } - 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 &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 0; -} - -int yaml2wasm(llvm::WasmYAML::Object &Doc, raw_ostream &Out) { - WasmWriter Writer(Doc); - - return Writer.writeWasm(Out); -} Index: llvm/trunk/unittests/ObjectYAML/CMakeLists.txt =================================================================== --- llvm/trunk/unittests/ObjectYAML/CMakeLists.txt +++ llvm/trunk/unittests/ObjectYAML/CMakeLists.txt @@ -5,6 +5,7 @@ add_llvm_unittest(ObjectYAMLTests MinidumpYAMLTest.cpp + YAML2ObjTest.cpp YAMLTest.cpp ) Index: llvm/trunk/unittests/ObjectYAML/YAML2ObjTest.cpp =================================================================== --- llvm/trunk/unittests/ObjectYAML/YAML2ObjTest.cpp +++ llvm/trunk/unittests/ObjectYAML/YAML2ObjTest.cpp @@ -0,0 +1,36 @@ +//===- YAML2ObjTest.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/SmallString.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Error.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace object; +using namespace yaml; + +TEST(yaml2ObjectFile, ELF) { + SmallString<0> Storage; + Expected> ErrOrObj = yaml2ObjectFile(Storage, R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64)"); + + ASSERT_THAT_EXPECTED(ErrOrObj, Succeeded()); + + std::unique_ptr ObjFile = std::move(ErrOrObj.get()); + + ASSERT_TRUE(ObjFile->isELF()); + ASSERT_TRUE(ObjFile->isRelocatableObject()); +}