mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-21 18:22:53 +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:
parent
25e5e32d22
commit
5ef47e6205
77
include/llvm/ObjectYAML/ArchiveYAML.h
Normal file
77
include/llvm/ObjectYAML/ArchiveYAML.h
Normal 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
|
@ -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;
|
||||
|
@ -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);
|
||||
|
51
lib/ObjectYAML/ArchiveEmitter.cpp
Normal file
51
lib/ObjectYAML/ArchiveEmitter.cpp
Normal 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
|
58
lib/ObjectYAML/ArchiveYAML.cpp
Normal file
58
lib/ObjectYAML/ArchiveYAML.cpp
Normal 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
|
@ -1,4 +1,6 @@
|
||||
add_llvm_component_library(LLVMObjectYAML
|
||||
ArchiveEmitter.cpp
|
||||
ArchiveYAML.cpp
|
||||
CodeViewYAMLDebugSections.cpp
|
||||
CodeViewYAMLSymbols.cpp
|
||||
CodeViewYAMLTypeHashing.cpp
|
||||
|
@ -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")) {
|
||||
|
@ -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)
|
||||
|
158
test/tools/obj2yaml/Archives/regular.yaml
Normal file
158
test/tools/obj2yaml/Archives/regular.yaml
Normal 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: ...
|
132
test/tools/yaml2obj/Archives/regular.yaml
Normal file
132
test/tools/yaml2obj/Archives/regular.yaml
Normal 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:
|
||||
- {}
|
||||
- {}
|
@ -8,6 +8,7 @@ set(LLVM_LINK_COMPONENTS
|
||||
)
|
||||
|
||||
add_llvm_utility(obj2yaml
|
||||
archive2yaml.cpp
|
||||
obj2yaml.cpp
|
||||
coff2yaml.cpp
|
||||
dwarf2yaml.cpp
|
||||
|
114
tools/obj2yaml/archive2yaml.cpp
Normal file
114
tools/obj2yaml/archive2yaml.cpp
Normal 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();
|
||||
}
|
@ -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))
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user