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
22namespace llvm {
23
24template <typename T> class ArrayRef;
25class APSInt;
26
27namespace codeview {
28class TypeIndex;
29struct GUID;
30
31class CodeViewRecordStreamer {
32public:
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
43class 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
53public:
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(Data: BytesSR);
86 incrStreamedLen(Len: 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(Value: (int)Value, Size: sizeof(T));
104 incrStreamedLen(Len: 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>(Args: 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(Value: Size, Size: sizeof(Size));
150 incrStreamedLen(Len: 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
218private:
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(T: 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

source code of llvm/include/llvm/DebugInfo/CodeView/CodeViewRecordIO.h