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 = typename std::underlying_type<T>::type; 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