115 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "obj2yaml.h"10#include "llvm/BinaryFormat/Magic.h"11#include "llvm/ObjectYAML/ArchiveYAML.h"12 13using namespace llvm;14 15namespace {16 17class ArchiveDumper {18public: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(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%" PRIx6466 " 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%" PRIx6472 " 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 89private: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} // namespace102 103Error 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