272 lines
7.9 KiB
C
272 lines
7.9 KiB
C
|
//===- CodeViewRecordIO.h ---------------------------------------*- 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
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
#ifndef LLVM_DEBUGINFO_CODEVIEW_CODEVIEWRECORDIO_H
|
||
|
#define LLVM_DEBUGINFO_CODEVIEW_CODEVIEWRECORDIO_H
|
||
|
|
||
|
#include "llvm/ADT/APSInt.h"
|
||
|
#include "llvm/ADT/None.h"
|
||
|
#include "llvm/ADT/Optional.h"
|
||
|
#include "llvm/ADT/SmallVector.h"
|
||
|
#include "llvm/ADT/StringRef.h"
|
||
|
#include "llvm/DebugInfo/CodeView/CodeViewError.h"
|
||
|
#include "llvm/DebugInfo/CodeView/GUID.h"
|
||
|
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||
|
#include "llvm/Support/BinaryStreamReader.h"
|
||
|
#include "llvm/Support/BinaryStreamWriter.h"
|
||
|
#include "llvm/Support/Error.h"
|
||
|
#include <cassert>
|
||
|
#include <cstdint>
|
||
|
#include <type_traits>
|
||
|
|
||
|
namespace llvm {
|
||
|
|
||
|
namespace codeview {
|
||
|
|
||
|
class CodeViewRecordStreamer {
|
||
|
public:
|
||
|
virtual void emitBytes(StringRef Data) = 0;
|
||
|
virtual void emitIntValue(uint64_t Value, unsigned Size) = 0;
|
||
|
virtual void emitBinaryData(StringRef Data) = 0;
|
||
|
virtual void AddComment(const Twine &T) = 0;
|
||
|
virtual void AddRawComment(const Twine &T) = 0;
|
||
|
virtual bool isVerboseAsm() = 0;
|
||
|
virtual std::string getTypeName(TypeIndex TI) = 0;
|
||
|
virtual ~CodeViewRecordStreamer() = default;
|
||
|
};
|
||
|
|
||
|
class CodeViewRecordIO {
|
||
|
uint32_t getCurrentOffset() const {
|
||
|
if (isWriting())
|
||
|
return Writer->getOffset();
|
||
|
else if (isReading())
|
||
|
return Reader->getOffset();
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
// deserializes records to structures
|
||
|
explicit CodeViewRecordIO(BinaryStreamReader &Reader) : Reader(&Reader) {}
|
||
|
|
||
|
// serializes records to buffer
|
||
|
explicit CodeViewRecordIO(BinaryStreamWriter &Writer) : Writer(&Writer) {}
|
||
|
|
||
|
// writes records to assembly file using MC library interface
|
||
|
explicit CodeViewRecordIO(CodeViewRecordStreamer &Streamer)
|
||
|
: Streamer(&Streamer) {}
|
||
|
|
||
|
Error beginRecord(Optional<uint32_t> MaxLength);
|
||
|
Error endRecord();
|
||
|
|
||
|
Error mapInteger(TypeIndex &TypeInd, const Twine &Comment = "");
|
||
|
|
||
|
bool isStreaming() const {
|
||
|
return (Streamer != nullptr) && (Reader == nullptr) && (Writer == nullptr);
|
||
|
}
|
||
|
bool isReading() const {
|
||
|
return (Reader != nullptr) && (Streamer == nullptr) && (Writer == nullptr);
|
||
|
}
|
||
|
bool isWriting() const {
|
||
|
return (Writer != nullptr) && (Streamer == nullptr) && (Reader == nullptr);
|
||
|
}
|
||
|
|
||
|
uint32_t maxFieldLength() const;
|
||
|
|
||
|
template <typename T> Error mapObject(T &Value) {
|
||
|
if (isStreaming()) {
|
||
|
StringRef BytesSR =
|
||
|
StringRef((reinterpret_cast<const char *>(&Value)), sizeof(Value));
|
||
|
Streamer->emitBytes(BytesSR);
|
||
|
incrStreamedLen(sizeof(T));
|
||
|
return Error::success();
|
||
|
}
|
||
|
|
||
|
if (isWriting())
|
||
|
return Writer->writeObject(Value);
|
||
|
|
||
|
const T *ValuePtr;
|
||
|
if (auto EC = Reader->readObject(ValuePtr))
|
||
|
return EC;
|
||
|
Value = *ValuePtr;
|
||
|
return Error::success();
|
||
|
}
|
||
|
|
||
|
template <typename T> Error mapInteger(T &Value, const Twine &Comment = "") {
|
||
|
if (isStreaming()) {
|
||
|
emitComment(Comment);
|
||
|
Streamer->emitIntValue((int)Value, sizeof(T));
|
||
|
incrStreamedLen(sizeof(T));
|
||
|
return Error::success();
|
||
|
}
|
||
|
|
||
|
if (isWriting())
|
||
|
return Writer->writeInteger(Value);
|
||
|
|
||
|
return Reader->readInteger(Value);
|
||
|
}
|
||
|
|
||
|
template <typename T> Error mapEnum(T &Value, const Twine &Comment = "") {
|
||
|
if (!isStreaming() && sizeof(Value) > maxFieldLength())
|
||
|
return make_error<CodeViewError>(cv_error_code::insufficient_buffer);
|
||
|
|
||
|
using U = std::underlying_type_t<T>;
|
||
|
U X;
|
||
|
|
||
|
if (isWriting() || isStreaming())
|
||
|
X = static_cast<U>(Value);
|
||
|
|
||
|
if (auto EC = mapInteger(X, Comment))
|
||
|
return EC;
|
||
|
|
||
|
if (isReading())
|
||
|
Value = static_cast<T>(X);
|
||
|
|
||
|
return Error::success();
|
||
|
}
|
||
|
|
||
|
Error mapEncodedInteger(int64_t &Value, const Twine &Comment = "");
|
||
|
Error mapEncodedInteger(uint64_t &Value, const Twine &Comment = "");
|
||
|
Error mapEncodedInteger(APSInt &Value, const Twine &Comment = "");
|
||
|
Error mapStringZ(StringRef &Value, const Twine &Comment = "");
|
||
|
Error mapGuid(GUID &Guid, const Twine &Comment = "");
|
||
|
|
||
|
Error mapStringZVectorZ(std::vector<StringRef> &Value,
|
||
|
const Twine &Comment = "");
|
||
|
|
||
|
template <typename SizeType, typename T, typename ElementMapper>
|
||
|
Error mapVectorN(T &Items, const ElementMapper &Mapper,
|
||
|
const Twine &Comment = "") {
|
||
|
SizeType Size;
|
||
|
if (isStreaming()) {
|
||
|
Size = static_cast<SizeType>(Items.size());
|
||
|
emitComment(Comment);
|
||
|
Streamer->emitIntValue(Size, sizeof(Size));
|
||
|
incrStreamedLen(sizeof(Size)); // add 1 for the delimiter
|
||
|
|
||
|
for (auto &X : Items) {
|
||
|
if (auto EC = Mapper(*this, X))
|
||
|
return EC;
|
||
|
}
|
||
|
} else if (isWriting()) {
|
||
|
Size = static_cast<SizeType>(Items.size());
|
||
|
if (auto EC = Writer->writeInteger(Size))
|
||
|
return EC;
|
||
|
|
||
|
for (auto &X : Items) {
|
||
|
if (auto EC = Mapper(*this, X))
|
||
|
return EC;
|
||
|
}
|
||
|
} else {
|
||
|
if (auto EC = Reader->readInteger(Size))
|
||
|
return EC;
|
||
|
for (SizeType I = 0; I < Size; ++I) {
|
||
|
typename T::value_type Item;
|
||
|
if (auto EC = Mapper(*this, Item))
|
||
|
return EC;
|
||
|
Items.push_back(Item);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return Error::success();
|
||
|
}
|
||
|
|
||
|
template <typename T, typename ElementMapper>
|
||
|
Error mapVectorTail(T &Items, const ElementMapper &Mapper,
|
||
|
const Twine &Comment = "") {
|
||
|
emitComment(Comment);
|
||
|
if (isStreaming() || isWriting()) {
|
||
|
for (auto &Item : Items) {
|
||
|
if (auto EC = Mapper(*this, Item))
|
||
|
return EC;
|
||
|
}
|
||
|
} else {
|
||
|
typename T::value_type Field;
|
||
|
// Stop when we run out of bytes or we hit record padding bytes.
|
||
|
while (!Reader->empty() && Reader->peek() < 0xf0 /* LF_PAD0 */) {
|
||
|
if (auto EC = Mapper(*this, Field))
|
||
|
return EC;
|
||
|
Items.push_back(Field);
|
||
|
}
|
||
|
}
|
||
|
return Error::success();
|
||
|
}
|
||
|
|
||
|
Error mapByteVectorTail(ArrayRef<uint8_t> &Bytes, const Twine &Comment = "");
|
||
|
Error mapByteVectorTail(std::vector<uint8_t> &Bytes,
|
||
|
const Twine &Comment = "");
|
||
|
|
||
|
Error padToAlignment(uint32_t Align);
|
||
|
Error skipPadding();
|
||
|
|
||
|
uint64_t getStreamedLen() {
|
||
|
if (isStreaming())
|
||
|
return StreamedLen;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void emitRawComment(const Twine &T) {
|
||
|
if (isStreaming() && Streamer->isVerboseAsm())
|
||
|
Streamer->AddRawComment(T);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
void emitEncodedSignedInteger(const int64_t &Value,
|
||
|
const Twine &Comment = "");
|
||
|
void emitEncodedUnsignedInteger(const uint64_t &Value,
|
||
|
const Twine &Comment = "");
|
||
|
Error writeEncodedSignedInteger(const int64_t &Value);
|
||
|
Error writeEncodedUnsignedInteger(const uint64_t &Value);
|
||
|
|
||
|
void incrStreamedLen(const uint64_t &Len) {
|
||
|
if (isStreaming())
|
||
|
StreamedLen += Len;
|
||
|
}
|
||
|
|
||
|
void resetStreamedLen() {
|
||
|
if (isStreaming())
|
||
|
StreamedLen = 4; // The record prefix is 4 bytes long
|
||
|
}
|
||
|
|
||
|
void emitComment(const Twine &Comment) {
|
||
|
if (isStreaming() && Streamer->isVerboseAsm()) {
|
||
|
Twine TComment(Comment);
|
||
|
if (!TComment.isTriviallyEmpty())
|
||
|
Streamer->AddComment(TComment);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct RecordLimit {
|
||
|
uint32_t BeginOffset;
|
||
|
Optional<uint32_t> MaxLength;
|
||
|
|
||
|
Optional<uint32_t> bytesRemaining(uint32_t CurrentOffset) const {
|
||
|
if (!MaxLength.hasValue())
|
||
|
return None;
|
||
|
assert(CurrentOffset >= BeginOffset);
|
||
|
|
||
|
uint32_t BytesUsed = CurrentOffset - BeginOffset;
|
||
|
if (BytesUsed >= *MaxLength)
|
||
|
return 0;
|
||
|
return *MaxLength - BytesUsed;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
SmallVector<RecordLimit, 2> Limits;
|
||
|
|
||
|
BinaryStreamReader *Reader = nullptr;
|
||
|
BinaryStreamWriter *Writer = nullptr;
|
||
|
CodeViewRecordStreamer *Streamer = nullptr;
|
||
|
uint64_t StreamedLen = 0;
|
||
|
};
|
||
|
|
||
|
} // end namespace codeview
|
||
|
} // end namespace llvm
|
||
|
|
||
|
#endif // LLVM_DEBUGINFO_CODEVIEW_CODEVIEWRECORDIO_H
|