1 //===- MinidumpTest.cpp - Tests for Minidump.cpp --------------------------===// 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/Object/Minidump.h" 10 #include "llvm/Support/MemoryBuffer.h" 11 #include "llvm/Testing/Support/Error.h" 12 #include "gtest/gtest.h" 13 14 using namespace llvm; 15 using namespace llvm::object; 16 using namespace minidump; 17 18 static Expected<std::unique_ptr<MinidumpFile>> create(ArrayRef<uint8_t> Data) { 19 return MinidumpFile::create( 20 MemoryBufferRef(toStringRef(Data), "Test buffer")); 21 } 22 23 TEST(MinidumpFile, BasicInterface) { 24 std::vector<uint8_t> Data{ // Header 25 'M', 'D', 'M', 'P', // Signature 26 0x93, 0xa7, 0, 0, // Version 27 1, 0, 0, 0, // NumberOfStreams, 28 0x20, 0, 0, 0, // StreamDirectoryRVA 29 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 30 8, 9, 0, 1, 2, 3, 4, 5, // Flags 31 // Stream Directory 32 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, 33 0x2c, 0, 0, 0, // RVA 34 // Stream 35 'C', 'P', 'U', 'I', 'N', 'F', 'O'}; 36 // A very simple minidump file which contains just a single stream. 37 auto ExpectedFile = create(Data); 38 ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); 39 const MinidumpFile &File = **ExpectedFile; 40 const Header &H = File.header(); 41 EXPECT_EQ(Header::MagicSignature, H.Signature); 42 EXPECT_EQ(Header::MagicVersion, H.Version); 43 EXPECT_EQ(1u, H.NumberOfStreams); 44 EXPECT_EQ(0x20u, H.StreamDirectoryRVA); 45 EXPECT_EQ(0x03020100u, H.Checksum); 46 EXPECT_EQ(0x07060504u, H.TimeDateStamp); 47 EXPECT_EQ(uint64_t(0x0504030201000908), H.Flags); 48 49 ASSERT_EQ(1u, File.streams().size()); 50 const Directory &Stream0 = File.streams()[0]; 51 EXPECT_EQ(StreamType::LinuxCPUInfo, Stream0.Type); 52 EXPECT_EQ(7u, Stream0.Location.DataSize); 53 EXPECT_EQ(0x2cu, Stream0.Location.RVA); 54 55 EXPECT_EQ("CPUINFO", toStringRef(File.getRawStream(Stream0))); 56 EXPECT_EQ("CPUINFO", 57 toStringRef(*File.getRawStream(StreamType::LinuxCPUInfo))); 58 59 EXPECT_THAT_EXPECTED(File.getSystemInfo(), Failed<BinaryError>()); 60 } 61 62 // Use the input from the previous test, but corrupt it in various ways 63 TEST(MinidumpFile, create_ErrorCases) { 64 std::vector<uint8_t> FileTooShort{'M', 'D', 'M', 'P'}; 65 EXPECT_THAT_EXPECTED(create(FileTooShort), Failed<BinaryError>()); 66 67 std::vector<uint8_t> WrongSignature{ 68 // Header 69 '!', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 70 1, 0, 0, 0, // NumberOfStreams, 71 0x20, 0, 0, 0, // StreamDirectoryRVA 72 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 73 8, 9, 0, 1, 2, 3, 4, 5, // Flags 74 // Stream Directory 75 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, 76 0x2c, 0, 0, 0, // RVA 77 // Stream 78 'C', 'P', 'U', 'I', 'N', 'F', 'O'}; 79 EXPECT_THAT_EXPECTED(create(WrongSignature), Failed<BinaryError>()); 80 81 std::vector<uint8_t> WrongVersion{ 82 // Header 83 'M', 'D', 'M', 'P', 0x39, 0xa7, 0, 0, // Signature, Version 84 1, 0, 0, 0, // NumberOfStreams, 85 0x20, 0, 0, 0, // StreamDirectoryRVA 86 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 87 8, 9, 0, 1, 2, 3, 4, 5, // Flags 88 // Stream Directory 89 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, 90 0x2c, 0, 0, 0, // RVA 91 // Stream 92 'C', 'P', 'U', 'I', 'N', 'F', 'O'}; 93 EXPECT_THAT_EXPECTED(create(WrongVersion), Failed<BinaryError>()); 94 95 std::vector<uint8_t> DirectoryAfterEOF{ 96 // Header 97 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 98 1, 0, 0, 0, // NumberOfStreams, 99 0x20, 1, 0, 0, // StreamDirectoryRVA 100 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 101 8, 9, 0, 1, 2, 3, 4, 5, // Flags 102 // Stream Directory 103 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, 104 0x2c, 0, 0, 0, // RVA 105 // Stream 106 'C', 'P', 'U', 'I', 'N', 'F', 'O'}; 107 EXPECT_THAT_EXPECTED(create(DirectoryAfterEOF), Failed<BinaryError>()); 108 109 std::vector<uint8_t> TruncatedDirectory{ 110 // Header 111 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 112 1, 1, 0, 0, // NumberOfStreams, 113 0x20, 0, 0, 0, // StreamDirectoryRVA 114 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 115 8, 9, 0, 1, 2, 3, 4, 5, // Flags 116 // Stream Directory 117 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, 118 0x2c, 0, 0, 0, // RVA 119 // Stream 120 'C', 'P', 'U', 'I', 'N', 'F', 'O'}; 121 EXPECT_THAT_EXPECTED(create(TruncatedDirectory), Failed<BinaryError>()); 122 123 std::vector<uint8_t> Stream0AfterEOF{ 124 // Header 125 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 126 1, 0, 0, 0, // NumberOfStreams, 127 0x20, 0, 0, 0, // StreamDirectoryRVA 128 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 129 8, 9, 0, 1, 2, 3, 4, 5, // Flags 130 // Stream Directory 131 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, 132 0x2c, 1, 0, 0, // RVA 133 // Stream 134 'C', 'P', 'U', 'I', 'N', 'F', 'O'}; 135 EXPECT_THAT_EXPECTED(create(Stream0AfterEOF), Failed<BinaryError>()); 136 137 std::vector<uint8_t> Stream0Truncated{ 138 // Header 139 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 140 1, 0, 0, 0, // NumberOfStreams, 141 0x20, 0, 0, 0, // StreamDirectoryRVA 142 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 143 8, 9, 0, 1, 2, 3, 4, 5, // Flags 144 // Stream Directory 145 3, 0, 0x67, 0x47, 8, 0, 0, 0, // Type, DataSize, 146 0x2c, 0, 0, 0, // RVA 147 // Stream 148 'C', 'P', 'U', 'I', 'N', 'F', 'O'}; 149 EXPECT_THAT_EXPECTED(create(Stream0Truncated), Failed<BinaryError>()); 150 151 std::vector<uint8_t> DuplicateStream{ 152 // Header 153 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 154 2, 0, 0, 0, // NumberOfStreams, 155 0x20, 0, 0, 0, // StreamDirectoryRVA 156 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 157 8, 9, 0, 1, 2, 3, 4, 5, // Flags 158 // Stream Directory 159 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, 160 0x40, 0, 0, 0, // RVA 161 // Stream 162 3, 0, 0x67, 0x47, 7, 0, 0, 0, // Type, DataSize, 163 0x40, 0, 0, 0, // RVA 164 // Stream 165 'C', 'P', 'U', 'I', 'N', 'F', 'O'}; 166 EXPECT_THAT_EXPECTED(create(DuplicateStream), Failed<BinaryError>()); 167 168 std::vector<uint8_t> DenseMapInfoConflict{ 169 // Header 170 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 171 1, 0, 0, 0, // NumberOfStreams, 172 0x20, 0, 0, 0, // StreamDirectoryRVA 173 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 174 8, 9, 0, 1, 2, 3, 4, 5, // Flags 175 // Stream Directory 176 0xff, 0xff, 0xff, 0xff, 7, 0, 0, 0, // Type, DataSize, 177 0x2c, 0, 0, 0, // RVA 178 // Stream 179 'C', 'P', 'U', 'I', 'N', 'F', 'O'}; 180 EXPECT_THAT_EXPECTED(create(DenseMapInfoConflict), Failed<BinaryError>()); 181 } 182 183 TEST(MinidumpFile, IngoresDummyStreams) { 184 std::vector<uint8_t> TwoDummyStreams{ 185 // Header 186 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 187 2, 0, 0, 0, // NumberOfStreams, 188 0x20, 0, 0, 0, // StreamDirectoryRVA 189 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 190 8, 9, 0, 1, 2, 3, 4, 5, // Flags 191 // Stream Directory 192 0, 0, 0, 0, 0, 0, 0, 0, // Type, DataSize, 193 0x20, 0, 0, 0, // RVA 194 0, 0, 0, 0, 0, 0, 0, 0, // Type, DataSize, 195 0x20, 0, 0, 0, // RVA 196 }; 197 auto ExpectedFile = create(TwoDummyStreams); 198 ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); 199 const MinidumpFile &File = **ExpectedFile; 200 ASSERT_EQ(2u, File.streams().size()); 201 EXPECT_EQ(StreamType::Unused, File.streams()[0].Type); 202 EXPECT_EQ(StreamType::Unused, File.streams()[1].Type); 203 EXPECT_EQ(std::nullopt, File.getRawStream(StreamType::Unused)); 204 } 205 206 TEST(MinidumpFile, getSystemInfo) { 207 std::vector<uint8_t> Data{ 208 // Header 209 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 210 1, 0, 0, 0, // NumberOfStreams, 211 0x20, 0, 0, 0, // StreamDirectoryRVA 212 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 213 8, 9, 0, 1, 2, 3, 4, 5, // Flags 214 // Stream Directory 215 7, 0, 0, 0, 56, 0, 0, 0, // Type, DataSize, 216 0x2c, 0, 0, 0, // RVA 217 // SystemInfo 218 0, 0, 1, 2, // ProcessorArch, ProcessorLevel 219 3, 4, 5, 6, // ProcessorRevision, NumberOfProcessors, ProductType 220 7, 8, 9, 0, 1, 2, 3, 4, // MajorVersion, MinorVersion 221 5, 6, 7, 8, 2, 0, 0, 0, // BuildNumber, PlatformId 222 1, 2, 3, 4, 5, 6, 7, 8, // CSDVersionRVA, SuiteMask, Reserved 223 'L', 'L', 'V', 'M', 'L', 'L', 'V', 'M', 'L', 'L', 'V', 'M', // VendorID 224 1, 2, 3, 4, 5, 6, 7, 8, // VersionInfo, FeatureInfo 225 9, 0, 1, 2, // AMDExtendedFeatures 226 }; 227 auto ExpectedFile = create(Data); 228 ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); 229 const MinidumpFile &File = **ExpectedFile; 230 231 auto ExpectedInfo = File.getSystemInfo(); 232 ASSERT_THAT_EXPECTED(ExpectedInfo, Succeeded()); 233 const SystemInfo &Info = *ExpectedInfo; 234 EXPECT_EQ(ProcessorArchitecture::X86, Info.ProcessorArch); 235 EXPECT_EQ(0x0201, Info.ProcessorLevel); 236 EXPECT_EQ(0x0403, Info.ProcessorRevision); 237 EXPECT_EQ(5, Info.NumberOfProcessors); 238 EXPECT_EQ(6, Info.ProductType); 239 EXPECT_EQ(0x00090807u, Info.MajorVersion); 240 EXPECT_EQ(0x04030201u, Info.MinorVersion); 241 EXPECT_EQ(0x08070605u, Info.BuildNumber); 242 EXPECT_EQ(OSPlatform::Win32NT, Info.PlatformId); 243 EXPECT_EQ(0x04030201u, Info.CSDVersionRVA); 244 EXPECT_EQ(0x0605u, Info.SuiteMask); 245 EXPECT_EQ(0x0807u, Info.Reserved); 246 EXPECT_EQ("LLVMLLVMLLVM", llvm::StringRef(Info.CPU.X86.VendorID, 247 sizeof(Info.CPU.X86.VendorID))); 248 EXPECT_EQ(0x04030201u, Info.CPU.X86.VersionInfo); 249 EXPECT_EQ(0x08070605u, Info.CPU.X86.FeatureInfo); 250 EXPECT_EQ(0x02010009u, Info.CPU.X86.AMDExtendedFeatures); 251 } 252 253 TEST(MinidumpFile, getString) { 254 std::vector<uint8_t> ManyStrings{ 255 // Header 256 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 257 2, 0, 0, 0, // NumberOfStreams, 258 0x20, 0, 0, 0, // StreamDirectoryRVA 259 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 260 8, 9, 0, 1, 2, 3, 4, 5, // Flags 261 // Stream Directory 262 0, 0, 0, 0, 0, 0, 0, 0, // Type, DataSize, 263 0x20, 0, 0, 0, // RVA 264 1, 0, 0, 0, 0, 0, // String1 - odd length 265 0, 0, 1, 0, 0, 0, // String2 - too long 266 2, 0, 0, 0, 0, 0xd8, // String3 - invalid utf16 267 0, 0, 0, 0, 0, 0, // String4 - "" 268 2, 0, 0, 0, 'a', 0, // String5 - "a" 269 0, // Mis-align next string 270 2, 0, 0, 0, 'a', 0, // String6 - "a" 271 272 }; 273 auto ExpectedFile = create(ManyStrings); 274 ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); 275 const MinidumpFile &File = **ExpectedFile; 276 EXPECT_THAT_EXPECTED(File.getString(44), Failed<BinaryError>()); 277 EXPECT_THAT_EXPECTED(File.getString(50), Failed<BinaryError>()); 278 EXPECT_THAT_EXPECTED(File.getString(56), Failed<BinaryError>()); 279 EXPECT_THAT_EXPECTED(File.getString(62), HasValue("")); 280 EXPECT_THAT_EXPECTED(File.getString(68), HasValue("a")); 281 EXPECT_THAT_EXPECTED(File.getString(75), HasValue("a")); 282 283 // Check the case when the size field does not fit into the remaining data. 284 EXPECT_THAT_EXPECTED(File.getString(ManyStrings.size() - 2), 285 Failed<BinaryError>()); 286 } 287 288 TEST(MinidumpFile, getModuleList) { 289 std::vector<uint8_t> OneModule{ 290 // Header 291 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 292 1, 0, 0, 0, // NumberOfStreams, 293 32, 0, 0, 0, // StreamDirectoryRVA 294 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 295 0, 0, 0, 0, 0, 0, 0, 0, // Flags 296 // Stream Directory 297 4, 0, 0, 0, 112, 0, 0, 0, // Type, DataSize, 298 44, 0, 0, 0, // RVA 299 // ModuleList 300 1, 0, 0, 0, // NumberOfModules 301 1, 2, 3, 4, 5, 6, 7, 8, // BaseOfImage 302 9, 0, 1, 2, 3, 4, 5, 6, // SizeOfImage, Checksum 303 7, 8, 9, 0, 1, 2, 3, 4, // TimeDateStamp, ModuleNameRVA 304 0, 0, 0, 0, 0, 0, 0, 0, // Signature, StructVersion 305 0, 0, 0, 0, 0, 0, 0, 0, // FileVersion 306 0, 0, 0, 0, 0, 0, 0, 0, // ProductVersion 307 0, 0, 0, 0, 0, 0, 0, 0, // FileFlagsMask, FileFlags 308 0, 0, 0, 0, // FileOS 309 0, 0, 0, 0, 0, 0, 0, 0, // FileType, FileSubType 310 0, 0, 0, 0, 0, 0, 0, 0, // FileDate 311 1, 2, 3, 4, 5, 6, 7, 8, // CvRecord 312 9, 0, 1, 2, 3, 4, 5, 6, // MiscRecord 313 7, 8, 9, 0, 1, 2, 3, 4, // Reserved0 314 5, 6, 7, 8, 9, 0, 1, 2, // Reserved1 315 }; 316 // Same as before, but with a padded module list. 317 std::vector<uint8_t> PaddedModule{ 318 // Header 319 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 320 1, 0, 0, 0, // NumberOfStreams, 321 32, 0, 0, 0, // StreamDirectoryRVA 322 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 323 0, 0, 0, 0, 0, 0, 0, 0, // Flags 324 // Stream Directory 325 4, 0, 0, 0, 116, 0, 0, 0, // Type, DataSize, 326 44, 0, 0, 0, // RVA 327 // ModuleList 328 1, 0, 0, 0, // NumberOfModules 329 0, 0, 0, 0, // Padding 330 1, 2, 3, 4, 5, 6, 7, 8, // BaseOfImage 331 9, 0, 1, 2, 3, 4, 5, 6, // SizeOfImage, Checksum 332 7, 8, 9, 0, 1, 2, 3, 4, // TimeDateStamp, ModuleNameRVA 333 0, 0, 0, 0, 0, 0, 0, 0, // Signature, StructVersion 334 0, 0, 0, 0, 0, 0, 0, 0, // FileVersion 335 0, 0, 0, 0, 0, 0, 0, 0, // ProductVersion 336 0, 0, 0, 0, 0, 0, 0, 0, // FileFlagsMask, FileFlags 337 0, 0, 0, 0, // FileOS 338 0, 0, 0, 0, 0, 0, 0, 0, // FileType, FileSubType 339 0, 0, 0, 0, 0, 0, 0, 0, // FileDate 340 1, 2, 3, 4, 5, 6, 7, 8, // CvRecord 341 9, 0, 1, 2, 3, 4, 5, 6, // MiscRecord 342 7, 8, 9, 0, 1, 2, 3, 4, // Reserved0 343 5, 6, 7, 8, 9, 0, 1, 2, // Reserved1 344 }; 345 346 for (ArrayRef<uint8_t> Data : {OneModule, PaddedModule}) { 347 auto ExpectedFile = create(Data); 348 ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); 349 const MinidumpFile &File = **ExpectedFile; 350 Expected<ArrayRef<Module>> ExpectedModule = File.getModuleList(); 351 ASSERT_THAT_EXPECTED(ExpectedModule, Succeeded()); 352 ASSERT_EQ(1u, ExpectedModule->size()); 353 const Module &M = ExpectedModule.get()[0]; 354 EXPECT_EQ(0x0807060504030201u, M.BaseOfImage); 355 EXPECT_EQ(0x02010009u, M.SizeOfImage); 356 EXPECT_EQ(0x06050403u, M.Checksum); 357 EXPECT_EQ(0x00090807u, M.TimeDateStamp); 358 EXPECT_EQ(0x04030201u, M.ModuleNameRVA); 359 EXPECT_EQ(0x04030201u, M.CvRecord.DataSize); 360 EXPECT_EQ(0x08070605u, M.CvRecord.RVA); 361 EXPECT_EQ(0x02010009u, M.MiscRecord.DataSize); 362 EXPECT_EQ(0x06050403u, M.MiscRecord.RVA); 363 EXPECT_EQ(0x0403020100090807u, M.Reserved0); 364 EXPECT_EQ(0x0201000908070605u, M.Reserved1); 365 } 366 367 std::vector<uint8_t> StreamTooShort{ 368 // Header 369 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 370 1, 0, 0, 0, // NumberOfStreams, 371 32, 0, 0, 0, // StreamDirectoryRVA 372 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 373 0, 0, 0, 0, 0, 0, 0, 0, // Flags 374 // Stream Directory 375 4, 0, 0, 0, 111, 0, 0, 0, // Type, DataSize, 376 44, 0, 0, 0, // RVA 377 // ModuleList 378 1, 0, 0, 0, // NumberOfModules 379 1, 2, 3, 4, 5, 6, 7, 8, // BaseOfImage 380 9, 0, 1, 2, 3, 4, 5, 6, // SizeOfImage, Checksum 381 7, 8, 9, 0, 1, 2, 3, 4, // TimeDateStamp, ModuleNameRVA 382 0, 0, 0, 0, 0, 0, 0, 0, // Signature, StructVersion 383 0, 0, 0, 0, 0, 0, 0, 0, // FileVersion 384 0, 0, 0, 0, 0, 0, 0, 0, // ProductVersion 385 0, 0, 0, 0, 0, 0, 0, 0, // FileFlagsMask, FileFlags 386 0, 0, 0, 0, // FileOS 387 0, 0, 0, 0, 0, 0, 0, 0, // FileType, FileSubType 388 0, 0, 0, 0, 0, 0, 0, 0, // FileDate 389 1, 2, 3, 4, 5, 6, 7, 8, // CvRecord 390 9, 0, 1, 2, 3, 4, 5, 6, // MiscRecord 391 7, 8, 9, 0, 1, 2, 3, 4, // Reserved0 392 5, 6, 7, 8, 9, 0, 1, 2, // Reserved1 393 }; 394 auto ExpectedFile = create(StreamTooShort); 395 ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); 396 const MinidumpFile &File = **ExpectedFile; 397 EXPECT_THAT_EXPECTED(File.getModuleList(), Failed<BinaryError>()); 398 } 399 400 TEST(MinidumpFile, getThreadList) { 401 std::vector<uint8_t> OneThread{ 402 // Header 403 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 404 1, 0, 0, 0, // NumberOfStreams, 405 32, 0, 0, 0, // StreamDirectoryRVA 406 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 407 0, 0, 0, 0, 0, 0, 0, 0, // Flags 408 // Stream Directory 409 3, 0, 0, 0, 52, 0, 0, 0, // Type, DataSize, 410 44, 0, 0, 0, // RVA 411 // ThreadList 412 1, 0, 0, 0, // NumberOfThreads 413 1, 2, 3, 4, 5, 6, 7, 8, // ThreadId, SuspendCount 414 9, 0, 1, 2, 3, 4, 5, 6, // PriorityClass, Priority 415 7, 8, 9, 0, 1, 2, 3, 4, // EnvironmentBlock 416 // Stack 417 5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange 418 3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA 419 // Context 420 1, 2, 3, 4, 5, 6, 7, 8, // DataSize, RVA 421 }; 422 // Same as before, but with a padded thread list. 423 std::vector<uint8_t> PaddedThread{ 424 // Header 425 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 426 1, 0, 0, 0, // NumberOfStreams, 427 32, 0, 0, 0, // StreamDirectoryRVA 428 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 429 0, 0, 0, 0, 0, 0, 0, 0, // Flags 430 // Stream Directory 431 3, 0, 0, 0, 56, 0, 0, 0, // Type, DataSize, 432 44, 0, 0, 0, // RVA 433 // ThreadList 434 1, 0, 0, 0, // NumberOfThreads 435 0, 0, 0, 0, // Padding 436 1, 2, 3, 4, 5, 6, 7, 8, // ThreadId, SuspendCount 437 9, 0, 1, 2, 3, 4, 5, 6, // PriorityClass, Priority 438 7, 8, 9, 0, 1, 2, 3, 4, // EnvironmentBlock 439 // Stack 440 5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange 441 3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA 442 // Context 443 1, 2, 3, 4, 5, 6, 7, 8, // DataSize, RVA 444 }; 445 446 for (ArrayRef<uint8_t> Data : {OneThread, PaddedThread}) { 447 auto ExpectedFile = create(Data); 448 ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); 449 const MinidumpFile &File = **ExpectedFile; 450 Expected<ArrayRef<Thread>> ExpectedThread = File.getThreadList(); 451 ASSERT_THAT_EXPECTED(ExpectedThread, Succeeded()); 452 ASSERT_EQ(1u, ExpectedThread->size()); 453 const Thread &T = ExpectedThread.get()[0]; 454 EXPECT_EQ(0x04030201u, T.ThreadId); 455 EXPECT_EQ(0x08070605u, T.SuspendCount); 456 EXPECT_EQ(0x02010009u, T.PriorityClass); 457 EXPECT_EQ(0x06050403u, T.Priority); 458 EXPECT_EQ(0x0403020100090807u, T.EnvironmentBlock); 459 EXPECT_EQ(0x0201000908070605u, T.Stack.StartOfMemoryRange); 460 EXPECT_EQ(0x06050403u, T.Stack.Memory.DataSize); 461 EXPECT_EQ(0x00090807u, T.Stack.Memory.RVA); 462 EXPECT_EQ(0x04030201u, T.Context.DataSize); 463 EXPECT_EQ(0x08070605u, T.Context.RVA); 464 } 465 } 466 467 TEST(MinidumpFile, getMemoryList) { 468 std::vector<uint8_t> OneRange{ 469 // Header 470 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 471 1, 0, 0, 0, // NumberOfStreams, 472 32, 0, 0, 0, // StreamDirectoryRVA 473 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 474 0, 0, 0, 0, 0, 0, 0, 0, // Flags 475 // Stream Directory 476 5, 0, 0, 0, 20, 0, 0, 0, // Type, DataSize, 477 44, 0, 0, 0, // RVA 478 // MemoryDescriptor 479 1, 0, 0, 0, // NumberOfMemoryRanges 480 5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange 481 3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA 482 }; 483 // Same as before, but with a padded memory list. 484 std::vector<uint8_t> PaddedRange{ 485 // Header 486 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 487 1, 0, 0, 0, // NumberOfStreams, 488 32, 0, 0, 0, // StreamDirectoryRVA 489 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 490 0, 0, 0, 0, 0, 0, 0, 0, // Flags 491 // Stream Directory 492 5, 0, 0, 0, 24, 0, 0, 0, // Type, DataSize, 493 44, 0, 0, 0, // RVA 494 // MemoryDescriptor 495 1, 0, 0, 0, // NumberOfMemoryRanges 496 0, 0, 0, 0, // Padding 497 5, 6, 7, 8, 9, 0, 1, 2, // StartOfMemoryRange 498 3, 4, 5, 6, 7, 8, 9, 0, // DataSize, RVA 499 }; 500 501 for (ArrayRef<uint8_t> Data : {OneRange, PaddedRange}) { 502 auto ExpectedFile = create(Data); 503 ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); 504 const MinidumpFile &File = **ExpectedFile; 505 Expected<ArrayRef<MemoryDescriptor>> ExpectedRanges = File.getMemoryList(); 506 ASSERT_THAT_EXPECTED(ExpectedRanges, Succeeded()); 507 ASSERT_EQ(1u, ExpectedRanges->size()); 508 const MemoryDescriptor &MD = ExpectedRanges.get()[0]; 509 EXPECT_EQ(0x0201000908070605u, MD.StartOfMemoryRange); 510 EXPECT_EQ(0x06050403u, MD.Memory.DataSize); 511 EXPECT_EQ(0x00090807u, MD.Memory.RVA); 512 } 513 } 514 515 TEST(MinidumpFile, getMemoryInfoList) { 516 std::vector<uint8_t> OneEntry{ 517 // Header 518 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 519 1, 0, 0, 0, // NumberOfStreams, 520 32, 0, 0, 0, // StreamDirectoryRVA 521 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 522 0, 0, 0, 0, 0, 0, 0, 0, // Flags 523 // Stream Directory 524 16, 0, 0, 0, 64, 0, 0, 0, // Type, DataSize, 525 44, 0, 0, 0, // RVA 526 // MemoryInfoListHeader 527 16, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry 528 1, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries 529 // MemoryInfo 530 0, 1, 2, 3, 4, 5, 6, 7, // BaseAddress 531 8, 9, 0, 1, 2, 3, 4, 5, // AllocationBase 532 16, 0, 0, 0, 6, 7, 8, 9, // AllocationProtect, Reserved0 533 0, 1, 2, 3, 4, 5, 6, 7, // RegionSize 534 0, 16, 0, 0, 32, 0, 0, 0, // State, Protect 535 0, 0, 2, 0, 8, 9, 0, 1, // Type, Reserved1 536 }; 537 538 // Same as before, but the list header is larger. 539 std::vector<uint8_t> BiggerHeader{ 540 // Header 541 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 542 1, 0, 0, 0, // NumberOfStreams, 543 32, 0, 0, 0, // StreamDirectoryRVA 544 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 545 0, 0, 0, 0, 0, 0, 0, 0, // Flags 546 // Stream Directory 547 16, 0, 0, 0, 68, 0, 0, 0, // Type, DataSize, 548 44, 0, 0, 0, // RVA 549 // MemoryInfoListHeader 550 20, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry 551 1, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries 552 0, 0, 0, 0, // ??? 553 // MemoryInfo 554 0, 1, 2, 3, 4, 5, 6, 7, // BaseAddress 555 8, 9, 0, 1, 2, 3, 4, 5, // AllocationBase 556 16, 0, 0, 0, 6, 7, 8, 9, // AllocationProtect, Reserved0 557 0, 1, 2, 3, 4, 5, 6, 7, // RegionSize 558 0, 16, 0, 0, 32, 0, 0, 0, // State, Protect 559 0, 0, 2, 0, 8, 9, 0, 1, // Type, Reserved1 560 }; 561 562 // Same as before, but the entry is larger. 563 std::vector<uint8_t> BiggerEntry{ 564 // Header 565 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 566 1, 0, 0, 0, // NumberOfStreams, 567 32, 0, 0, 0, // StreamDirectoryRVA 568 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 569 0, 0, 0, 0, 0, 0, 0, 0, // Flags 570 // Stream Directory 571 16, 0, 0, 0, 68, 0, 0, 0, // Type, DataSize, 572 44, 0, 0, 0, // RVA 573 // MemoryInfoListHeader 574 16, 0, 0, 0, 52, 0, 0, 0, // SizeOfHeader, SizeOfEntry 575 1, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries 576 // MemoryInfo 577 0, 1, 2, 3, 4, 5, 6, 7, // BaseAddress 578 8, 9, 0, 1, 2, 3, 4, 5, // AllocationBase 579 16, 0, 0, 0, 6, 7, 8, 9, // AllocationProtect, Reserved0 580 0, 1, 2, 3, 4, 5, 6, 7, // RegionSize 581 0, 16, 0, 0, 32, 0, 0, 0, // State, Protect 582 0, 0, 2, 0, 8, 9, 0, 1, // Type, Reserved1 583 0, 0, 0, 0, // ??? 584 }; 585 586 for (ArrayRef<uint8_t> Data : {OneEntry, BiggerHeader, BiggerEntry}) { 587 auto ExpectedFile = create(Data); 588 ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); 589 const MinidumpFile &File = **ExpectedFile; 590 auto ExpectedInfo = File.getMemoryInfoList(); 591 ASSERT_THAT_EXPECTED(ExpectedInfo, Succeeded()); 592 ASSERT_EQ(1, std::distance(ExpectedInfo->begin(), ExpectedInfo->end())); 593 const MemoryInfo &Info = *ExpectedInfo.get().begin(); 594 EXPECT_EQ(0x0706050403020100u, Info.BaseAddress); 595 EXPECT_EQ(0x0504030201000908u, Info.AllocationBase); 596 EXPECT_EQ(MemoryProtection::Execute, Info.AllocationProtect); 597 EXPECT_EQ(0x09080706u, Info.Reserved0); 598 EXPECT_EQ(0x0706050403020100u, Info.RegionSize); 599 EXPECT_EQ(MemoryState::Commit, Info.State); 600 EXPECT_EQ(MemoryProtection::ExecuteRead, Info.Protect); 601 EXPECT_EQ(MemoryType::Private, Info.Type); 602 EXPECT_EQ(0x01000908u, Info.Reserved1); 603 } 604 605 // Header does not fit into the stream. 606 std::vector<uint8_t> HeaderTooBig{ 607 // Header 608 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 609 1, 0, 0, 0, // NumberOfStreams, 610 32, 0, 0, 0, // StreamDirectoryRVA 611 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 612 0, 0, 0, 0, 0, 0, 0, 0, // Flags 613 // Stream Directory 614 16, 0, 0, 0, 15, 0, 0, 0, // Type, DataSize, 615 44, 0, 0, 0, // RVA 616 // MemoryInfoListHeader 617 16, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry 618 1, 0, 0, 0, 0, 0, 0, // ??? 619 }; 620 Expected<std::unique_ptr<MinidumpFile>> File = create(HeaderTooBig); 621 ASSERT_THAT_EXPECTED(File, Succeeded()); 622 EXPECT_THAT_EXPECTED(File.get()->getMemoryInfoList(), Failed<BinaryError>()); 623 624 // Header fits into the stream, but it is too small to contain the required 625 // entries. 626 std::vector<uint8_t> HeaderTooSmall{ 627 // Header 628 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 629 1, 0, 0, 0, // NumberOfStreams, 630 32, 0, 0, 0, // StreamDirectoryRVA 631 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 632 0, 0, 0, 0, 0, 0, 0, 0, // Flags 633 // Stream Directory 634 16, 0, 0, 0, 15, 0, 0, 0, // Type, DataSize, 635 44, 0, 0, 0, // RVA 636 // MemoryInfoListHeader 637 15, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry 638 1, 0, 0, 0, 0, 0, 0, // ??? 639 }; 640 File = create(HeaderTooSmall); 641 ASSERT_THAT_EXPECTED(File, Succeeded()); 642 EXPECT_THAT_EXPECTED(File.get()->getMemoryInfoList(), Failed<BinaryError>()); 643 644 std::vector<uint8_t> EntryTooBig{ 645 // Header 646 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 647 1, 0, 0, 0, // NumberOfStreams, 648 32, 0, 0, 0, // StreamDirectoryRVA 649 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 650 0, 0, 0, 0, 0, 0, 0, 0, // Flags 651 // Stream Directory 652 16, 0, 0, 0, 64, 0, 0, 0, // Type, DataSize, 653 44, 0, 0, 0, // RVA 654 // MemoryInfoListHeader 655 16, 0, 0, 0, 49, 0, 0, 0, // SizeOfHeader, SizeOfEntry 656 1, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries 657 // MemoryInfo 658 0, 1, 2, 3, 4, 5, 6, 7, // BaseAddress 659 8, 9, 0, 1, 2, 3, 4, 5, // AllocationBase 660 16, 0, 0, 0, 6, 7, 8, 9, // AllocationProtect, Reserved0 661 0, 1, 2, 3, 4, 5, 6, 7, // RegionSize 662 0, 16, 0, 0, 32, 0, 0, 0, // State, Protect 663 0, 0, 2, 0, 8, 9, 0, 1, // Type, Reserved1 664 }; 665 File = create(EntryTooBig); 666 ASSERT_THAT_EXPECTED(File, Succeeded()); 667 EXPECT_THAT_EXPECTED(File.get()->getMemoryInfoList(), Failed<BinaryError>()); 668 669 std::vector<uint8_t> ThreeEntries{ 670 // Header 671 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 672 1, 0, 0, 0, // NumberOfStreams, 673 32, 0, 0, 0, // StreamDirectoryRVA 674 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 675 0, 0, 0, 0, 0, 0, 0, 0, // Flags 676 // Stream Directory 677 16, 0, 0, 0, 160, 0, 0, 0, // Type, DataSize, 678 44, 0, 0, 0, // RVA 679 // MemoryInfoListHeader 680 16, 0, 0, 0, 48, 0, 0, 0, // SizeOfHeader, SizeOfEntry 681 3, 0, 0, 0, 0, 0, 0, 0, // NumberOfEntries 682 // MemoryInfo 683 0, 1, 2, 3, 0, 0, 0, 0, // BaseAddress 684 0, 0, 0, 0, 0, 0, 0, 0, // AllocationBase 685 0, 0, 0, 0, 0, 0, 0, 0, // AllocationProtect, Reserved0 686 0, 0, 0, 0, 0, 0, 0, 0, // RegionSize 687 0, 0, 0, 0, 0, 0, 0, 0, // State, Protect 688 0, 0, 0, 0, 0, 0, 0, 0, // Type, Reserved1 689 0, 0, 4, 5, 6, 7, 0, 0, // BaseAddress 690 0, 0, 0, 0, 0, 0, 0, 0, // AllocationBase 691 0, 0, 0, 0, 0, 0, 0, 0, // AllocationProtect, Reserved0 692 0, 0, 0, 0, 0, 0, 0, 0, // RegionSize 693 0, 0, 0, 0, 0, 0, 0, 0, // State, Protect 694 0, 0, 0, 0, 0, 0, 0, 0, // Type, Reserved1 695 0, 0, 0, 8, 9, 0, 1, 0, // BaseAddress 696 0, 0, 0, 0, 0, 0, 0, 0, // AllocationBase 697 0, 0, 0, 0, 0, 0, 0, 0, // AllocationProtect, Reserved0 698 0, 0, 0, 0, 0, 0, 0, 0, // RegionSize 699 0, 0, 0, 0, 0, 0, 0, 0, // State, Protect 700 0, 0, 0, 0, 0, 0, 0, 0, // Type, Reserved1 701 }; 702 File = create(ThreeEntries); 703 ASSERT_THAT_EXPECTED(File, Succeeded()); 704 auto ExpectedInfo = File.get()->getMemoryInfoList(); 705 ASSERT_THAT_EXPECTED(ExpectedInfo, Succeeded()); 706 EXPECT_THAT(to_vector<3>(map_range(*ExpectedInfo, 707 [](const MemoryInfo &Info) -> uint64_t { 708 return Info.BaseAddress; 709 })), 710 testing::ElementsAre(0x0000000003020100u, 0x0000070605040000u, 711 0x0001000908000000u)); 712 } 713 714 TEST(MinidumpFile, getExceptionStreams) { 715 std::vector<uint8_t> Data{ 716 // Header 717 'M', 'D', 'M', 'P', 0x93, 0xa7, 0, 0, // Signature, Version 718 1, 0, 0, 0, // NumberOfStreams, 719 0x20, 0, 0, 0, // StreamDirectoryRVA 720 0, 1, 2, 3, 4, 5, 6, 7, // Checksum, TimeDateStamp 721 8, 9, 0, 1, 2, 3, 4, 5, // Flags 722 // Stream Directory 723 6, 0, 0, 0, 168, 0, 0, 0, // Type, DataSize, 724 0x2c, 0, 0, 0, // RVA 725 // Exception Stream 726 1, 2, 3, 4, // Thread ID 727 0, 0, 0, 0, // Padding 728 // Exception Record 729 2, 3, 4, 2, 7, 8, 8, 9, // Code, Flags 730 3, 4, 5, 6, 7, 8, 9, 10, // Inner exception record address 731 8, 7, 6, 5, 4, 3, 2, 1, // Exception address 732 4, 0, 0, 0, 0, 0, 0, 0, // Parameter count, padding 733 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // Parameter 0 734 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, // Parameter 1 735 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // Parameter 2 736 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, // Parameter 3 737 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, // Parameter 4 738 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, // Parameter 5 739 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, // Parameter 6 740 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, // Parameter 7 741 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, // Parameter 8 742 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, // Parameter 9 743 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, // Parameter 10 744 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, // Parameter 11 745 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, // Parameter 12 746 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, // Parameter 13 747 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, // Parameter 14 748 // Thread Context 749 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, // DataSize, RVA 750 }; 751 auto ExpectedFile = create(Data); 752 ASSERT_THAT_EXPECTED(ExpectedFile, Succeeded()); 753 const MinidumpFile &File = **ExpectedFile; 754 755 auto ExceptionStreams = File.getExceptionStreams(); 756 ASSERT_NE(ExceptionStreams.begin(), ExceptionStreams.end()); 757 auto ExceptionIterator = ExceptionStreams.begin(); 758 Expected<const ExceptionStream &> ExpectedStream = *ExceptionIterator; 759 ASSERT_THAT_EXPECTED(ExpectedStream, Succeeded()); 760 EXPECT_EQ(0x04030201u, ExpectedStream->ThreadId); 761 const minidump::Exception &Exception = ExpectedStream->ExceptionRecord; 762 EXPECT_EQ(0x02040302u, Exception.ExceptionCode); 763 EXPECT_EQ(0x09080807u, Exception.ExceptionFlags); 764 EXPECT_EQ(0x0a09080706050403u, Exception.ExceptionRecord); 765 EXPECT_EQ(0x0102030405060708u, Exception.ExceptionAddress); 766 EXPECT_EQ(4u, Exception.NumberParameters); 767 for (uint64_t index = 0; index < Exception.MaxParameters; ++index) { 768 EXPECT_EQ(0x1716151413121110u + index * 0x1010101010101010u, 769 Exception.ExceptionInformation[index]); 770 } 771 EXPECT_EQ(0x84838281, ExpectedStream->ThreadContext.DataSize); 772 EXPECT_EQ(0x88878685, ExpectedStream->ThreadContext.RVA); 773 ++ExceptionIterator; 774 ASSERT_EQ(ExceptionIterator, ExceptionStreams.end()); 775 } 776