[yaml2obj] Move core yaml2obj code into lib and include for use in unit tests

Reviewers: jhenderson, rupprecht, MaskRay, grimar, labath

Reviewed By: rupprecht

Subscribers: gribozavr, mgrang, seiya, mgorny, sbc100, hiraditya, aheejin, jakehehrlich, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D65255

llvm-svn: 368119
diff --git a/llvm/include/llvm/ObjectYAML/yaml2obj.h b/llvm/include/llvm/ObjectYAML/yaml2obj.h
new file mode 100644
index 0000000..d8d8dad
--- /dev/null
+++ b/llvm/include/llvm/ObjectYAML/yaml2obj.h
@@ -0,0 +1,62 @@
+//===--- yaml2obj.h - -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// Common declarations for yaml2obj
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_TOOLS_YAML2OBJ_YAML2OBJ_H
+#define LLVM_TOOLS_YAML2OBJ_YAML2OBJ_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <memory>
+
+namespace llvm {
+class raw_ostream;
+template <typename T> class SmallVectorImpl;
+template <typename T> class Expected;
+
+namespace object {
+class ObjectFile;
+}
+
+namespace COFFYAML {
+struct Object;
+}
+
+namespace ELFYAML {
+struct Object;
+}
+
+namespace MinidumpYAML {
+struct Object;
+}
+
+namespace WasmYAML {
+struct Object;
+}
+
+namespace yaml {
+class Input;
+struct YamlObjectFile;
+
+int yaml2coff(COFFYAML::Object &Doc, raw_ostream &Out);
+int yaml2elf(ELFYAML::Object &Doc, raw_ostream &Out);
+int yaml2macho(YamlObjectFile &Doc, raw_ostream &Out);
+int yaml2minidump(MinidumpYAML::Object &Doc, raw_ostream &Out);
+int yaml2wasm(WasmYAML::Object &Doc, raw_ostream &Out);
+
+Error convertYAML(Input &YIn, raw_ostream &Out, unsigned DocNum = 1);
+
+/// Convenience function for tests.
+Expected<std::unique_ptr<object::ObjectFile>>
+yaml2ObjectFile(SmallVectorImpl<char> &Storage, StringRef Yaml);
+
+} // namespace yaml
+} // namespace llvm
+
+#endif
diff --git a/llvm/lib/ObjectYAML/CMakeLists.txt b/llvm/lib/ObjectYAML/CMakeLists.txt
index 8943aab..434da71 100644
--- a/llvm/lib/ObjectYAML/CMakeLists.txt
+++ b/llvm/lib/ObjectYAML/CMakeLists.txt
@@ -3,15 +3,24 @@
   CodeViewYAMLSymbols.cpp
   CodeViewYAMLTypeHashing.cpp
   CodeViewYAMLTypes.cpp
+  COFFEmitter.cpp
   COFFYAML.cpp
   DWARFEmitter.cpp
   DWARFVisitor.cpp
   DWARFYAML.cpp
+  ELFEmitter.cpp
   ELFYAML.cpp
+  MachOEmitter.cpp
   MachOYAML.cpp
   ObjectYAML.cpp
+  MinidumpEmitter.cpp
   MinidumpYAML.cpp
+  WasmEmitter.cpp
   WasmYAML.cpp
   XCOFFYAML.cpp
   YAML.cpp
+  yaml2obj.cpp
+
+  ADDITIONAL_HEADER_DIRS
+  ${LLVM_MAIN_INCLUDE_DIR}/llvm/ObjectYAML
   )
diff --git a/llvm/tools/yaml2obj/yaml2coff.cpp b/llvm/lib/ObjectYAML/COFFEmitter.cpp
similarity index 91%
rename from llvm/tools/yaml2obj/yaml2coff.cpp
rename to llvm/lib/ObjectYAML/COFFEmitter.cpp
index 3afbd58..d94cdbf 100644
--- a/llvm/tools/yaml2obj/yaml2coff.cpp
+++ b/llvm/lib/ObjectYAML/COFFEmitter.cpp
@@ -11,7 +11,6 @@
 ///
 //===----------------------------------------------------------------------===//
 
-#include "yaml2obj.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringMap.h"
@@ -20,6 +19,7 @@
 #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"
@@ -29,6 +29,8 @@
 
 using namespace llvm;
 
+namespace {
+
 /// This parses a yaml stream that represents a COFF object file.
 /// See docs/yaml2obj for the yaml scheema.
 struct COFFParser {
@@ -64,7 +66,8 @@
 
   bool parseSections() {
     for (std::vector<COFFYAML::Section>::iterator i = Obj.Sections.begin(),
-           e = Obj.Sections.end(); i != e; ++i) {
+                                                  e = Obj.Sections.end();
+         i != e; ++i) {
       COFFYAML::Section &Sec = *i;
 
       // If the name is less than 8 bytes, store it in place, otherwise
@@ -102,7 +105,8 @@
 
   bool parseSymbols() {
     for (std::vector<COFFYAML::Symbol>::iterator i = Obj.Symbols.begin(),
-           e = Obj.Symbols.end(); i != e; ++i) {
+                                                 e = Obj.Symbols.end();
+         i != e; ++i) {
       COFFYAML::Symbol &Sym = *i;
 
       // If the name is less than 8 bytes, store it in place, otherwise
@@ -113,8 +117,8 @@
       } 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;
+        *reinterpret_cast<support::aligned_ulittle32_t *>(Sym.Header.Name + 4) =
+            Index;
       }
 
       Sym.Header.Type = Sym.SimpleType;
@@ -153,6 +157,10 @@
   uint32_t SectionTableSize;
 };
 
+enum { DOSStubSize = 128 };
+
+} // end anonymous namespace
+
 // Take a CP and assign addresses and sizes to everything. Returns false if the
 // layout is not valid to do.
 static bool layoutOptionalHeader(COFFParser &CP) {
@@ -166,10 +174,6 @@
   return true;
 }
 
-namespace {
-enum { DOSStubSize = 128 };
-}
-
 static yaml::BinaryRef
 toDebugS(ArrayRef<CodeViewYAML::YAMLDebugSubsection> Subsections,
          const codeview::StringsAndChecksums &SC, BumpPtrAllocator &Allocator) {
@@ -271,7 +275,7 @@
   uint32_t NumberOfSymbols = 0;
   for (std::vector<COFFYAML::Symbol>::iterator i = CP.Obj.Symbols.begin(),
                                                e = CP.Obj.Symbols.end();
-                                               i != e; ++i) {
+       i != e; ++i) {
     uint32_t NumberOfAuxSymbols = 0;
     if (i->FunctionDefinition)
       NumberOfAuxSymbols += 1;
@@ -298,24 +302,23 @@
   else
     CP.Obj.Header.PointerToSymbolTable = 0;
 
-  *reinterpret_cast<support::ulittle32_t *>(&CP.StringTable[0])
-    = CP.StringTable.size();
+  *reinterpret_cast<support::ulittle32_t *>(&CP.StringTable[0]) =
+      CP.StringTable.size();
 
   return true;
 }
 
-template <typename value_type>
-struct binary_le_impl {
+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) {
+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);
+      Buffer, BLE.Value);
   OS.write(Buffer, sizeof(BLE.Value));
   return OS;
 }
@@ -335,13 +338,13 @@
   return OS;
 }
 
-template <typename T>
-zeros_impl<sizeof(T)> zeros(const T &) {
+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) {
+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;
@@ -376,10 +379,8 @@
       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->MajorImageVersion = CP.Obj.OptionalHeader->Header.MajorImageVersion;
+  Header->MinorImageVersion = CP.Obj.OptionalHeader->Header.MinorImageVersion;
   Header->MajorSubsystemVersion =
       CP.Obj.OptionalHeader->Header.MajorSubsystemVersion;
   Header->MinorSubsystemVersion =
@@ -423,15 +424,13 @@
   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(
+              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)
+    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 {
@@ -450,7 +449,8 @@
       OS.write(reinterpret_cast<char *>(&PEH), sizeof(PEH));
     } else {
       object::pe32_header PEH;
-      uint32_t BaseOfData = initializeOptionalHeader(CP, COFF::PE32Header::PE32, &PEH);
+      uint32_t BaseOfData =
+          initializeOptionalHeader(CP, COFF::PE32Header::PE32, &PEH);
       PEH.BaseOfData = BaseOfData;
       OS.write(reinterpret_cast<char *>(&PEH), sizeof(PEH));
     }
@@ -472,7 +472,7 @@
   // Output section table.
   for (std::vector<COFFYAML::Section>::iterator i = CP.Obj.Sections.begin(),
                                                 e = CP.Obj.Sections.end();
-                                                i != e; ++i) {
+       i != e; ++i) {
     OS.write(i->Header.Name, COFF::NameSize);
     OS << binary_le(i->Header.VirtualSize)
        << binary_le(i->Header.VirtualAddress)
@@ -514,8 +514,7 @@
       } else {
         SymbolTableIndex = SymbolTableIndexMap[R.SymbolName];
       }
-      OS << binary_le(R.VirtualAddress)
-         << binary_le(SymbolTableIndex)
+      OS << binary_le(R.VirtualAddress) << binary_le(SymbolTableIndex)
          << binary_le(R.Type);
     }
   }
@@ -524,15 +523,14 @@
 
   for (std::vector<COFFYAML::Symbol>::const_iterator i = CP.Obj.Symbols.begin(),
                                                      e = CP.Obj.Symbols.end();
-                                                     i != e; ++i) {
+       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);
+      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)
+      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) {
@@ -578,8 +576,7 @@
       OS.write_zeros(CP.getSymbolSize() - COFF::Symbol16Size);
     }
     if (i->CLRToken) {
-      OS << binary_le(i->CLRToken->AuxType)
-         << zeros(i->CLRToken->unused1)
+      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);
@@ -592,6 +589,9 @@
   return true;
 }
 
+namespace llvm {
+namespace yaml {
+
 int yaml2coff(llvm::COFFYAML::Object &Doc, raw_ostream &Out) {
   COFFParser CP(Doc);
   if (!CP.parse()) {
@@ -614,3 +614,6 @@
   }
   return 0;
 }
+
+} // namespace yaml
+} // namespace llvm
diff --git a/llvm/tools/yaml2obj/yaml2elf.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp
similarity index 96%
rename from llvm/tools/yaml2obj/yaml2elf.cpp
rename to llvm/lib/ObjectYAML/ELFEmitter.cpp
index bcb390b..370d620 100644
--- a/llvm/tools/yaml2obj/yaml2elf.cpp
+++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp
@@ -11,13 +11,13 @@
 ///
 //===----------------------------------------------------------------------===//
 
-#include "yaml2obj.h"
 #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/ADT/StringSet.h"
+#include "llvm/ObjectYAML/yaml2obj.h"
 #include "llvm/Support/EndianStream.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/WithColor.h"
@@ -55,11 +55,9 @@
   }
   void writeBlobToStream(raw_ostream &Out) { Out << OS.str(); }
 };
-} // end anonymous namespace
 
 // Used to keep track of section and symbol names, so that in the YAML file
 // sections and symbols can be referenced by name instead of by index.
-namespace {
 class NameToIdxMap {
   StringMap<unsigned> Map;
 
@@ -86,29 +84,11 @@
   }
   unsigned size() const { return Map.size(); }
 };
-} // 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));
-}
-
-namespace {
 /// "Single point of truth" for the ELF file construction.
 /// TODO: This class still has a ways to go before it is truly a "single
 /// point of truth".
-template <class ELFT>
-class ELFState {
+template <class ELFT> class ELFState {
   typedef typename ELFT::Ehdr Elf_Ehdr;
   typedef typename ELFT::Phdr Elf_Phdr;
   typedef typename ELFT::Shdr Elf_Shdr;
@@ -185,8 +165,17 @@
 };
 } // end anonymous namespace
 
-template <class ELFT>
-ELFState<ELFT>::ELFState(ELFYAML::Object &D) : Doc(D) {
+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) : Doc(D) {
   StringSet<> DocSections;
   for (std::unique_ptr<ELFYAML::Section> &D : Doc.Sections)
     if (!D->Name.empty())
@@ -197,7 +186,7 @@
     Doc.Sections.insert(
         Doc.Sections.begin(),
         llvm::make_unique<ELFYAML::Section>(
-          ELFYAML::Section::SectionKind::RawContent, /*IsImplicit=*/true));
+            ELFYAML::Section::SectionKind::RawContent, /*IsImplicit=*/true));
 
   std::vector<StringRef> ImplicitSections = {".symtab", ".strtab", ".shstrtab"};
   if (!Doc.DynamicSymbols.empty())
@@ -216,8 +205,7 @@
   }
 }
 
-template <class ELFT>
-void ELFState<ELFT>::initELFHeader(Elf_Ehdr &Header) {
+template <class ELFT> void ELFState<ELFT>::initELFHeader(Elf_Ehdr &Header) {
   using namespace llvm::ELF;
   zero(Header);
   Header.e_ident[EI_MAG0] = 0x7f;
@@ -700,10 +688,9 @@
 }
 
 template <class ELFT>
-bool
-ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader,
-                                    const ELFYAML::RelocationSection &Section,
-                                    ContiguousBlobAccumulator &CBA) {
+bool 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");
@@ -949,7 +936,8 @@
   else
     SHeader.sh_entsize = sizeof(Elf_Dyn);
 
-  raw_ostream &OS = CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign);
+  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);
@@ -1075,6 +1063,9 @@
   return 0;
 }
 
+namespace llvm {
+namespace yaml {
+
 int yaml2elf(llvm::ELFYAML::Object &Doc, raw_ostream &Out) {
   bool IsLE = Doc.Header.Data == ELFYAML::ELF_ELFDATA(ELF::ELFDATA2LSB);
   bool Is64Bit = Doc.Header.Class == ELFYAML::ELF_ELFCLASS(ELF::ELFCLASS64);
@@ -1087,3 +1078,6 @@
     return ELFState<object::ELF32LE>::writeELF(Out, Doc);
   return ELFState<object::ELF32BE>::writeELF(Out, Doc);
 }
+
+} // namespace yaml
+} // namespace llvm
diff --git a/llvm/lib/ObjectYAML/LLVMBuild.txt b/llvm/lib/ObjectYAML/LLVMBuild.txt
index c921236..de43aaf 100644
--- a/llvm/lib/ObjectYAML/LLVMBuild.txt
+++ b/llvm/lib/ObjectYAML/LLVMBuild.txt
@@ -10,4 +10,4 @@
 type = Library
 name = ObjectYAML
 parent = Libraries
-required_libraries = Object Support DebugInfoCodeView
+required_libraries = Object Support DebugInfoCodeView MC
diff --git a/llvm/tools/yaml2obj/yaml2macho.cpp b/llvm/lib/ObjectYAML/MachOEmitter.cpp
similarity index 97%
rename from llvm/tools/yaml2obj/yaml2macho.cpp
rename to llvm/lib/ObjectYAML/MachOEmitter.cpp
index 9dcc7d1..e7789d0 100644
--- a/llvm/tools/yaml2obj/yaml2macho.cpp
+++ b/llvm/lib/ObjectYAML/MachOEmitter.cpp
@@ -11,10 +11,10 @@
 ///
 //===----------------------------------------------------------------------===//
 
-#include "yaml2obj.h"
 #include "llvm/BinaryFormat/MachO.h"
 #include "llvm/ObjectYAML/DWARFEmitter.h"
 #include "llvm/ObjectYAML/ObjectYAML.h"
+#include "llvm/ObjectYAML/yaml2obj.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/LEB128.h"
 #include "llvm/Support/YAMLTraits.h"
@@ -263,8 +263,7 @@
 }
 
 static bool isVirtualSection(uint8_t type) {
-  return (type == MachO::S_ZEROFILL ||
-          type == MachO::S_GB_ZEROFILL ||
+  return (type == MachO::S_ZEROFILL || type == MachO::S_GB_ZEROFILL ||
           type == MachO::S_THREAD_LOCAL_ZEROFILL);
 }
 
@@ -276,7 +275,8 @@
     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)) {
+      if (0 ==
+          strncmp(&LC.Data.segment_command_data.segname[0], "__LINKEDIT", 16)) {
         FoundLinkEditSeg = true;
         if (auto Err = writeLinkEditData(OS))
           return Err;
@@ -592,7 +592,10 @@
 
 } // end anonymous namespace
 
-int yaml2macho(yaml::YamlObjectFile &Doc, raw_ostream &Out) {
+namespace llvm {
+namespace yaml {
+
+int yaml2macho(YamlObjectFile &Doc, raw_ostream &Out) {
   UniversalWriter Writer(Doc);
   if (auto Err = Writer.writeMachO(Out)) {
     errs() << toString(std::move(Err));
@@ -600,3 +603,6 @@
   }
   return 0;
 }
+
+} // namespace yaml
+} // namespace llvm
diff --git a/llvm/tools/yaml2obj/yaml2minidump.cpp b/llvm/lib/ObjectYAML/MinidumpEmitter.cpp
similarity index 82%
rename from llvm/tools/yaml2obj/yaml2minidump.cpp
rename to llvm/lib/ObjectYAML/MinidumpEmitter.cpp
index a9a2947..3e3a254 100644
--- a/llvm/tools/yaml2obj/yaml2minidump.cpp
+++ b/llvm/lib/ObjectYAML/MinidumpEmitter.cpp
@@ -6,13 +6,19 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "yaml2obj.h"
 #include "llvm/ObjectYAML/MinidumpYAML.h"
+#include "llvm/ObjectYAML/yaml2obj.h"
 #include "llvm/Support/raw_ostream.h"
 
 using namespace llvm;
 
+namespace llvm {
+namespace yaml {
+
 int yaml2minidump(MinidumpYAML::Object &Doc, raw_ostream &Out) {
   writeAsBinary(Doc, Out);
   return 0;
 }
+
+} // namespace yaml
+} // namespace llvm
diff --git a/llvm/tools/yaml2obj/yaml2wasm.cpp b/llvm/lib/ObjectYAML/WasmEmitter.cpp
similarity index 98%
rename from llvm/tools/yaml2obj/yaml2wasm.cpp
rename to llvm/lib/ObjectYAML/WasmEmitter.cpp
index 758c498..5769d7b 100644
--- a/llvm/tools/yaml2obj/yaml2wasm.cpp
+++ b/llvm/lib/ObjectYAML/WasmEmitter.cpp
@@ -14,11 +14,13 @@
 
 #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 {
@@ -58,6 +60,26 @@
   uint32_t NumImportedEvents = 0;
 };
 
+class SubSectionWriter {
+  raw_ostream &OS;
+  std::string OutString;
+  raw_string_ostream StringStream;
+
+public:
+  SubSectionWriter(raw_ostream &OS) : OS(OS), StringStream(OutString) {}
+
+  void done() {
+    StringStream.flush();
+    encodeULEB128(OutString.size(), OS);
+    OS << OutString;
+    OutString.clear();
+  }
+
+  raw_ostream &getStream() { return StringStream; }
+};
+
+} // end anonymous namespace
+
 static int writeUint64(raw_ostream &OS, uint64_t Value) {
   char Data[sizeof(Value)];
   support::endian::write64le(Data, Value);
@@ -119,24 +141,6 @@
   return 0;
 }
 
-class SubSectionWriter {
-  raw_ostream &OS;
-  std::string OutString;
-  raw_string_ostream StringStream;
-
-public:
-  SubSectionWriter(raw_ostream &OS) : OS(OS), StringStream(OutString) {}
-
-  void done() {
-    StringStream.flush();
-    encodeULEB128(OutString.size(), OS);
-    OS << OutString;
-    OutString.clear();
-  }
-
-  raw_ostream &getStream() { return StringStream; }
-};
-
 int WasmWriter::writeSectionContent(raw_ostream &OS,
                                     WasmYAML::DylinkSection &Section) {
   writeStringRef(Section.Name, OS);
@@ -651,8 +655,14 @@
   return 0;
 }
 
-int yaml2wasm(llvm::WasmYAML::Object &Doc, raw_ostream &Out) {
+namespace llvm {
+namespace yaml {
+
+int yaml2wasm(WasmYAML::Object &Doc, raw_ostream &Out) {
   WasmWriter Writer(Doc);
 
   return Writer.writeWasm(Out);
 }
+
+} // namespace yaml
+} // namespace llvm
diff --git a/llvm/lib/ObjectYAML/yaml2obj.cpp b/llvm/lib/ObjectYAML/yaml2obj.cpp
new file mode 100644
index 0000000..f9bcba6
--- /dev/null
+++ b/llvm/lib/ObjectYAML/yaml2obj.cpp
@@ -0,0 +1,68 @@
+//===-- yaml2obj.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ObjectYAML/yaml2obj.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/ObjectYAML/ObjectYAML.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/YAMLTraits.h"
+
+namespace llvm {
+namespace yaml {
+
+Error convertYAML(yaml::Input &YIn, raw_ostream &Out, unsigned DocNum) {
+  // TODO: make yaml2* functions return Error instead of int.
+  auto IntToErr = [](int Ret) -> Error {
+    if (Ret)
+      return createStringError(errc::invalid_argument, "yaml2obj failed");
+    return Error::success();
+  };
+
+  unsigned CurDocNum = 0;
+  do {
+    if (++CurDocNum == DocNum) {
+      yaml::YamlObjectFile Doc;
+      YIn >> Doc;
+      if (std::error_code EC = YIn.error())
+        return createStringError(EC, "Failed to parse YAML input!");
+      if (Doc.Elf)
+        return IntToErr(yaml2elf(*Doc.Elf, Out));
+      if (Doc.Coff)
+        return IntToErr(yaml2coff(*Doc.Coff, Out));
+      if (Doc.MachO || Doc.FatMachO)
+        return IntToErr(yaml2macho(Doc, Out));
+      if (Doc.Minidump)
+        return IntToErr(yaml2minidump(*Doc.Minidump, Out));
+      if (Doc.Wasm)
+        return IntToErr(yaml2wasm(*Doc.Wasm, Out));
+      return createStringError(errc::invalid_argument,
+                               "Unknown document type!");
+    }
+  } while (YIn.nextDocument());
+
+  return createStringError(errc::invalid_argument,
+                           "Cannot find the %u%s document", DocNum,
+                           getOrdinalSuffix(DocNum).data());
+}
+
+Expected<std::unique_ptr<object::ObjectFile>>
+yaml2ObjectFile(SmallVectorImpl<char> &Storage, StringRef Yaml) {
+  Storage.clear();
+  raw_svector_ostream OS(Storage);
+
+  yaml::Input YIn(Yaml);
+  if (Error E = convertYAML(YIn, OS))
+    return std::move(E);
+
+  return object::ObjectFile::createObjectFile(
+      MemoryBufferRef(OS.str(), "YamlObject"));
+}
+
+} // namespace yaml
+} // namespace llvm
diff --git a/llvm/test/tools/yaml2obj/empty-or-invalid-doc.yaml b/llvm/test/tools/yaml2obj/empty-or-invalid-doc.yaml
index 450f2f7..de79f41 100644
--- a/llvm/test/tools/yaml2obj/empty-or-invalid-doc.yaml
+++ b/llvm/test/tools/yaml2obj/empty-or-invalid-doc.yaml
@@ -2,7 +2,7 @@
 # RUN: echo -n "" | not yaml2obj 2>&1 | FileCheck %s
 # RUN: echo " " | not yaml2obj 2>&1 | FileCheck %s
 # RUN: echo "  " | not yaml2obj 2>&1 | FileCheck %s
-# CHECK: yaml2obj: Unknown document type!
+# CHECK: yaml2obj: error: Unknown document type!
 
 # RUN: echo -e -n "\xff" | not yaml2obj 2>&1 | FileCheck %s --check-prefix=INVALID
-# INVALID: yaml2obj: Failed to parse YAML file!
+# INVALID: yaml2obj: error: Failed to parse YAML input!
diff --git a/llvm/test/tools/yaml2obj/invalid-docnum.test b/llvm/test/tools/yaml2obj/invalid-docnum.test
new file mode 100644
index 0000000..0f79602
--- /dev/null
+++ b/llvm/test/tools/yaml2obj/invalid-docnum.test
@@ -0,0 +1,22 @@
+## Test that an error is reported when a docnum is specified, which is
+## greater than the number of YAML inputs in the file.
+
+# RUN: not yaml2obj %s --docnum=3 2>&1 | FileCheck %s
+# CHECK: yaml2obj: error: Cannot find the 3rd document
+
+# RUN: not yaml2obj %s --docnum=76768677 2>&1 | FileCheck %s --check-prefix=TWO
+# TWO: yaml2obj: error: Cannot find the 76768677th document
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_DYN
+  Machine: EM_X86_64
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_DYN
+  Machine: EM_X86_64
diff --git a/llvm/test/tools/yaml2obj/missing_document_tag.yaml b/llvm/test/tools/yaml2obj/missing_document_tag.yaml
index 8cfd9a1..d41e799 100644
--- a/llvm/test/tools/yaml2obj/missing_document_tag.yaml
+++ b/llvm/test/tools/yaml2obj/missing_document_tag.yaml
@@ -6,4 +6,4 @@
 ...
 
 # CHECK: YAML:4:1: error: YAML Object File missing document type tag!
-# CHECK: yaml2obj: Failed to parse YAML file!
+# CHECK: yaml2obj: error: Failed to parse YAML input!
diff --git a/llvm/test/tools/yaml2obj/multi-doc.test b/llvm/test/tools/yaml2obj/multi-doc.test
index 30e7c1e..9d3762b 100644
--- a/llvm/test/tools/yaml2obj/multi-doc.test
+++ b/llvm/test/tools/yaml2obj/multi-doc.test
@@ -16,7 +16,7 @@
 # DOC2: Name: _sym2
 # DOC3: Name: _sym3
 # DOC4: Name: _sym4
-# DOC5: yaml2obj: Cannot find the 5th document
+# DOC5: yaml2obj: error: Cannot find the 5th document
 
 --- !ELF
 FileHeader: !FileHeader
diff --git a/llvm/test/tools/yaml2obj/section-size-content.yaml b/llvm/test/tools/yaml2obj/section-size-content.yaml
index 1c9c00d..b57526a 100644
--- a/llvm/test/tools/yaml2obj/section-size-content.yaml
+++ b/llvm/test/tools/yaml2obj/section-size-content.yaml
@@ -175,4 +175,4 @@
 # ERR2:      error: Section size must be greater than or equal to the content size
 # ERR2-NEXT: - Name: .data
 # ERR2-NEXT:   ^
-# ERR2-NEXT: yaml2obj: Failed to parse YAML file!
+# ERR2-NEXT: yaml2obj: error: Failed to parse YAML input!
diff --git a/llvm/tools/yaml2obj/CMakeLists.txt b/llvm/tools/yaml2obj/CMakeLists.txt
index 1c61fb7..0cbc8e0 100644
--- a/llvm/tools/yaml2obj/CMakeLists.txt
+++ b/llvm/tools/yaml2obj/CMakeLists.txt
@@ -1,16 +1,8 @@
 set(LLVM_LINK_COMPONENTS
-  DebugInfoCodeView
-  MC
-  Object
   ObjectYAML
   Support
   )
 
 add_llvm_tool(yaml2obj
   yaml2obj.cpp
-  yaml2coff.cpp
-  yaml2elf.cpp
-  yaml2macho.cpp
-  yaml2minidump.cpp
-  yaml2wasm.cpp
   )
diff --git a/llvm/tools/yaml2obj/yaml2obj.cpp b/llvm/tools/yaml2obj/yaml2obj.cpp
index 533c8ea..76e6663 100644
--- a/llvm/tools/yaml2obj/yaml2obj.cpp
+++ b/llvm/tools/yaml2obj/yaml2obj.cpp
@@ -13,7 +13,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "yaml2obj.h"
+#include "llvm/ObjectYAML/yaml2obj.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ObjectYAML/ObjectYAML.h"
 #include "llvm/Support/CommandLine.h"
@@ -21,6 +21,7 @@
 #include "llvm/Support/InitLLVM.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
 #include "llvm/Support/YAMLTraits.h"
 #include "llvm/Support/raw_ostream.h"
 #include <system_error>
@@ -42,32 +43,6 @@
   exit(1);
 }
 
-static int convertYAML(yaml::Input &YIn, raw_ostream &Out) {
-  unsigned CurDocNum = 0;
-  do {
-    if (++CurDocNum == DocNum) {
-      yaml::YamlObjectFile Doc;
-      YIn >> Doc;
-      if (YIn.error())
-        error("yaml2obj: Failed to parse YAML file!");
-      if (Doc.Elf)
-        return yaml2elf(*Doc.Elf, Out);
-      if (Doc.Coff)
-        return yaml2coff(*Doc.Coff, Out);
-      if (Doc.MachO || Doc.FatMachO)
-        return yaml2macho(Doc, Out);
-      if (Doc.Minidump)
-        return yaml2minidump(*Doc.Minidump, Out);
-      if (Doc.Wasm)
-        return yaml2wasm(*Doc.Wasm, Out);
-      error("yaml2obj: Unknown document type!");
-    }
-  } while (YIn.nextDocument());
-
-  error("yaml2obj: Cannot find the " + Twine(DocNum) +
-        llvm::getOrdinalSuffix(DocNum) + " document");
-}
-
 int main(int argc, char **argv) {
   InitLLVM X(argc, argv);
   cl::ParseCommandLineOptions(argc, argv);
@@ -87,10 +62,12 @@
     return 1;
 
   yaml::Input YIn(Buf.get()->getBuffer());
-  int Res = convertYAML(YIn, Out->os());
-  if (Res == 0)
-    Out->keep();
+  if (Error E = convertYAML(YIn, Out->os(), DocNum)) {
+    logAllUnhandledErrors(std::move(E), WithColor::error(errs(), argv[0]));
+    return 1;
+  }
 
+  Out->keep();
   Out->os().flush();
-  return Res;
+  return 0;
 }
diff --git a/llvm/tools/yaml2obj/yaml2obj.h b/llvm/tools/yaml2obj/yaml2obj.h
deleted file mode 100644
index 60c2987..0000000
--- a/llvm/tools/yaml2obj/yaml2obj.h
+++ /dev/null
@@ -1,45 +0,0 @@
-//===--- yaml2obj.h - -------------------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-/// \file
-/// Common declarations for yaml2obj
-//===----------------------------------------------------------------------===//
-#ifndef LLVM_TOOLS_YAML2OBJ_YAML2OBJ_H
-#define LLVM_TOOLS_YAML2OBJ_YAML2OBJ_H
-
-namespace llvm {
-class raw_ostream;
-
-namespace COFFYAML {
-struct Object;
-}
-
-namespace ELFYAML {
-struct Object;
-}
-
-namespace MinidumpYAML {
-struct Object;
-}
-
-namespace WasmYAML {
-struct Object;
-}
-
-namespace yaml {
-class Input;
-struct YamlObjectFile;
-}
-}
-
-int yaml2coff(llvm::COFFYAML::Object &Doc, llvm::raw_ostream &Out);
-int yaml2elf(llvm::ELFYAML::Object &Doc, llvm::raw_ostream &Out);
-int yaml2macho(llvm::yaml::YamlObjectFile &Doc, llvm::raw_ostream &Out);
-int yaml2minidump(llvm::MinidumpYAML::Object &Doc, llvm::raw_ostream &Out);
-int yaml2wasm(llvm::WasmYAML::Object &Doc, llvm::raw_ostream &Out);
-
-#endif
diff --git a/llvm/unittests/ObjectYAML/CMakeLists.txt b/llvm/unittests/ObjectYAML/CMakeLists.txt
index 7fcc974..45e9c67 100644
--- a/llvm/unittests/ObjectYAML/CMakeLists.txt
+++ b/llvm/unittests/ObjectYAML/CMakeLists.txt
@@ -5,6 +5,7 @@
 
 add_llvm_unittest(ObjectYAMLTests
   MinidumpYAMLTest.cpp
+  YAML2ObjTest.cpp
   YAMLTest.cpp
   )
 
diff --git a/llvm/unittests/ObjectYAML/YAML2ObjTest.cpp b/llvm/unittests/ObjectYAML/YAML2ObjTest.cpp
new file mode 100644
index 0000000..0a38388
--- /dev/null
+++ b/llvm/unittests/ObjectYAML/YAML2ObjTest.cpp
@@ -0,0 +1,36 @@
+//===- YAML2ObjTest.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ObjectYAML/yaml2obj.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace object;
+using namespace yaml;
+
+TEST(yaml2ObjectFile, ELF) {
+  SmallString<0> Storage;
+  Expected<std::unique_ptr<ObjectFile>> ErrOrObj = yaml2ObjectFile(Storage, R"(
+--- !ELF
+FileHeader:
+  Class:    ELFCLASS64
+  Data:     ELFDATA2LSB
+  Type:     ET_REL
+  Machine:  EM_X86_64)");
+
+  ASSERT_THAT_EXPECTED(ErrOrObj, Succeeded());
+
+  std::unique_ptr<ObjectFile> ObjFile = std::move(ErrOrObj.get());
+
+  ASSERT_TRUE(ObjFile->isELF());
+  ASSERT_TRUE(ObjFile->isRelocatableObject());
+}