Home | History | Annotate | Line # | Download | only in CodeView
      1 //===- CodeViewRecordIO.h ---------------------------------------*- 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-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 
      9 #ifndef LLVM_DEBUGINFO_CODEVIEW_CODEVIEWRECORDIO_H
     10 #define LLVM_DEBUGINFO_CODEVIEW_CODEVIEWRECORDIO_H
     11 
     12 #include "llvm/ADT/APSInt.h"
     13 #include "llvm/ADT/None.h"
     14 #include "llvm/ADT/Optional.h"
     15 #include "llvm/ADT/SmallVector.h"
     16 #include "llvm/ADT/StringRef.h"
     17 #include "llvm/DebugInfo/CodeView/CodeViewError.h"
     18 #include "llvm/DebugInfo/CodeView/GUID.h"
     19 #include "llvm/DebugInfo/CodeView/TypeIndex.h"
     20 #include "llvm/Support/BinaryStreamReader.h"
     21 #include "llvm/Support/BinaryStreamWriter.h"
     22 #include "llvm/Support/Error.h"
     23 #include <cassert>
     24 #include <cstdint>
     25 #include <type_traits>
     26 
     27 namespace llvm {
     28 
     29 namespace codeview {
     30 
     31 class CodeViewRecordStreamer {
     32 public:
     33   virtual void emitBytes(StringRef Data) = 0;
     34   virtual void emitIntValue(uint64_t Value, unsigned Size) = 0;
     35   virtual void emitBinaryData(StringRef Data) = 0;
     36   virtual void AddComment(const Twine &T) = 0;
     37   virtual void AddRawComment(const Twine &T) = 0;
     38   virtual bool isVerboseAsm() = 0;
     39   virtual std::string getTypeName(TypeIndex TI) = 0;
     40   virtual ~CodeViewRecordStreamer() = default;
     41 };
     42 
     43 class CodeViewRecordIO {
     44   uint32_t getCurrentOffset() const {
     45     if (isWriting())
     46       return Writer->getOffset();
     47     else if (isReading())
     48       return Reader->getOffset();
     49     else
     50       return 0;
     51   }
     52 
     53 public:
     54   // deserializes records to structures
     55   explicit CodeViewRecordIO(BinaryStreamReader &Reader) : Reader(&Reader) {}
     56 
     57   // serializes records to buffer
     58   explicit CodeViewRecordIO(BinaryStreamWriter &Writer) : Writer(&Writer) {}
     59 
     60   // writes records to assembly file using MC library interface
     61   explicit CodeViewRecordIO(CodeViewRecordStreamer &Streamer)
     62       : Streamer(&Streamer) {}
     63 
     64   Error beginRecord(Optional<uint32_t> MaxLength);
     65   Error endRecord();
     66 
     67   Error mapInteger(TypeIndex &TypeInd, const Twine &Comment = "");
     68 
     69   bool isStreaming() const {
     70     return (Streamer != nullptr) && (Reader == nullptr) && (Writer == nullptr);
     71   }
     72   bool isReading() const {
     73     return (Reader != nullptr) && (Streamer == nullptr) && (Writer == nullptr);
     74   }
     75   bool isWriting() const {
     76     return (Writer != nullptr) && (Streamer == nullptr) && (Reader == nullptr);
     77   }
     78 
     79   uint32_t maxFieldLength() const;
     80 
     81   template <typename T> Error mapObject(T &Value) {
     82     if (isStreaming()) {
     83       StringRef BytesSR =
     84           StringRef((reinterpret_cast<const char *>(&Value)), sizeof(Value));
     85       Streamer->emitBytes(BytesSR);
     86       incrStreamedLen(sizeof(T));
     87       return Error::success();
     88     }
     89 
     90     if (isWriting())
     91       return Writer->writeObject(Value);
     92 
     93     const T *ValuePtr;
     94     if (auto EC = Reader->readObject(ValuePtr))
     95       return EC;
     96     Value = *ValuePtr;
     97     return Error::success();
     98   }
     99 
    100   template <typename T> Error mapInteger(T &Value, const Twine &Comment = "") {
    101     if (isStreaming()) {
    102       emitComment(Comment);
    103       Streamer->emitIntValue((int)Value, sizeof(T));
    104       incrStreamedLen(sizeof(T));
    105       return Error::success();
    106     }
    107 
    108     if (isWriting())
    109       return Writer->writeInteger(Value);
    110 
    111     return Reader->readInteger(Value);
    112   }
    113 
    114   template <typename T> Error mapEnum(T &Value, const Twine &Comment = "") {
    115     if (!isStreaming() && sizeof(Value) > maxFieldLength())
    116       return make_error<CodeViewError>(cv_error_code::insufficient_buffer);
    117 
    118     using U = std::underlying_type_t<T>;
    119     U X;
    120 
    121     if (isWriting() || isStreaming())
    122       X = static_cast<U>(Value);
    123 
    124     if (auto EC = mapInteger(X, Comment))
    125       return EC;
    126 
    127     if (isReading())
    128       Value = static_cast<T>(X);
    129 
    130     return Error::success();
    131   }
    132 
    133   Error mapEncodedInteger(int64_t &Value, const Twine &Comment = "");
    134   Error mapEncodedInteger(uint64_t &Value, const Twine &Comment = "");
    135   Error mapEncodedInteger(APSInt &Value, const Twine &Comment = "");
    136   Error mapStringZ(StringRef &Value, const Twine &Comment = "");
    137   Error mapGuid(GUID &Guid, const Twine &Comment = "");
    138 
    139   Error mapStringZVectorZ(std::vector<StringRef> &Value,
    140                           const Twine &Comment = "");
    141 
    142   template <typename SizeType, typename T, typename ElementMapper>
    143   Error mapVectorN(T &Items, const ElementMapper &Mapper,
    144                    const Twine &Comment = "") {
    145     SizeType Size;
    146     if (isStreaming()) {
    147       Size = static_cast<SizeType>(Items.size());
    148       emitComment(Comment);
    149       Streamer->emitIntValue(Size, sizeof(Size));
    150       incrStreamedLen(sizeof(Size)); // add 1 for the delimiter
    151 
    152       for (auto &X : Items) {
    153         if (auto EC = Mapper(*this, X))
    154           return EC;
    155       }
    156     } else if (isWriting()) {
    157       Size = static_cast<SizeType>(Items.size());
    158       if (auto EC = Writer->writeInteger(Size))
    159         return EC;
    160 
    161       for (auto &X : Items) {
    162         if (auto EC = Mapper(*this, X))
    163           return EC;
    164       }
    165     } else {
    166       if (auto EC = Reader->readInteger(Size))
    167         return EC;
    168       for (SizeType I = 0; I < Size; ++I) {
    169         typename T::value_type Item;
    170         if (auto EC = Mapper(*this, Item))
    171           return EC;
    172         Items.push_back(Item);
    173       }
    174     }
    175 
    176     return Error::success();
    177   }
    178 
    179   template <typename T, typename ElementMapper>
    180   Error mapVectorTail(T &Items, const ElementMapper &Mapper,
    181                       const Twine &Comment = "") {
    182     emitComment(Comment);
    183     if (isStreaming() || isWriting()) {
    184       for (auto &Item : Items) {
    185         if (auto EC = Mapper(*this, Item))
    186           return EC;
    187       }
    188     } else {
    189       typename T::value_type Field;
    190       // Stop when we run out of bytes or we hit record padding bytes.
    191       while (!Reader->empty() && Reader->peek() < 0xf0 /* LF_PAD0 */) {
    192         if (auto EC = Mapper(*this, Field))
    193           return EC;
    194         Items.push_back(Field);
    195       }
    196     }
    197     return Error::success();
    198   }
    199 
    200   Error mapByteVectorTail(ArrayRef<uint8_t> &Bytes, const Twine &Comment = "");
    201   Error mapByteVectorTail(std::vector<uint8_t> &Bytes,
    202                           const Twine &Comment = "");
    203 
    204   Error padToAlignment(uint32_t Align);
    205   Error skipPadding();
    206 
    207   uint64_t getStreamedLen() {
    208     if (isStreaming())
    209       return StreamedLen;
    210     return 0;
    211   }
    212 
    213   void emitRawComment(const Twine &T) {
    214     if (isStreaming() && Streamer->isVerboseAsm())
    215       Streamer->AddRawComment(T);
    216   }
    217 
    218 private:
    219   void emitEncodedSignedInteger(const int64_t &Value,
    220                                 const Twine &Comment = "");
    221   void emitEncodedUnsignedInteger(const uint64_t &Value,
    222                                   const Twine &Comment = "");
    223   Error writeEncodedSignedInteger(const int64_t &Value);
    224   Error writeEncodedUnsignedInteger(const uint64_t &Value);
    225 
    226   void incrStreamedLen(const uint64_t &Len) {
    227     if (isStreaming())
    228       StreamedLen += Len;
    229   }
    230 
    231   void resetStreamedLen() {
    232     if (isStreaming())
    233       StreamedLen = 4; // The record prefix is 4 bytes long
    234   }
    235 
    236   void emitComment(const Twine &Comment) {
    237     if (isStreaming() && Streamer->isVerboseAsm()) {
    238       Twine TComment(Comment);
    239       if (!TComment.isTriviallyEmpty())
    240         Streamer->AddComment(TComment);
    241     }
    242   }
    243 
    244   struct RecordLimit {
    245     uint32_t BeginOffset;
    246     Optional<uint32_t> MaxLength;
    247 
    248     Optional<uint32_t> bytesRemaining(uint32_t CurrentOffset) const {
    249       if (!MaxLength.hasValue())
    250         return None;
    251       assert(CurrentOffset >= BeginOffset);
    252 
    253       uint32_t BytesUsed = CurrentOffset - BeginOffset;
    254       if (BytesUsed >= *MaxLength)
    255         return 0;
    256       return *MaxLength - BytesUsed;
    257     }
    258   };
    259 
    260   SmallVector<RecordLimit, 2> Limits;
    261 
    262   BinaryStreamReader *Reader = nullptr;
    263   BinaryStreamWriter *Writer = nullptr;
    264   CodeViewRecordStreamer *Streamer = nullptr;
    265   uint64_t StreamedLen = 0;
    266 };
    267 
    268 } // end namespace codeview
    269 } // end namespace llvm
    270 
    271 #endif // LLVM_DEBUGINFO_CODEVIEW_CODEVIEWRECORDIO_H
    272