blob: d3bad4cd405caec38192b275677d9e58a667b717 [file] [log] [blame]
//===- 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/TypeRecord.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 ~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);
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) {
if (isStreaming()) {
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) {
if (!isStreaming() && sizeof(Value) > maxFieldLength())
return make_error<CodeViewError>(cv_error_code::insufficient_buffer);
using U = typename std::underlying_type<T>::type;
U X;
if (isWriting() || isStreaming())
X = static_cast<U>(Value);
if (auto EC = mapInteger(X))
return EC;
if (isReading())
Value = static_cast<T>(X);
return Error::success();
}
Error mapEncodedInteger(int64_t &Value);
Error mapEncodedInteger(uint64_t &Value);
Error mapEncodedInteger(APSInt &Value);
Error mapStringZ(StringRef &Value);
Error mapGuid(GUID &Guid);
Error mapStringZVectorZ(std::vector<StringRef> &Value);
template <typename SizeType, typename T, typename ElementMapper>
Error mapVectorN(T &Items, const ElementMapper &Mapper) {
SizeType Size;
if (isStreaming()) {
Size = static_cast<SizeType>(Items.size());
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) {
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);
Error mapByteVectorTail(std::vector<uint8_t> &Bytes);
Error padToAlignment(uint32_t Align);
Error skipPadding();
uint64_t getStreamedLen() {
if (isStreaming())
return StreamedLen;
return 0;
}
private:
void emitEncodedSignedInteger(const int64_t &Value);
void emitEncodedUnsignedInteger(const uint64_t &Value);
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
}
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