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 
26 namespace llvm {
27 
28 namespace codeview {
29 
30 class CodeViewRecordStreamer {
31 public:
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 
42 class CodeViewRecordIO {
getCurrentOffset()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 
52 public:
53   // deserializes records to structures
CodeViewRecordIO(BinaryStreamReader & Reader)54   explicit CodeViewRecordIO(BinaryStreamReader &Reader) : Reader(&Reader) {}
55 
56   // serializes records to buffer
CodeViewRecordIO(BinaryStreamWriter & Writer)57   explicit CodeViewRecordIO(BinaryStreamWriter &Writer) : Writer(&Writer) {}
58 
59   // writes records to assembly file using MC library interface
CodeViewRecordIO(CodeViewRecordStreamer & Streamer)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 
isStreaming()68   bool isStreaming() const {
69     return (Streamer != nullptr) && (Reader == nullptr) && (Writer == nullptr);
70   }
isReading()71   bool isReading() const {
72     return (Reader != nullptr) && (Streamer == nullptr) && (Writer == nullptr);
73   }
isWriting()74   bool isWriting() const {
75     return (Writer != nullptr) && (Streamer == nullptr) && (Reader == nullptr);
76   }
77 
78   uint32_t maxFieldLength() const;
79 
mapObject(T & Value)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 = std::underlying_type_t<T>;
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 
getStreamedLen()206   uint64_t getStreamedLen() {
207     if (isStreaming())
208       return StreamedLen;
209     return 0;
210   }
211 
emitRawComment(const Twine & T)212   void emitRawComment(const Twine &T) {
213     if (isStreaming() && Streamer->isVerboseAsm())
214       Streamer->AddRawComment(T);
215   }
216 
217 private:
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 
incrStreamedLen(const uint64_t & Len)225   void incrStreamedLen(const uint64_t &Len) {
226     if (isStreaming())
227       StreamedLen += Len;
228   }
229 
resetStreamedLen()230   void resetStreamedLen() {
231     if (isStreaming())
232       StreamedLen = 4; // The record prefix is 4 bytes long
233   }
234 
emitComment(const Twine & Comment)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 
bytesRemainingRecordLimit247     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