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/SmallVector.h"
13 #include "llvm/ADT/StringRef.h"
14 #include "llvm/DebugInfo/CodeView/CodeViewError.h"
15 #include "llvm/Support/BinaryStreamReader.h"
16 #include "llvm/Support/BinaryStreamWriter.h"
17 #include "llvm/Support/Error.h"
18 #include <cassert>
19 #include <cstdint>
20 #include <type_traits>
21 
22 namespace llvm {
23 
24 template <typename T> class ArrayRef;
25 class APSInt;
26 
27 namespace codeview {
28 class TypeIndex;
29 struct GUID;
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(std::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     std::optional<uint32_t> MaxLength;
247 
248     std::optional<uint32_t> bytesRemaining(uint32_t CurrentOffset) const {
249       if (!MaxLength)
250         return std::nullopt;
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