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.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 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