1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 02:33:06 +01:00

[yaml2obj][obj2yaml] - Teach tools to work with regular archives.

This teaches obj2yaml to dump valid regular (not thin) archives.
This also teaches yaml2obj to recognize archives YAML descriptions,
what allows to craft all different kinds of archives (valid and broken ones).

Differential revision: https://reviews.llvm.org/D89949
This commit is contained in:
Georgii Rymar 2020-10-20 15:12:28 +03:00
parent 25e5e32d22
commit 5ef47e6205
14 changed files with 628 additions and 6 deletions

View File

@ -0,0 +1,77 @@
//===- 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 Child {
struct Field {
Field() = default;
Field(StringRef Default, unsigned Length)
: DefaultValue(Default), MaxLength(Length) {}
StringRef Value;
StringRef DefaultValue;
unsigned MaxLength;
};
Child() {
Fields["Name"] = {"", 16};
Fields["LastModified"] = {"0", 12};
Fields["UID"] = {"0", 6};
Fields["GID"] = {"0", 6};
Fields["AccessMode"] = {"0", 8};
Fields["Size"] = {"0", 10};
Fields["Terminator"] = {"`\n", 2};
}
MapVector<StringRef, Field> Fields;
Optional<yaml::BinaryRef> Content;
Optional<llvm::yaml::Hex8> PaddingByte;
};
StringRef Magic;
Optional<std::vector<Child>> Members;
Optional<yaml::BinaryRef> Content;
};
} // end namespace ArchYAML
} // end namespace llvm
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ArchYAML::Archive::Child)
namespace llvm {
namespace yaml {
template <> struct MappingTraits<ArchYAML::Archive> {
static void mapping(IO &IO, ArchYAML::Archive &A);
static std::string validate(IO &, ArchYAML::Archive &A);
};
template <> struct MappingTraits<ArchYAML::Archive::Child> {
static void mapping(IO &IO, ArchYAML::Archive::Child &C);
static std::string validate(IO &, ArchYAML::Archive::Child &C);
};
} // end namespace yaml
} // end namespace llvm
#endif // LLVM_OBJECTYAML_ARCHIVEYAML_H

View File

@ -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 @@ namespace yaml {
class IO;
struct YamlObjectFile {
std::unique_ptr<ArchYAML::Archive> Arch;
std::unique_ptr<ELFYAML::Object> Elf;
std::unique_ptr<COFFYAML::Object> Coff;
std::unique_ptr<MachOYAML::Object> MachO;

View File

@ -40,12 +40,17 @@ namespace WasmYAML {
struct Object;
}
namespace ArchYAML {
struct Archive;
}
namespace yaml {
class Input;
struct YamlObjectFile;
using ErrorHandler = llvm::function_ref<void(const Twine &Msg)>;
bool yaml2archive(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);

View File

@ -0,0 +1,51 @@
//===- 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 yaml2archive(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.Members)
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::Child &C : *Doc.Members) {
for (auto &P : C.Fields)
WriteField(P.second.Value, P.second.MaxLength);
if (C.Content)
C.Content->writeAsBinary(Out);
if (C.PaddingByte)
Out.write(*C.PaddingByte);
}
return true;
}
} // namespace yaml
} // namespace llvm

View File

@ -0,0 +1,58 @@
//===- 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<ArchYAML::Archive>::mapping(IO &IO, ArchYAML::Archive &A) {
assert(!IO.getContext() && "The IO context is initialized already");
IO.setContext(&A);
IO.mapTag("!Arch", true);
IO.mapOptional("Magic", A.Magic, "!<arch>\n");
IO.mapOptional("Members", A.Members);
IO.mapOptional("Content", A.Content);
IO.setContext(nullptr);
}
std::string MappingTraits<ArchYAML::Archive>::validate(IO &,
ArchYAML::Archive &A) {
if (A.Members && A.Content)
return "\"Content\" and \"Members\" cannot be used together";
return "";
}
void MappingTraits<ArchYAML::Archive::Child>::mapping(
IO &IO, ArchYAML::Archive::Child &E) {
assert(IO.getContext() && "The IO context is not initialized");
for (auto &P : E.Fields)
IO.mapOptional(P.first.data(), P.second.Value, P.second.DefaultValue);
IO.mapOptional("Content", E.Content);
IO.mapOptional("PaddingByte", E.PaddingByte);
}
std::string
MappingTraits<ArchYAML::Archive::Child>::validate(IO &,
ArchYAML::Archive::Child &C) {
for (auto &P : C.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

View File

@ -1,4 +1,6 @@
add_llvm_component_library(LLVMObjectYAML
ArchiveEmitter.cpp
ArchiveYAML.cpp
CodeViewYAMLDebugSections.cpp
CodeViewYAMLSymbols.cpp
CodeViewYAMLTypeHashing.cpp

View File

@ -33,7 +33,14 @@ void MappingTraits<YamlObjectFile>::mapping(IO &IO,
*ObjectFile.FatMachO);
} else {
Input &In = (Input &)IO;
if (IO.mapTag("!ELF")) {
if (IO.mapTag("!Arch")) {
ObjectFile.Arch.reset(new ArchYAML::Archive());
MappingTraits<ArchYAML::Archive>::mapping(IO, *ObjectFile.Arch);
std::string Err =
MappingTraits<ArchYAML::Archive>::validate(IO, *ObjectFile.Arch);
if (!Err.empty())
IO.setError(Err);
} else if (IO.mapTag("!ELF")) {
ObjectFile.Elf.reset(new ELFYAML::Object());
MappingTraits<ELFYAML::Object>::mapping(IO, *ObjectFile.Elf);
} else if (IO.mapTag("!COFF")) {

View File

@ -32,6 +32,8 @@ bool convertYAML(yaml::Input &YIn, raw_ostream &Out, ErrorHandler ErrHandler,
return false;
}
if (Doc.Arch)
return yaml2archive(*Doc.Arch, Out, ErrHandler);
if (Doc.Elf)
return yaml2elf(*Doc.Elf, Out, ErrHandler, MaxSize);
if (Doc.Coff)

View File

@ -0,0 +1,158 @@
## 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: Members: []
# EMPTY-NEXT: ...
## Check how we dump archives with multiple members.
## Check we don't dump excessive spaces when dumping fields.
## Check we don't dump fields with values that are equal to default values.
## Check how we dump empty field values.
# RUN: yaml2obj %s --docnum=1 -o %t.multiple.a
# RUN: obj2yaml %t.multiple.a | FileCheck %s --check-prefix=MULTIPLE
# MULTIPLE: --- !Arch
# MULTIPLE-NEXT: Members:
# MULTIPLE-NEXT: - Name: 'bbb/'
# MULTIPLE-NEXT: LastModified: '1'
# MULTIPLE-NEXT: UID: '2'
# MULTIPLE-NEXT: GID: '3'
# MULTIPLE-NEXT: AccessMode: '644'
# MULTIPLE-NEXT: Size: '6'
# MULTIPLE-NEXT: Content: 20616161200A
# MULTIPLE-NEXT: - Name: 'dddd/'
# MULTIPLE-NEXT: LastModified: '4'
# MULTIPLE-NEXT: UID: '5'
# MULTIPLE-NEXT: GID: '6'
# MULTIPLE-NEXT: AccessMode: '987'
# MULTIPLE-NEXT: Size: '7'
# MULTIPLE-NEXT: Content: 2063636363200A
# MULTIPLE-NEXT: PaddingByte: 0x0A
# MULTIPLE-NEXT: - LastModified: ''
# MULTIPLE-NEXT: UID: ''
# MULTIPLE-NEXT: GID: ''
# MULTIPLE-NEXT: AccessMode: ''
# MULTIPLE-NEXT: Terminator: ''
# MULTIPLE-NEXT: Content: ''
# MULTIPLE-NEXT: - {}
# MULTIPLE-NEXT: ...
--- !Arch
Members:
- Name: 'bbb/'
LastModified: '1'
UID: '2'
GID: '3'
AccessMode: '644'
Size: '6'
Terminator: "`\n"
Content: 20616161200A ## " aaa \n"
- Name: 'dddd/'
LastModified: '4'
UID: '5'
GID: '6'
AccessMode: '987'
Size: '7'
Terminator: "`\n"
Content: 2063636363200A ## " cccc \n"
PaddingByte: 0x0A
## All fields are empty (where possible).
- Name: ''
LastModified: ''
UID: ''
GID: ''
AccessMode: ''
Size: '0'
Terminator: ''
Content: ''
## All fields are explicitly set to the default values.
- Name: ''
LastModified: '0'
UID: '0'
GID: '0'
AccessMode: '0'
Size: '0'
Terminator: "`\n"
Content: ""
...
## Check we report an error for non-regular archives.
# RUN: yaml2obj %s --docnum=2 -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: "!<thin>\n"
Members:
- {}
## Check we report an error when unable to read the header of an archive member.
# RUN: yaml2obj %s --docnum=3 -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 the header of a child at offset 0x8
--- !Arch
Content: "00"
## Check we report an error when unable to read the data of an archive member.
# RUN: yaml2obj %s --docnum=4 -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 the data of a child at offset 0x8 of size 1: the remaining archive size is 0
--- !Arch
Members:
- Size: [[SIZE='1']]
## Check we report an error when unable to read the size of an archive member.
# RUN: yaml2obj %s --docnum=4 -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 the size of a child at offset 0x8 as integer: "x"
## Check we don't try to dump the padding byte when the size of the content is odd and
## the content ends at the end of a file.
# RUN: yaml2obj %s --docnum=5 -DCONTENT="61" -o %t.no.padding.byte.a
# RUN: obj2yaml %t.no.padding.byte.a | FileCheck %s --check-prefix=NO-PADDING-BYTE
# NO-PADDING-BYTE: --- !Arch
# NO-PADDING-BYTE-NEXT: Members:
# NO-PADDING-BYTE-NEXT: - Size: '1'
# NO-PADDING-BYTE-NEXT: Content: '61'
# NO-PADDING-BYTE-NEXT: ...
--- !Arch
Members:
- Size: '1'
Content: [[CONTENT]]
## Check we dump the padding byte when the size of the content is odd and the content ends
## before the end of a file.
# RUN: yaml2obj %s --docnum=5 -DCONTENT="610A" -o %t.padding.byte.a
# RUN: obj2yaml %t.padding.byte.a | FileCheck %s --check-prefix=PADDING-BYTE
# PADDING-BYTE: --- !Arch
# PADDING-BYTE-NEXT: Members:
# PADDING-BYTE-NEXT: - Size: '1'
# PADDING-BYTE-NEXT: Content: '61'
# PADDING-BYTE-NEXT: PaddingByte: 0x0A
# PADDING-BYTE-NEXT: ...

View File

@ -0,0 +1,132 @@
## Check how yaml2obj creates archives.
## Check we create an empty archive when neither "Members" 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=!<arch>\n]]"
Content: [[CONTENT=<none>]]
Members: [[MEMBERS=<none>]]
## Check we report an error when both "Content" and "Members" keys are used together.
# RUN: not yaml2obj --docnum=1 -DMEMBERS="[]" -DCONTENT="00" %s 2>&1 | FileCheck %s --check-prefix=BOTH
## BOTH: error: "Content" and "Members" 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 specify magic bytes of size greater than the normal size (size of "!<arch>\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 size less than the normal size (size of "!<arch>\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 members.
## Check we are able to omit the "Magic" key and this defaults to "!<arch>\n".
# RUN: yaml2obj --docnum=2 %s -o %t.two.a
# RUN: llvm-ar -t %t.two.a | FileCheck %s --check-prefix=TWO
# RUN: FileCheck --input-file=%t.two.a %s \
# RUN: --match-full-lines --strict-whitespace --check-prefix=TWO-DATA
# TWO: {{^}}bbbbbbbbbbbbbbb{{$}}
# TWO-NEXT: {{^}}a{{$}}
# TWO-NOT: {{.}}
# TWO-DATA:!<arch>
# TWO-DATA-NEXT:bbbbbbbbbbbbbbb/1234567890abqwertyasdfgh876543217 `
# TWO-DATA-NEXT: cccc {{$}}
# TWO-DATA-NEXT:za/ 1 2 3 456 6 `
# TWO-DATA-NEXT: aaa {{$}}
# TWO-DATA-NOT:{{.}}
--- !Arch
Members:
## 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: 0x7a ## 'z'
## An arbitrary entry to demonstrate that we use the 0x20 byte (space character)
## to fill 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
Members:
- Name: '[[NAME=""]]'
LastModified: '[[LAST=""]]'
UID: '[[UID=""]]'
GID: '[[GID=""]]'
AccessMode: '[[ACCESSMODE=""]]'
Size: '[[SIZE=""]]'
Terminator: '[[TERMINATOR=""]]'
## Check that all keys are optional for members.
# RUN: yaml2obj --docnum=4 %s -o %t.all.defaults.a
# RUN: FileCheck --input-file=%t.all.defaults.a %s \
# RUN: --match-full-lines --strict-whitespace --check-prefix=DEFAULTS
# DEFAULTS:!<arch>
# DEFAULTS-NEXT: 0 0 0 0 0 `
# DEFAULTS-NEXT: 0 0 0 0 0 `
# DEFAULTS-NOT:{{.}}
--- !Arch
Members:
- {}
- {}

View File

@ -8,6 +8,7 @@ set(LLVM_LINK_COMPONENTS
)
add_llvm_utility(obj2yaml
archive2yaml.cpp
obj2yaml.cpp
coff2yaml.cpp
dwarf2yaml.cpp

View File

@ -0,0 +1,114 @@
//===------ 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 {
public:
Expected<ArchYAML::Archive *> dump(MemoryBufferRef Source) {
StringRef Buffer = Source.getBuffer();
assert(file_magic::archive == identify_magic(Buffer));
std::unique_ptr<ArchYAML::Archive> Obj =
std::make_unique<ArchYAML::Archive>();
StringRef Magic = "!<arch>\n";
if (!Buffer.startswith(Magic))
return createStringError(std::errc::not_supported,
"only regular archives are supported");
Obj->Magic = Magic;
Buffer = Buffer.drop_front(Magic.size());
Obj->Members.emplace();
while (!Buffer.empty()) {
uint64_t Offset = Buffer.data() - Source.getBuffer().data();
if (Buffer.size() < sizeof(ArchiveHeader))
return createStringError(
std::errc::illegal_byte_sequence,
"unable to read the header of a child at offset 0x%" PRIx64,
Offset);
const ArchiveHeader &Hdr =
*reinterpret_cast<const ArchiveHeader *>(Buffer.data());
Buffer = Buffer.drop_front(sizeof(ArchiveHeader));
auto ToString = [](ArrayRef<char> V) {
// We don't want to dump excessive spaces.
return StringRef(V.data(), V.size()).rtrim(' ');
};
ArchYAML::Archive::Child C;
C.Fields["Name"].Value = ToString(Hdr.Name);
C.Fields["LastModified"].Value = ToString(Hdr.LastModified);
C.Fields["UID"].Value = ToString(Hdr.UID);
C.Fields["GID"].Value = ToString(Hdr.GID);
C.Fields["AccessMode"].Value = ToString(Hdr.AccessMode);
StringRef SizeStr = ToString(Hdr.Size);
C.Fields["Size"].Value = SizeStr;
C.Fields["Terminator"].Value = ToString(Hdr.Terminator);
uint64_t Size;
if (SizeStr.getAsInteger(10, Size))
return createStringError(
std::errc::illegal_byte_sequence,
"unable to read the size of a child at offset 0x%" PRIx64
" as integer: \"%s\"",
Offset, SizeStr.str().c_str());
if (Buffer.size() < Size)
return createStringError(
std::errc::illegal_byte_sequence,
"unable to read the data of a child at offset 0x%" PRIx64
" of size %" PRId64 ": the remaining archive size is %zu",
Offset, Size, Buffer.size());
if (!Buffer.empty())
C.Content = arrayRefFromStringRef(Buffer.take_front(Size));
const bool HasPaddingByte = (Size & 1) && Buffer.size() > Size;
if (HasPaddingByte)
C.PaddingByte = Buffer[Size];
Obj->Members->push_back(C);
// 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<ArchYAML::Archive *> YAMLOrErr = Dumper.dump(Source);
if (!YAMLOrErr)
return YAMLOrErr.takeError();
std::unique_ptr<ArchYAML::Archive> YAML(YAMLOrErr.get());
yaml::Output Yout(Out);
Yout << *YAML;
return Error::success();
}

View File

@ -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 dumpObject(const ObjectFile &Obj) {
}
static Error dumpInput(StringRef File) {
Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(File);
if (!BinaryOrErr)
return BinaryOrErr.takeError();
ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
MemoryBuffer::getFileOrSTDIN(File, /*FileSize=*/-1,
/*RequiresNullTerminator=*/false);
if (std::error_code EC = FileOrErr.getError())
return errorCodeToError(EC);
std::unique_ptr<MemoryBuffer> &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<std::unique_ptr<Binary>> 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<ObjectFile>(&Binary))
return dumpObject(*Obj);
if (MinidumpFile *Minidump = dyn_cast<MinidumpFile>(&Binary))

View File

@ -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 <system_error>
std::error_code coff2yaml(llvm::raw_ostream &Out,
@ -31,6 +32,7 @@ std::error_code xcoff2yaml(llvm::raw_ostream &Out,
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 {