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