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