1 //===- TypeRecordMapping.cpp ------------------------------------*- 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 #include "llvm/DebugInfo/CodeView/TypeRecordMapping.h" 10 11 using namespace llvm; 12 using namespace llvm::codeview; 13 14 #define error(X) \ 15 if (auto EC = X) \ 16 return EC; 17 18 namespace { 19 struct MapOneMethodRecord { 20 explicit MapOneMethodRecord(bool IsFromOverloadList) 21 : IsFromOverloadList(IsFromOverloadList) {} 22 23 Error operator()(CodeViewRecordIO &IO, OneMethodRecord &Method) const { 24 error(IO.mapInteger(Method.Attrs.Attrs, "AccessSpecifier")); 25 if (IsFromOverloadList) { 26 uint16_t Padding = 0; 27 error(IO.mapInteger(Padding, "Padding")); 28 } 29 error(IO.mapInteger(Method.Type, "Type")); 30 if (Method.isIntroducingVirtual()) { 31 error(IO.mapInteger(Method.VFTableOffset, "VFTableOffset")); 32 } else if (IO.isReading()) 33 Method.VFTableOffset = -1; 34 35 if (!IsFromOverloadList) 36 error(IO.mapStringZ(Method.Name, "Name")); 37 38 return Error::success(); 39 } 40 41 private: 42 bool IsFromOverloadList; 43 }; 44 } 45 46 static Error mapNameAndUniqueName(CodeViewRecordIO &IO, StringRef &Name, 47 StringRef &UniqueName, bool HasUniqueName) { 48 if (IO.isWriting()) { 49 // Try to be smart about what we write here. We can't write anything too 50 // large, so if we're going to go over the limit, truncate both the name 51 // and unique name by the same amount. 52 size_t BytesLeft = IO.maxFieldLength(); 53 if (HasUniqueName) { 54 size_t BytesNeeded = Name.size() + UniqueName.size() + 2; 55 StringRef N = Name; 56 StringRef U = UniqueName; 57 if (BytesNeeded > BytesLeft) { 58 size_t BytesToDrop = (BytesNeeded - BytesLeft); 59 size_t DropN = std::min(N.size(), BytesToDrop / 2); 60 size_t DropU = std::min(U.size(), BytesToDrop - DropN); 61 62 N = N.drop_back(DropN); 63 U = U.drop_back(DropU); 64 } 65 66 error(IO.mapStringZ(N)); 67 error(IO.mapStringZ(U)); 68 } else { 69 // Cap the length of the string at however many bytes we have available, 70 // plus one for the required null terminator. 71 auto N = StringRef(Name).take_front(BytesLeft - 1); 72 error(IO.mapStringZ(N)); 73 } 74 } else { 75 // Reading & Streaming mode come after writing mode is executed for each 76 // record. Truncating large names are done during writing, so its not 77 // necessary to do it while reading or streaming. 78 error(IO.mapStringZ(Name, "Name")); 79 if (HasUniqueName) 80 error(IO.mapStringZ(UniqueName, "LinkageName")); 81 } 82 83 return Error::success(); 84 } 85 86 Error TypeRecordMapping::visitTypeBegin(CVType &CVR) { 87 assert(!TypeKind.hasValue() && "Already in a type mapping!"); 88 assert(!MemberKind.hasValue() && "Already in a member mapping!"); 89 90 // FieldList and MethodList records can be any length because they can be 91 // split with continuation records. All other record types cannot be 92 // longer than the maximum record length. 93 Optional<uint32_t> MaxLen; 94 if (CVR.kind() != TypeLeafKind::LF_FIELDLIST && 95 CVR.kind() != TypeLeafKind::LF_METHODLIST) 96 MaxLen = MaxRecordLength - sizeof(RecordPrefix); 97 error(IO.beginRecord(MaxLen)); 98 TypeKind = CVR.kind(); 99 return Error::success(); 100 } 101 102 Error TypeRecordMapping::visitTypeBegin(CVType &CVR, TypeIndex Index) { 103 return visitTypeBegin(CVR); 104 } 105 106 Error TypeRecordMapping::visitTypeEnd(CVType &Record) { 107 assert(TypeKind.hasValue() && "Not in a type mapping!"); 108 assert(!MemberKind.hasValue() && "Still in a member mapping!"); 109 110 error(IO.endRecord()); 111 112 TypeKind.reset(); 113 return Error::success(); 114 } 115 116 Error TypeRecordMapping::visitMemberBegin(CVMemberRecord &Record) { 117 assert(TypeKind.hasValue() && "Not in a type mapping!"); 118 assert(!MemberKind.hasValue() && "Already in a member mapping!"); 119 120 // The largest possible subrecord is one in which there is a record prefix, 121 // followed by the subrecord, followed by a continuation, and that entire 122 // sequence spaws `MaxRecordLength` bytes. So the record's length is 123 // calculated as follows. 124 constexpr uint32_t ContinuationLength = 8; 125 error(IO.beginRecord(MaxRecordLength - sizeof(RecordPrefix) - 126 ContinuationLength)); 127 128 MemberKind = Record.Kind; 129 return Error::success(); 130 } 131 132 Error TypeRecordMapping::visitMemberEnd(CVMemberRecord &Record) { 133 assert(TypeKind.hasValue() && "Not in a type mapping!"); 134 assert(MemberKind.hasValue() && "Not in a member mapping!"); 135 136 if (IO.isReading()) { 137 if (auto EC = IO.skipPadding()) 138 return EC; 139 } 140 141 MemberKind.reset(); 142 error(IO.endRecord()); 143 return Error::success(); 144 } 145 146 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ModifierRecord &Record) { 147 error(IO.mapInteger(Record.ModifiedType, "ModifiedType")); 148 error(IO.mapEnum(Record.Modifiers, "Modifiers")); 149 return Error::success(); 150 } 151 152 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, 153 ProcedureRecord &Record) { 154 error(IO.mapInteger(Record.ReturnType, "ReturnType")); 155 error(IO.mapEnum(Record.CallConv, "CallingConvention")); 156 error(IO.mapEnum(Record.Options, "FunctionOptions")); 157 error(IO.mapInteger(Record.ParameterCount, "NumParameters")); 158 error(IO.mapInteger(Record.ArgumentList, "ArgListType")); 159 160 return Error::success(); 161 } 162 163 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, 164 MemberFunctionRecord &Record) { 165 error(IO.mapInteger(Record.ReturnType, "ReturnType")); 166 error(IO.mapInteger(Record.ClassType, "ClassType")); 167 error(IO.mapInteger(Record.ThisType, "ThisType")); 168 error(IO.mapEnum(Record.CallConv, "CallingConvention")); 169 error(IO.mapEnum(Record.Options, "FunctionOptions")); 170 error(IO.mapInteger(Record.ParameterCount, "NumParameters")); 171 error(IO.mapInteger(Record.ArgumentList, "ArgListType")); 172 error(IO.mapInteger(Record.ThisPointerAdjustment, "ThisAdjustment")); 173 174 return Error::success(); 175 } 176 177 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ArgListRecord &Record) { 178 error(IO.mapVectorN<uint32_t>( 179 Record.ArgIndices, 180 [](CodeViewRecordIO &IO, TypeIndex &N) { 181 return IO.mapInteger(N, "Argument"); 182 }, 183 "NumArgs")); 184 return Error::success(); 185 } 186 187 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, 188 StringListRecord &Record) { 189 error(IO.mapVectorN<uint32_t>( 190 Record.StringIndices, 191 [](CodeViewRecordIO &IO, TypeIndex &N) { 192 return IO.mapInteger(N, "Strings"); 193 }, 194 "NumStrings")); 195 196 return Error::success(); 197 } 198 199 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, PointerRecord &Record) { 200 error(IO.mapInteger(Record.ReferentType, "PointeeType")); 201 error(IO.mapInteger(Record.Attrs, "Attributes")); 202 203 if (Record.isPointerToMember()) { 204 if (IO.isReading()) 205 Record.MemberInfo.emplace(); 206 207 MemberPointerInfo &M = *Record.MemberInfo; 208 error(IO.mapInteger(M.ContainingType, "ClassType")); 209 error(IO.mapEnum(M.Representation, "Representation")); 210 } 211 212 return Error::success(); 213 } 214 215 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ArrayRecord &Record) { 216 error(IO.mapInteger(Record.ElementType, "ElementType")); 217 error(IO.mapInteger(Record.IndexType, "IndexType")); 218 error(IO.mapEncodedInteger(Record.Size, "SizeOf")); 219 error(IO.mapStringZ(Record.Name, "Name")); 220 221 return Error::success(); 222 } 223 224 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ClassRecord &Record) { 225 assert((CVR.kind() == TypeLeafKind::LF_STRUCTURE) || 226 (CVR.kind() == TypeLeafKind::LF_CLASS) || 227 (CVR.kind() == TypeLeafKind::LF_INTERFACE)); 228 229 error(IO.mapInteger(Record.MemberCount, "MemberCount")); 230 error(IO.mapEnum(Record.Options, "Properties")); 231 error(IO.mapInteger(Record.FieldList, "FieldList")); 232 error(IO.mapInteger(Record.DerivationList, "DerivedFrom")); 233 error(IO.mapInteger(Record.VTableShape, "VShape")); 234 error(IO.mapEncodedInteger(Record.Size, "SizeOf")); 235 error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName, 236 Record.hasUniqueName())); 237 238 return Error::success(); 239 } 240 241 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, UnionRecord &Record) { 242 error(IO.mapInteger(Record.MemberCount, "MemberCount")); 243 error(IO.mapEnum(Record.Options, "Properties")); 244 error(IO.mapInteger(Record.FieldList, "FieldList")); 245 error(IO.mapEncodedInteger(Record.Size, "SizeOf")); 246 error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName, 247 Record.hasUniqueName())); 248 249 return Error::success(); 250 } 251 252 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, EnumRecord &Record) { 253 error(IO.mapInteger(Record.MemberCount, "NumEnumerators")); 254 error(IO.mapEnum(Record.Options, "Properties")); 255 error(IO.mapInteger(Record.UnderlyingType, "UnderlyingType")); 256 error(IO.mapInteger(Record.FieldList, "FieldListType")); 257 error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName, 258 Record.hasUniqueName())); 259 260 return Error::success(); 261 } 262 263 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, BitFieldRecord &Record) { 264 error(IO.mapInteger(Record.Type, "Type")); 265 error(IO.mapInteger(Record.BitSize, "BitSize")); 266 error(IO.mapInteger(Record.BitOffset, "BitOffset")); 267 268 return Error::success(); 269 } 270 271 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, 272 VFTableShapeRecord &Record) { 273 uint16_t Size; 274 if (!IO.isReading()) { 275 ArrayRef<VFTableSlotKind> Slots = Record.getSlots(); 276 Size = Slots.size(); 277 error(IO.mapInteger(Size, "VFEntryCount")); 278 279 for (size_t SlotIndex = 0; SlotIndex < Slots.size(); SlotIndex += 2) { 280 uint8_t Byte = static_cast<uint8_t>(Slots[SlotIndex]) << 4; 281 if ((SlotIndex + 1) < Slots.size()) { 282 Byte |= static_cast<uint8_t>(Slots[SlotIndex + 1]); 283 } 284 error(IO.mapInteger(Byte)); 285 } 286 } else { 287 error(IO.mapInteger(Size)); 288 for (uint16_t I = 0; I < Size; I += 2) { 289 uint8_t Byte; 290 error(IO.mapInteger(Byte)); 291 Record.Slots.push_back(static_cast<VFTableSlotKind>(Byte & 0xF)); 292 if ((I + 1) < Size) 293 Record.Slots.push_back(static_cast<VFTableSlotKind>(Byte >> 4)); 294 } 295 } 296 297 return Error::success(); 298 } 299 300 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, VFTableRecord &Record) { 301 error(IO.mapInteger(Record.CompleteClass, "CompleteClass")); 302 error(IO.mapInteger(Record.OverriddenVFTable, "OverriddenVFTable")); 303 error(IO.mapInteger(Record.VFPtrOffset, "VFPtrOffset")); 304 uint32_t NamesLen = 0; 305 if (!IO.isReading()) { 306 for (auto Name : Record.MethodNames) 307 NamesLen += Name.size() + 1; 308 } 309 error(IO.mapInteger(NamesLen)); 310 error(IO.mapVectorTail( 311 Record.MethodNames, 312 [](CodeViewRecordIO &IO, StringRef &S) { 313 return IO.mapStringZ(S, "MethodName"); 314 }, 315 "VFTableName")); 316 317 return Error::success(); 318 } 319 320 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, StringIdRecord &Record) { 321 error(IO.mapInteger(Record.Id, "Id")); 322 error(IO.mapStringZ(Record.String, "StringData")); 323 324 return Error::success(); 325 } 326 327 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, 328 UdtSourceLineRecord &Record) { 329 error(IO.mapInteger(Record.UDT, "UDT")); 330 error(IO.mapInteger(Record.SourceFile, "SourceFile")); 331 error(IO.mapInteger(Record.LineNumber, "LineNumber")); 332 333 return Error::success(); 334 } 335 336 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, 337 UdtModSourceLineRecord &Record) { 338 error(IO.mapInteger(Record.UDT, "UDT")); 339 error(IO.mapInteger(Record.SourceFile, "SourceFile")); 340 error(IO.mapInteger(Record.LineNumber, "LineNumber")); 341 error(IO.mapInteger(Record.Module, "Module")); 342 343 return Error::success(); 344 } 345 346 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, FuncIdRecord &Record) { 347 error(IO.mapInteger(Record.ParentScope, "ParentScope")); 348 error(IO.mapInteger(Record.FunctionType, "FunctionType")); 349 error(IO.mapStringZ(Record.Name, "Name")); 350 351 return Error::success(); 352 } 353 354 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, 355 MemberFuncIdRecord &Record) { 356 error(IO.mapInteger(Record.ClassType, "ClassType")); 357 error(IO.mapInteger(Record.FunctionType, "FunctionType")); 358 error(IO.mapStringZ(Record.Name, "Name")); 359 360 return Error::success(); 361 } 362 363 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, 364 BuildInfoRecord &Record) { 365 error(IO.mapVectorN<uint16_t>( 366 Record.ArgIndices, 367 [](CodeViewRecordIO &IO, TypeIndex &N) { 368 return IO.mapInteger(N, "Argument"); 369 }, 370 "NumArgs")); 371 372 return Error::success(); 373 } 374 375 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, 376 MethodOverloadListRecord &Record) { 377 // TODO: Split the list into multiple records if it's longer than 64KB, using 378 // a subrecord of TypeRecordKind::Index to chain the records together. 379 error(IO.mapVectorTail(Record.Methods, MapOneMethodRecord(true), "Method")); 380 381 return Error::success(); 382 } 383 384 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, 385 FieldListRecord &Record) { 386 error(IO.mapByteVectorTail(Record.Data)); 387 388 return Error::success(); 389 } 390 391 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, 392 TypeServer2Record &Record) { 393 error(IO.mapGuid(Record.Guid, "Guid")); 394 error(IO.mapInteger(Record.Age, "Age")); 395 error(IO.mapStringZ(Record.Name, "Name")); 396 return Error::success(); 397 } 398 399 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, LabelRecord &Record) { 400 error(IO.mapEnum(Record.Mode, "Mode")); 401 return Error::success(); 402 } 403 404 Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, 405 BaseClassRecord &Record) { 406 error(IO.mapInteger(Record.Attrs.Attrs, "AccessSpecifier")); 407 error(IO.mapInteger(Record.Type, "BaseType")); 408 error(IO.mapEncodedInteger(Record.Offset, "BaseOffset")); 409 410 return Error::success(); 411 } 412 413 Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, 414 EnumeratorRecord &Record) { 415 error(IO.mapInteger(Record.Attrs.Attrs)); 416 417 // FIXME: Handle full APInt such as __int128. 418 error(IO.mapEncodedInteger(Record.Value, "EnumValue")); 419 error(IO.mapStringZ(Record.Name, "Name")); 420 421 return Error::success(); 422 } 423 424 Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, 425 DataMemberRecord &Record) { 426 error(IO.mapInteger(Record.Attrs.Attrs, "AccessSpecifier")); 427 error(IO.mapInteger(Record.Type, "Type")); 428 error(IO.mapEncodedInteger(Record.FieldOffset, "FieldOffset")); 429 error(IO.mapStringZ(Record.Name, "Name")); 430 431 return Error::success(); 432 } 433 434 Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, 435 OverloadedMethodRecord &Record) { 436 error(IO.mapInteger(Record.NumOverloads, "MethodCount")); 437 error(IO.mapInteger(Record.MethodList, "MethodListIndex")); 438 error(IO.mapStringZ(Record.Name, "Name")); 439 440 return Error::success(); 441 } 442 443 Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, 444 OneMethodRecord &Record) { 445 const bool IsFromOverloadList = (TypeKind == LF_METHODLIST); 446 MapOneMethodRecord Mapper(IsFromOverloadList); 447 return Mapper(IO, Record); 448 } 449 450 Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, 451 NestedTypeRecord &Record) { 452 uint16_t Padding = 0; 453 error(IO.mapInteger(Padding, "Padding")); 454 error(IO.mapInteger(Record.Type, "Type")); 455 error(IO.mapStringZ(Record.Name, "Name")); 456 457 return Error::success(); 458 } 459 460 Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, 461 StaticDataMemberRecord &Record) { 462 463 error(IO.mapInteger(Record.Attrs.Attrs, "AccessSpecifier")); 464 error(IO.mapInteger(Record.Type, "Type")); 465 error(IO.mapStringZ(Record.Name, "Name")); 466 467 return Error::success(); 468 } 469 470 Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, 471 VirtualBaseClassRecord &Record) { 472 473 error(IO.mapInteger(Record.Attrs.Attrs, "AccessSpecifier")); 474 error(IO.mapInteger(Record.BaseType, "BaseType")); 475 error(IO.mapInteger(Record.VBPtrType, "VBPtrType")); 476 error(IO.mapEncodedInteger(Record.VBPtrOffset, "VBPtrOffset")); 477 error(IO.mapEncodedInteger(Record.VTableIndex, "VBTableIndex")); 478 479 return Error::success(); 480 } 481 482 Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, 483 VFPtrRecord &Record) { 484 uint16_t Padding = 0; 485 error(IO.mapInteger(Padding, "Padding")); 486 error(IO.mapInteger(Record.Type, "Type")); 487 488 return Error::success(); 489 } 490 491 Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, 492 ListContinuationRecord &Record) { 493 uint16_t Padding = 0; 494 error(IO.mapInteger(Padding, "Padding")); 495 error(IO.mapInteger(Record.ContinuationIndex, "ContinuationIndex")); 496 497 return Error::success(); 498 } 499 500 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, 501 PrecompRecord &Precomp) { 502 error(IO.mapInteger(Precomp.StartTypeIndex, "StartIndex")); 503 error(IO.mapInteger(Precomp.TypesCount, "Count")); 504 error(IO.mapInteger(Precomp.Signature, "Signature")); 505 error(IO.mapStringZ(Precomp.PrecompFilePath, "PrecompFile")); 506 return Error::success(); 507 } 508 509 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, 510 EndPrecompRecord &EndPrecomp) { 511 error(IO.mapInteger(EndPrecomp.Signature, "Signature")); 512 return Error::success(); 513 } 514