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