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