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