115 lines
3.7 KiB
C++
115 lines
3.7 KiB
C++
|
//===------ 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.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<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();
|
||
|
}
|