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 | |
13 | using namespace llvm; |
14 | |
15 | namespace { |
16 | |
17 | class ArchiveDumper { |
18 | public: |
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 | |
89 | private: |
90 | struct { |
91 | char [16]; |
92 | char [12]; |
93 | char [6]; |
94 | char [6]; |
95 | char [8]; |
96 | char [10]; |
97 | char [2]; |
98 | }; |
99 | }; |
100 | |
101 | } // namespace |
102 | |
103 | Error 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 | |