
Normally we can only make sense of the content of a PDB in terms of streams and blocks, but in some cases it may be useful to dump bytes at a specific absolute file offset. For example, if you know that some interesting data is at a particular location and you want to see some surrounding data. llvm-svn: 306146
175 lines
5.0 KiB
C++
175 lines
5.0 KiB
C++
//===- BytesOutputStyle.cpp ----------------------------------- *- C++ --*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "BytesOutputStyle.h"
|
|
|
|
#include "StreamUtil.h"
|
|
#include "llvm-pdbutil.h"
|
|
|
|
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
|
|
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
|
|
#include "llvm/DebugInfo/PDB/Native/RawError.h"
|
|
#include "llvm/Support/BinaryStreamReader.h"
|
|
#include "llvm/Support/FormatAdapters.h"
|
|
#include "llvm/Support/FormatVariadic.h"
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::msf;
|
|
using namespace llvm::pdb;
|
|
|
|
namespace {
|
|
struct StreamSpec {
|
|
uint32_t SI = 0;
|
|
uint32_t Begin = 0;
|
|
uint32_t Size = 0;
|
|
};
|
|
} // namespace
|
|
|
|
static Expected<StreamSpec> parseStreamSpec(StringRef Str) {
|
|
StreamSpec Result;
|
|
if (Str.consumeInteger(0, Result.SI))
|
|
return make_error<RawError>(raw_error_code::invalid_format,
|
|
"Invalid Stream Specification");
|
|
if (Str.consume_front(":")) {
|
|
if (Str.consumeInteger(0, Result.Begin))
|
|
return make_error<RawError>(raw_error_code::invalid_format,
|
|
"Invalid Stream Specification");
|
|
}
|
|
if (Str.consume_front("@")) {
|
|
if (Str.consumeInteger(0, Result.Size))
|
|
return make_error<RawError>(raw_error_code::invalid_format,
|
|
"Invalid Stream Specification");
|
|
}
|
|
|
|
if (!Str.empty())
|
|
return make_error<RawError>(raw_error_code::invalid_format,
|
|
"Invalid Stream Specification");
|
|
return Result;
|
|
}
|
|
|
|
static SmallVector<StreamSpec, 2> parseStreamSpecs(LinePrinter &P) {
|
|
SmallVector<StreamSpec, 2> Result;
|
|
|
|
for (auto &Str : opts::bytes::DumpStreamData) {
|
|
auto ESS = parseStreamSpec(Str);
|
|
if (!ESS) {
|
|
P.formatLine("Error parsing stream spec {0}: {1}", Str,
|
|
toString(ESS.takeError()));
|
|
continue;
|
|
}
|
|
Result.push_back(*ESS);
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
static void printHeader(LinePrinter &P, const Twine &S) {
|
|
P.NewLine();
|
|
P.formatLine("{0,=60}", S);
|
|
P.formatLine("{0}", fmt_repeat('=', 60));
|
|
}
|
|
|
|
BytesOutputStyle::BytesOutputStyle(PDBFile &File)
|
|
: File(File), P(2, false, outs()) {}
|
|
|
|
Error BytesOutputStyle::dump() {
|
|
|
|
if (opts::bytes::DumpBlockRange.hasValue()) {
|
|
auto &R = *opts::bytes::DumpBlockRange;
|
|
uint32_t Max = R.Max.getValueOr(R.Min);
|
|
|
|
if (Max < R.Min)
|
|
return make_error<StringError>(
|
|
"Invalid block range specified. Max < Min",
|
|
inconvertibleErrorCode());
|
|
if (Max >= File.getBlockCount())
|
|
return make_error<StringError>(
|
|
"Invalid block range specified. Requested block out of bounds",
|
|
inconvertibleErrorCode());
|
|
|
|
dumpBlockRanges(R.Min, Max);
|
|
P.NewLine();
|
|
}
|
|
|
|
if (opts::bytes::DumpByteRange.hasValue()) {
|
|
auto &R = *opts::bytes::DumpByteRange;
|
|
uint32_t Max = R.Max.getValueOr(File.getFileSize());
|
|
|
|
if (Max < R.Min)
|
|
return make_error<StringError>("Invalid byte range specified. Max < Min",
|
|
inconvertibleErrorCode());
|
|
if (Max >= File.getFileSize())
|
|
return make_error<StringError>(
|
|
"Invalid byte range specified. Requested byte larger than file size",
|
|
inconvertibleErrorCode());
|
|
|
|
dumpByteRanges(R.Min, Max);
|
|
P.NewLine();
|
|
}
|
|
|
|
if (!opts::bytes::DumpStreamData.empty()) {
|
|
dumpStreamBytes();
|
|
P.NewLine();
|
|
}
|
|
return Error::success();
|
|
}
|
|
|
|
void BytesOutputStyle::dumpBlockRanges(uint32_t Min, uint32_t Max) {
|
|
printHeader(P, "MSF Blocks");
|
|
|
|
AutoIndent Indent(P);
|
|
for (uint32_t I = Min; I <= Max; ++I) {
|
|
uint64_t Base = I;
|
|
Base *= File.getBlockSize();
|
|
|
|
auto ExpectedData = File.getBlockData(I, File.getBlockSize());
|
|
if (!ExpectedData) {
|
|
P.formatLine("Could not get block {0}. Reason = {1}", I,
|
|
toString(ExpectedData.takeError()));
|
|
continue;
|
|
}
|
|
std::string Label = formatv("Block {0}", I).str();
|
|
P.formatBinary(Label, *ExpectedData, Base, 0);
|
|
}
|
|
}
|
|
|
|
void BytesOutputStyle::dumpByteRanges(uint32_t Min, uint32_t Max) {
|
|
printHeader(P, "MSF Bytes");
|
|
|
|
AutoIndent Indent(P);
|
|
|
|
BinaryStreamReader Reader(File.getMsfBuffer());
|
|
ArrayRef<uint8_t> Data;
|
|
consumeError(Reader.skip(Min));
|
|
uint32_t Size = Max - Min + 1;
|
|
auto EC = Reader.readBytes(Data, Size);
|
|
assert(!EC);
|
|
consumeError(std::move(EC));
|
|
P.formatBinary("Bytes", Data, Min);
|
|
}
|
|
|
|
void BytesOutputStyle::dumpStreamBytes() {
|
|
if (StreamPurposes.empty())
|
|
discoverStreamPurposes(File, StreamPurposes);
|
|
|
|
printHeader(P, "Stream Data");
|
|
ExitOnError Err("Unexpected error reading stream data");
|
|
|
|
auto Specs = parseStreamSpecs(P);
|
|
|
|
for (const auto &Spec : Specs) {
|
|
AutoIndent Indent(P);
|
|
if (Spec.SI >= StreamPurposes.size()) {
|
|
P.formatLine("Stream {0}: Not present", Spec.SI);
|
|
continue;
|
|
}
|
|
P.formatMsfStreamData("Data", File, Spec.SI, StreamPurposes[Spec.SI],
|
|
Spec.Begin, Spec.Size);
|
|
}
|
|
}
|