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:
dump(MemoryBufferRef Source)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.startswith(Magic))
28       return createStringError(std::errc::not_supported,
29                                "only regular archives are supported");
30     Obj->Magic = Magic;
31     Buffer = Buffer.drop_front(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             std::errc::illegal_byte_sequence,
39             "unable to read the header of a child at offset 0x%" PRIx64,
40             Offset);
41 
42       const ArchiveHeader &Hdr =
43           *reinterpret_cast<const ArchiveHeader *>(Buffer.data());
44       Buffer = Buffer.drop_front(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(' ');
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(10, Size))
63         return createStringError(
64             std::errc::illegal_byte_sequence,
65             "unable to read the size of a child at offset 0x%" PRIx64
66             " as integer: \"%s\"",
67             Offset, SizeStr.str().c_str());
68       if (Buffer.size() < Size)
69         return createStringError(
70             std::errc::illegal_byte_sequence,
71             "unable to read the data of a child at offset 0x%" PRIx64
72             " of size %" PRId64 ": the remaining archive size is %zu",
73             Offset, Size, Buffer.size());
74       if (!Buffer.empty())
75         C.Content = arrayRefFromStringRef(Buffer.take_front(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(C);
82       // If the size is odd, consume a padding byte.
83       Buffer = Buffer.drop_front(HasPaddingByte ? Size + 1 : Size);
84     }
85 
86     return Obj.release();
87   }
88 
89 private:
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 
archive2yaml(raw_ostream & Out,MemoryBufferRef Source)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