1//===------ utils/archive2yaml.cpp - obj2yaml conversion tool ---*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "obj2yaml.h"
10#include "llvm/BinaryFormat/Magic.h"
11#include "llvm/ObjectYAML/ArchiveYAML.h"
12
13using namespace llvm;
14
15namespace {
16
17class ArchiveDumper {
18public:
19 Expected<ArchYAML::Archive *> dump(MemoryBufferRef Source) {
20 StringRef Buffer = Source.getBuffer();
21 assert(file_magic::archive == identify_magic(Buffer));
22
23 std::unique_ptr<ArchYAML::Archive> Obj =
24 std::make_unique<ArchYAML::Archive>();
25
26 StringRef Magic = "!<arch>\n";
27 if (!Buffer.starts_with(Prefix: Magic))
28 return createStringError(EC: std::errc::not_supported,
29 Fmt: "only regular archives are supported");
30 Obj->Magic = Magic;
31 Buffer = Buffer.drop_front(N: Magic.size());
32
33 Obj->Members.emplace();
34 while (!Buffer.empty()) {
35 uint64_t Offset = Buffer.data() - Source.getBuffer().data();
36 if (Buffer.size() < sizeof(ArchiveHeader))
37 return createStringError(
38 EC: std::errc::illegal_byte_sequence,
39 Fmt: "unable to read the header of a child at offset 0x%" PRIx64,
40 Vals: Offset);
41
42 const ArchiveHeader &Hdr =
43 *reinterpret_cast<const ArchiveHeader *>(Buffer.data());
44 Buffer = Buffer.drop_front(N: sizeof(ArchiveHeader));
45
46 auto ToString = [](ArrayRef<char> V) {
47 // We don't want to dump excessive spaces.
48 return StringRef(V.data(), V.size()).rtrim(Char: ' ');
49 };
50
51 ArchYAML::Archive::Child C;
52 C.Fields["Name"].Value = ToString(Hdr.Name);
53 C.Fields["LastModified"].Value = ToString(Hdr.LastModified);
54 C.Fields["UID"].Value = ToString(Hdr.UID);
55 C.Fields["GID"].Value = ToString(Hdr.GID);
56 C.Fields["AccessMode"].Value = ToString(Hdr.AccessMode);
57 StringRef SizeStr = ToString(Hdr.Size);
58 C.Fields["Size"].Value = SizeStr;
59 C.Fields["Terminator"].Value = ToString(Hdr.Terminator);
60
61 uint64_t Size;
62 if (SizeStr.getAsInteger(Radix: 10, Result&: Size))
63 return createStringError(
64 EC: std::errc::illegal_byte_sequence,
65 Fmt: "unable to read the size of a child at offset 0x%" PRIx64
66 " as integer: \"%s\"",
67 Vals: Offset, Vals: SizeStr.str().c_str());
68 if (Buffer.size() < Size)
69 return createStringError(
70 EC: std::errc::illegal_byte_sequence,
71 Fmt: "unable to read the data of a child at offset 0x%" PRIx64
72 " of size %" PRId64 ": the remaining archive size is %zu",
73 Vals: Offset, Vals: Size, Vals: Buffer.size());
74 if (!Buffer.empty())
75 C.Content = arrayRefFromStringRef(Input: Buffer.take_front(N: Size));
76
77 const bool HasPaddingByte = (Size & 1) && Buffer.size() > Size;
78 if (HasPaddingByte)
79 C.PaddingByte = Buffer[Size];
80
81 Obj->Members->push_back(x: C);
82 // If the size is odd, consume a padding byte.
83 Buffer = Buffer.drop_front(N: HasPaddingByte ? Size + 1 : Size);
84 }
85
86 return Obj.release();
87 }
88
89private:
90 struct ArchiveHeader {
91 char Name[16];
92 char LastModified[12];
93 char UID[6];
94 char GID[6];
95 char AccessMode[8];
96 char Size[10];
97 char Terminator[2];
98 };
99};
100
101} // namespace
102
103Error archive2yaml(raw_ostream &Out, MemoryBufferRef Source) {
104 ArchiveDumper Dumper;
105 Expected<ArchYAML::Archive *> YAMLOrErr = Dumper.dump(Source);
106 if (!YAMLOrErr)
107 return YAMLOrErr.takeError();
108
109 std::unique_ptr<ArchYAML::Archive> YAML(YAMLOrErr.get());
110 yaml::Output Yout(Out);
111 Yout << *YAML;
112
113 return Error::success();
114}
115