//===------ 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 dump(MemoryBufferRef Source) { StringRef Buffer = Source.getBuffer(); assert(file_magic::archive == identify_magic(Buffer)); std::unique_ptr Obj = std::make_unique(); StringRef Magic = "!\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(Buffer.data()); Buffer = Buffer.drop_front(sizeof(ArchiveHeader)); auto ToString = [](ArrayRef 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 YAMLOrErr = Dumper.dump(Source); if (!YAMLOrErr) return YAMLOrErr.takeError(); std::unique_ptr YAML(YAMLOrErr.get()); yaml::Output Yout(Out); Yout << *YAML; return Error::success(); }