Index: llvm/include/llvm/ObjectYAML/ArchiveYAML.h =================================================================== --- /dev/null +++ llvm/include/llvm/ObjectYAML/ArchiveYAML.h @@ -0,0 +1,73 @@ +//===- ArchiveYAML.h - Archive YAMLIO implementation ------------*- 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 +/// This file declares classes for handling the YAML representation of archives. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_OBJECTYAML_ARCHIVEYAML_H +#define LLVM_OBJECTYAML_ARCHIVEYAML_H + +#include "llvm/Support/YAMLTraits.h" +#include "llvm/ObjectYAML/YAML.h" +#include "llvm/ADT/MapVector.h" + +namespace llvm { +namespace ArchYAML { + +struct Archive { + struct Entry { + struct Field { + StringRef Value; + unsigned MaxLength; + }; + + Entry() { + Fields["Name"] = {"", 16}; + Fields["LastModified"] = {"", 12}; + Fields["UID"] = {"", 6}; + Fields["GID"] = {"", 6}; + Fields["AccessMode"] = {"", 8}; + Fields["Size"] = {"", 10}; + Fields["Terminator"] = {"", 2}; + } + + MapVector Fields; + + yaml::BinaryRef Content; + Optional PaddingByte; + }; + + StringRef Magic; + Optional> Entries; + Optional Content; +}; + +} // end namespace ArchYAML +} // end namespace llvm + +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ArchYAML::Archive::Entry) + +namespace llvm { +namespace yaml { + +template <> struct MappingTraits { + static void mapping(IO &IO, ArchYAML::Archive &A); + static std::string validate(IO &, ArchYAML::Archive &A); +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, ArchYAML::Archive::Entry &E); + static std::string validate(IO &, ArchYAML::Archive::Entry &E); +}; + +} // end namespace yaml +} // end namespace llvm + +#endif // LLVM_OBJECTYAML_ARCHIVEYAML_H Index: llvm/include/llvm/ObjectYAML/ObjectYAML.h =================================================================== --- llvm/include/llvm/ObjectYAML/ObjectYAML.h +++ llvm/include/llvm/ObjectYAML/ObjectYAML.h @@ -9,6 +9,7 @@ #ifndef LLVM_OBJECTYAML_OBJECTYAML_H #define LLVM_OBJECTYAML_OBJECTYAML_H +#include "llvm/ObjectYAML/ArchiveYAML.h" #include "llvm/ObjectYAML/COFFYAML.h" #include "llvm/ObjectYAML/ELFYAML.h" #include "llvm/ObjectYAML/MachOYAML.h" @@ -23,6 +24,7 @@ class IO; struct YamlObjectFile { + std::unique_ptr Arch; std::unique_ptr Elf; std::unique_ptr Coff; std::unique_ptr MachO; Index: llvm/include/llvm/ObjectYAML/yaml2obj.h =================================================================== --- llvm/include/llvm/ObjectYAML/yaml2obj.h +++ llvm/include/llvm/ObjectYAML/yaml2obj.h @@ -40,12 +40,17 @@ struct Object; } +namespace ArchYAML { +struct Archive; +} + namespace yaml { class Input; struct YamlObjectFile; using ErrorHandler = llvm::function_ref; +bool yaml2arch(ArchYAML::Archive &Doc, raw_ostream &Out, ErrorHandler EH); bool yaml2coff(COFFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH); bool yaml2elf(ELFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH, uint64_t MaxSize); Index: llvm/lib/ObjectYAML/ArchiveEmitter.cpp =================================================================== --- /dev/null +++ llvm/lib/ObjectYAML/ArchiveEmitter.cpp @@ -0,0 +1,50 @@ +//===- ArchiveEmitter.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/ArchiveYAML.h" +#include "llvm/ObjectYAML/yaml2obj.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace ArchYAML; + +namespace llvm { +namespace yaml { + +bool yaml2arch(ArchYAML::Archive &Doc, raw_ostream &Out, ErrorHandler EH) { + Out.write(Doc.Magic.data(), Doc.Magic.size()); + + if (Doc.Content) { + Doc.Content->writeAsBinary(Out); + return true; + } + + if (!Doc.Entries) + return true; + + auto WriteField = [&](StringRef Field, uint8_t Size) { + Out.write(Field.data(), Field.size()); + for (size_t I = Field.size(); I != Size; ++I) + Out.write(' '); + }; + + for (const Archive::Entry &E : *Doc.Entries) { + for (auto &P : E.Fields) + WriteField(P.second.Value, P.second.MaxLength); + + E.Content.writeAsBinary(Out); + if (E.PaddingByte) + Out.write(*E.PaddingByte); + } + + return true; +} + +} // namespace yaml +} // namespace llvm Index: llvm/lib/ObjectYAML/ArchiveYAML.cpp =================================================================== --- /dev/null +++ llvm/lib/ObjectYAML/ArchiveYAML.cpp @@ -0,0 +1,66 @@ +//===- ArchiveYAML.cpp - ELF YAMLIO implementation -------------------- ----===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines classes for handling the YAML representation of archives. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ObjectYAML/ArchiveYAML.h" + +namespace llvm { + +namespace yaml { + +void MappingTraits::mapping(IO &IO, ArchYAML::Archive &A) { + assert(!IO.getContext() && "The IO context is initialized already"); + IO.setContext(&A); + IO.mapTag("!Arch", true); + IO.mapRequired("Magic", A.Magic); + IO.mapOptional("Entries", A.Entries); + IO.mapOptional("Content", A.Content); + + // MappingTraits::validate() is not invoked for + // top-level ArchYAML::Archive YAML description. It is perhaps not a critical + // limitation. Just call validate() directly from here. + std::string Err = MappingTraits::validate(IO, A); + if (!Err.empty()) + IO.setError(Err); + + IO.setContext(nullptr); +} + +std::string MappingTraits::validate(IO &, + ArchYAML::Archive &A) { + if (A.Entries && A.Content) + return "\"Content\" and \"Entries\" cannot be used together"; + return ""; +} + +void MappingTraits::mapping( + IO &IO, ArchYAML::Archive::Entry &E) { + assert(IO.getContext() && "The IO context is not initialized"); + for (auto &P : E.Fields) + IO.mapRequired(P.first.data(), P.second.Value); + IO.mapRequired("Content", E.Content); + IO.mapOptional("PaddingByte", E.PaddingByte); +} + +std::string +MappingTraits::validate(IO &, + ArchYAML::Archive::Entry &E) { + for (auto &P : E.Fields) + if (P.second.Value.size() > P.second.MaxLength) + return ("the maximum length of \"" + P.first + "\" field is " + + Twine(P.second.MaxLength)) + .str(); + return ""; +} + +} // end namespace yaml + +} // end namespace llvm Index: llvm/lib/ObjectYAML/CMakeLists.txt =================================================================== --- llvm/lib/ObjectYAML/CMakeLists.txt +++ llvm/lib/ObjectYAML/CMakeLists.txt @@ -1,4 +1,6 @@ add_llvm_component_library(LLVMObjectYAML + ArchiveEmitter.cpp + ArchiveYAML.cpp CodeViewYAMLDebugSections.cpp CodeViewYAMLSymbols.cpp CodeViewYAMLTypeHashing.cpp Index: llvm/lib/ObjectYAML/ObjectYAML.cpp =================================================================== --- llvm/lib/ObjectYAML/ObjectYAML.cpp +++ llvm/lib/ObjectYAML/ObjectYAML.cpp @@ -33,7 +33,10 @@ *ObjectFile.FatMachO); } else { Input &In = (Input &)IO; - if (IO.mapTag("!ELF")) { + if (IO.mapTag("!Arch")) { + ObjectFile.Arch.reset(new ArchYAML::Archive()); + MappingTraits::mapping(IO, *ObjectFile.Arch); + } else if (IO.mapTag("!ELF")) { ObjectFile.Elf.reset(new ELFYAML::Object()); MappingTraits::mapping(IO, *ObjectFile.Elf); } else if (IO.mapTag("!COFF")) { Index: llvm/lib/ObjectYAML/yaml2obj.cpp =================================================================== --- llvm/lib/ObjectYAML/yaml2obj.cpp +++ llvm/lib/ObjectYAML/yaml2obj.cpp @@ -32,6 +32,8 @@ return false; } + if (Doc.Arch) + return yaml2arch(*Doc.Arch, Out, ErrHandler); if (Doc.Elf) return yaml2elf(*Doc.Elf, Out, ErrHandler, MaxSize); if (Doc.Coff) Index: llvm/test/tools/obj2yaml/Archives/regular.yaml =================================================================== --- /dev/null +++ llvm/test/tools/obj2yaml/Archives/regular.yaml @@ -0,0 +1,100 @@ +## Check how obj2yaml dumps regular archives. + +## Check how we dump an empty archive. + +# RUN: rm -f %t.empty.a +# RUN: llvm-ar rc %t.empty.a +# RUN: obj2yaml %t.empty.a | FileCheck %s --check-prefix=EMPTY + +# EMPTY: --- !Arch +# EMPTY-NEXT: Magic: "!\n" +# EMPTY-NEXT: Entries: [] +# EMPTY-NEXT: ... + +## Check how we dump archives with multiple entries. +## Check we don't dump excessive spaces when dumping fields. + +# RUN: rm -rf %t.dir +# RUN: mkdir -p %t.dir +# RUN: rm -f %t.a +# RUN: echo " aaa " > %t.dir/bbb +# RUN: echo " cccc " > %t.dir/dddd + +# RUN: llvm-ar rc %t.two.a %t.dir/bbb %t.dir/dddd +# RUN: obj2yaml %t.two.a | FileCheck %s --check-prefix=TWO + +# TWO: --- !Arch +# TWO-NEXT: Magic: "!\n" +# TWO-NEXT: Entries: +# TWO-NEXT: - Name: 'bbb/' +# TWO-NEXT: LastModified: '0' +# TWO-NEXT: UID: '0' +# TWO-NEXT: GID: '0' +# TWO-NEXT: AccessMode: '644' +# TWO-NEXT: Size: '6' +# TWO-NEXT: Terminator: "`\n" +# TWO-NEXT: Content: 20616161200A +# TWO-NEXT: - Name: 'dddd/' +# TWO-NEXT: LastModified: '0' +# TWO-NEXT: UID: '0' +# TWO-NEXT: GID: '0' +# TWO-NEXT: AccessMode: '644' +# TWO-NEXT: Size: '7' +# TWO-NEXT: Terminator: "`\n" +# TWO-NEXT: Content: 2063636363200A +# TWO-NEXT: PaddingByte: 0x0A +# TWO-NEXT: ... + +## Check we report an error for non-regular archives. + +# RUN: yaml2obj %s -o %t.not.regular.a +# RUN: not obj2yaml %t.not.regular.a 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t.not.regular.a --check-prefix=NOT-REGULAR-ERR + +# NOT-REGULAR-ERR: Error reading file: [[FILE]]: only regular archives are supported + +--- !Arch +Magic: "!\n" +Entries: + - Name: '' + LastModified: '' + UID: '' + GID: '' + AccessMode: '' + Size: '0' + Terminator: '' + Content: "" + +# RUN: yaml2obj %s --docnum=2 -o %t.truncated.a +# RUN: not obj2yaml %t.truncated.a 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t.truncated.a --check-prefix=TRUNCATED-ERR + +# TRUNCATED-ERR: Error reading file: [[FILE]]: unable to read entry header + +--- !Arch +Magic: "!\n" +Content: "00" + +# RUN: yaml2obj %s --docnum=3 -DSIZE='1' -o %t.entdata.a +# RUN: not obj2yaml %t.entdata.a 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t.entdata.a --check-prefix=ENTDATA-ERR + +# ENTDATA-ERR: Error reading file: [[FILE]]: unable to read entry data + +--- !Arch +Magic: "!\n" +Entries: + - Name: '' + LastModified: '' + UID: '' + GID: '' + AccessMode: '' + Size: [[SIZE='']] + Terminator: '' + Content: "" + +# RUN: yaml2obj %s --docnum=3 -DSIZE='x' -o %t.entsize.a +# RUN: not obj2yaml %t.entsize.a 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t.entsize.a --check-prefix=ENTSIZE-ERR + +# ENTSIZE-ERR: Error reading file: [[FILE]]: unable to read entry size Index: llvm/test/tools/yaml2obj/Archives/regular.yaml =================================================================== --- /dev/null +++ llvm/test/tools/yaml2obj/Archives/regular.yaml @@ -0,0 +1,128 @@ +## Check how yaml2obj creates archives. + +## Check we create an empty section when neither of "Entries" nor "Content" are specified. + +# RUN: yaml2obj --docnum=1 %s -o %t.empty.a +# RUN: llvm-ar -t %t.empty.a | FileCheck %s --allow-empty --implicit-check-not={{.}} +# RUN: wc -c < %t.empty.a | FileCheck %s --check-prefix=EMPTY-SIZE +# RUN: od -t x1 -v %t.empty.a | FileCheck %s --ignore-case --check-prefix=EMPTY-DATA + +# EMPTY-SIZE: 8{{$}} +# EMPTY-DATA: 21 3c 61 72 63 68 3e 0a + +--- !Arch +Magic: "[[MAGIC=!\n]]" +Content: [[CONTENT=]] +Entries: [[ENTRIES=]] + +## Check we report an error when both "Content" and "Entries" keys are used together. + +# RUN: not yaml2obj --docnum=1 -DENTRIES="[]" -DCONTENT="00" %s 2>&1 | FileCheck %s --check-prefix=BOTH + +## BOTH: error: "Content" and "Entries" cannot be used together + +# RUN: yaml2obj --docnum=1 -DCONTENT="12" %s -o %t.content.a +# RUN: od -t x1 -v %t.content.a | FileCheck %s --ignore-case --check-prefix=CONTENT + +# CONTENT: 21 3c 61 72 63 68 3e 0a 12{{$}} + +## Check we can set arbitrary magic bytes. + +# RUN: yaml2obj --docnum=1 -DMAGIC="12345678" %s -o %t.magic.a +# RUN: wc -c < %t.magic.a | FileCheck %s --check-prefix=EMPTY-SIZE +# RUN: od -t x1 -v %t.magic.a | FileCheck %s --ignore-case --check-prefix=MAGIC-DATA + +# MAGIC-DATA: 31 32 33 34 35 36 37 38 + +## Check we can specify magic bytes of size greater than the normal size (size of "!\n"). + +# RUN: yaml2obj --docnum=1 -DMAGIC="123456789" %s -o %t.magic2.a +# RUN: wc -c < %t.magic2.a | FileCheck %s --check-prefix=MAGIC-SIZE-GR +# RUN: od -t x1 -v %t.magic2.a | FileCheck %s --ignore-case --check-prefix=MAGIC-DATA-GR + +# MAGIC-SIZE-GR: 9{{$}} +# MAGIC-DATA-GR: 31 32 33 34 35 36 37 38 39 + +## Check we can specify magic bytes of less greater than the normal size (size of "!\n"). + +# RUN: yaml2obj --docnum=1 -DMAGIC="1234567" %s -o %t.magic3.a +# RUN: wc -c < %t.magic3.a | FileCheck %s --check-prefix=MAGIC-SIZE-LESS +# RUN: od -t x1 -v %t.magic3.a | FileCheck %s --ignore-case --check-prefix=MAGIC-DATA-LESS + +# MAGIC-SIZE-LESS: 7{{$}} +# MAGIC-DATA-LESS: 31 32 33 34 35 36 37 + +## Check we can produce a valid archive with multiple entries. + +# RUN: yaml2obj --docnum=2 %s -o %t.two.a +# RUN: llvm-ar -t %t.two.a | FileCheck %s --check-prefix=TWO +# RUN: od -t x1 -v %t.two.a | FileCheck %s --ignore-case --check-prefix=TWO-DATA + +# TWO: {{^}}bbbbbbbbbbbbbbb{{$}} +# TWO-NEXT: {{^}}a{{$}} +# TWO-NOT: {{.}} + +# TWO-DATA: 21 3c 61 72 63 68 3e 0a 62 62 62 62 62 62 62 62 +# TWO-DATA-NEXT: 62 62 62 62 62 62 62 2f 31 32 33 34 35 36 37 38 +# TWO-DATA-NEXT: 39 30 61 62 71 77 65 72 74 79 61 73 64 66 67 68 +# TWO-DATA-NEXT: 38 37 36 35 34 33 32 31 37 20 20 20 20 20 20 20 +# TWO-DATA-NEXT: 20 20 60 0a 20 63 63 63 63 20 0a ee 61 2f 20 20 +# TWO-DATA-NEXT: 20 20 20 20 20 20 20 20 20 20 20 20 31 20 20 20 +# TWO-DATA-NEXT: 20 20 20 20 20 20 20 20 32 20 20 20 20 20 33 20 +# TWO-DATA-NEXT: 20 20 20 20 34 35 36 20 20 20 20 20 36 20 20 20 +# TWO-DATA-NEXT: 20 20 20 20 20 20 60 0a 20 61 61 61 20 0a{{$}} + +--- !Arch +Magic: "!\n" +Entries: +## An arbitrary entry where each of fields has maximum allowed length. + - Name: 'bbbbbbbbbbbbbbb/' + LastModified: '1234567890ab' + UID: 'qwerty' + GID: 'asdfgh' + AccessMode: '87654321' + Size: '7' + Terminator: "`\n" + Content: "2063636363200A" + PaddingByte: 0xEE +## An arbitrary entry to demonstrate that we use the 0x20 byte (space character) +## to fulfill gaps between field values. + - Name: 'a/' + LastModified: '1' + UID: '2' + GID: '3' + AccessMode: '456' + Size: '6' + Terminator: "`\n" + Content: "20616161200A" + +## Check how we validate maximum sizes of fields. + +# RUN: not yaml2obj --docnum=3 -DNAME="123456789ABCDEF01" %s 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERROR -DFIELD="Name" -DVAL=16 +# RUN: not yaml2obj --docnum=3 -DLAST="123456789ABCD" %s 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERROR -DFIELD="LastModified" -DVAL=12 +# RUN: not yaml2obj --docnum=3 -DUID="1234567" %s 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERROR -DFIELD="UID" -DVAL=6 +# RUN: not yaml2obj --docnum=3 -DGID="1234567" %s 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERROR -DFIELD="GID" -DVAL=6 +# RUN: not yaml2obj --docnum=3 -DACCESSMODE="123456789" %s 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERROR -DFIELD="AccessMode" -DVAL=8 +# RUN: not yaml2obj --docnum=3 -DSIZE="123456789AB" %s 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERROR -DFIELD="Size" -DVAL=10 +# RUN: not yaml2obj --docnum=3 -DTERMINATOR="123" %s 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERROR -DFIELD="Terminator" -DVAL=2 + +# ERROR: error: the maximum length of "[[FIELD]]" field is [[VAL]] + +--- !Arch +Magic: "!\n" +Entries: + - Name: '[[NAME=""]]' + LastModified: '[[LAST=""]]' + UID: '[[UID=""]]' + GID: '[[GID=""]]' + AccessMode: '[[ACCESSMODE=""]]' + Size: '[[SIZE=""]]' + Terminator: '[[TERMINATOR=""]]' + Content: '' Index: llvm/tools/obj2yaml/CMakeLists.txt =================================================================== --- llvm/tools/obj2yaml/CMakeLists.txt +++ llvm/tools/obj2yaml/CMakeLists.txt @@ -8,6 +8,7 @@ ) add_llvm_utility(obj2yaml + archive2yaml.cpp obj2yaml.cpp coff2yaml.cpp dwarf2yaml.cpp Index: llvm/tools/obj2yaml/archive2yaml.cpp =================================================================== --- /dev/null +++ llvm/tools/obj2yaml/archive2yaml.cpp @@ -0,0 +1,103 @@ +//===------ utils/archive2yaml.cpp - obj2yaml conversion tool ---*- 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 +// +//===----------------------------------------------------------------------===// + +#include "obj2yaml.h" +#include "llvm/BinaryFormat/Magic.h" +#include "llvm/ObjectYAML/ArchiveYAML.h" + +using namespace llvm; + +namespace { + +class ArchiveDumper final { +public: + Expected dump(MemoryBufferRef Source) { + StringRef Buffer = Source.getBuffer(); + assert(file_magic::archive == identify_magic(Buffer)); + + std::unique_ptr Obj = + std::make_unique(); + + StringRef Magic = "!\n"; + if (!Buffer.startswith(Magic)) + return createStringError(std::errc::illegal_byte_sequence, + "only regular archives are supported"); + Obj->Magic = Magic; + Buffer = Buffer.drop_front(Magic.size()); + + Obj->Entries.emplace(); + while (!Buffer.empty()) { + if (Buffer.size() < sizeof(ArchiveHeader)) + return createStringError(std::errc::illegal_byte_sequence, + "unable to read entry header"); + + const ArchiveHeader &Hdr = + *reinterpret_cast(Buffer.data()); + Buffer = Buffer.drop_front(sizeof(ArchiveHeader)); + + auto ToString = [](ArrayRef V) { + // We don't want to dump excessive spaces. + return StringRef(V.data(), V.size()).rtrim(' '); + }; + + ArchYAML::Archive::Entry E; + E.Fields["Name"].Value = ToString(Hdr.Name); + E.Fields["LastModified"].Value = ToString(Hdr.LastModified); + E.Fields["UID"].Value = ToString(Hdr.UID); + E.Fields["GID"].Value = ToString(Hdr.GID); + E.Fields["AccessMode"].Value = ToString(Hdr.AccessMode); + E.Fields["Size"].Value = ToString(Hdr.Size); + E.Fields["Terminator"].Value = ToString(Hdr.Terminator); + + uint64_t Size; + if (E.Fields["Size"].Value.getAsInteger(10, Size)) + return createStringError(std::errc::illegal_byte_sequence, + "unable to read entry size"); + if (Buffer.size() < Size) + return createStringError(std::errc::illegal_byte_sequence, + "unable to read entry data"); + E.Content = arrayRefFromStringRef(Buffer.take_front(Size)); + + const bool HasPaddingByte = (Size & 1) && Buffer.size() > Size; + if (HasPaddingByte) + E.PaddingByte = Buffer[Size]; + + Obj->Entries->push_back(E); + // If the size is odd, consume a padding byte. + Buffer = Buffer.drop_front(HasPaddingByte ? Size + 1 : Size); + } + + return Obj.release(); + } + +private: + struct ArchiveHeader { + char Name[16]; + char LastModified[12]; + char UID[6]; + char GID[6]; + char AccessMode[8]; + char Size[10]; + char Terminator[2]; + }; +}; + +} // namespace + +Error archive2yaml(raw_ostream &Out, MemoryBufferRef Source) { + ArchiveDumper Dumper; + Expected YAMLOrErr = Dumper.dump(Source); + if (!YAMLOrErr) + return YAMLOrErr.takeError(); + + std::unique_ptr YAML(YAMLOrErr.get()); + yaml::Output Yout(Out); + Yout << *YAML; + + return Error::success(); +} Index: llvm/tools/obj2yaml/obj2yaml.h =================================================================== --- llvm/tools/obj2yaml/obj2yaml.h +++ llvm/tools/obj2yaml/obj2yaml.h @@ -17,6 +17,7 @@ #include "llvm/Object/Wasm.h" #include "llvm/Object/XCOFFObjectFile.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/MemoryBufferRef.h" #include std::error_code coff2yaml(llvm::raw_ostream &Out, @@ -31,6 +32,7 @@ const llvm::object::XCOFFObjectFile &Obj); std::error_code wasm2yaml(llvm::raw_ostream &Out, const llvm::object::WasmObjectFile &Obj); +llvm::Error archive2yaml(llvm::raw_ostream &Out, llvm::MemoryBufferRef Source); // Forward decls for dwarf2yaml namespace llvm { Index: llvm/tools/obj2yaml/obj2yaml.cpp =================================================================== --- llvm/tools/obj2yaml/obj2yaml.cpp +++ llvm/tools/obj2yaml/obj2yaml.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "obj2yaml.h" +#include "llvm/BinaryFormat/Magic.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" #include "llvm/Object/Minidump.h" @@ -34,16 +35,26 @@ } static Error dumpInput(StringRef File) { - Expected> BinaryOrErr = createBinary(File); - if (!BinaryOrErr) - return BinaryOrErr.takeError(); + ErrorOr> FileOrErr = + MemoryBuffer::getFileOrSTDIN(File, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false); + if (std::error_code EC = FileOrErr.getError()) + return errorCodeToError(EC); + std::unique_ptr &Buffer = FileOrErr.get(); + MemoryBufferRef MemBuf = Buffer->getMemBufferRef(); + if (file_magic::archive == identify_magic(MemBuf.getBuffer())) + return archive2yaml(outs(), MemBuf); - Binary &Binary = *BinaryOrErr.get().getBinary(); + Expected> BinOrErr = + createBinary(MemBuf, /*Context=*/nullptr); + if (!BinOrErr) + return BinOrErr.takeError(); + + Binary &Binary = *BinOrErr->get(); // Universal MachO is not a subclass of ObjectFile, so it needs to be handled // here with the other binary types. if (Binary.isMachO() || Binary.isMachOUniversalBinary()) return macho2yaml(outs(), Binary); - // TODO: If this is an archive, then burst it and dump each entry if (ObjectFile *Obj = dyn_cast(&Binary)) return dumpObject(*Obj); if (MinidumpFile *Minidump = dyn_cast(&Binary))