1 // Copyright 2014 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "minidump/minidump_module_writer.h"
16
17 #include <stddef.h>
18 #include <string.h>
19
20 #include <utility>
21
22 #include "base/format_macros.h"
23 #include "base/stl_util.h"
24 #include "base/strings/stringprintf.h"
25 #include "base/strings/utf_string_conversions.h"
26 #include "gtest/gtest.h"
27 #include "minidump/minidump_file_writer.h"
28 #include "minidump/test/minidump_file_writer_test_util.h"
29 #include "minidump/test/minidump_string_writer_test_util.h"
30 #include "minidump/test/minidump_writable_test_util.h"
31 #include "snapshot/test/test_module_snapshot.h"
32 #include "test/gtest_death.h"
33 #include "util/file/string_file.h"
34 #include "util/misc/implicit_cast.h"
35 #include "util/misc/uuid.h"
36
37 namespace crashpad {
38 namespace test {
39 namespace {
40
GetModuleListStream(const std::string & file_contents,const MINIDUMP_MODULE_LIST ** module_list)41 void GetModuleListStream(const std::string& file_contents,
42 const MINIDUMP_MODULE_LIST** module_list) {
43 constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
44 constexpr size_t kModuleListStreamOffset =
45 kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);
46 constexpr size_t kModulesOffset =
47 kModuleListStreamOffset + sizeof(MINIDUMP_MODULE_LIST);
48
49 ASSERT_GE(file_contents.size(), kModulesOffset);
50
51 const MINIDUMP_DIRECTORY* directory;
52 const MINIDUMP_HEADER* header =
53 MinidumpHeaderAtStart(file_contents, &directory);
54 ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));
55 ASSERT_TRUE(directory);
56
57 ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeModuleList);
58 EXPECT_EQ(directory[0].Location.Rva, kModuleListStreamOffset);
59
60 *module_list = MinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>(
61 file_contents, directory[0].Location);
62 ASSERT_TRUE(module_list);
63 }
64
TEST(MinidumpModuleWriter,EmptyModuleList)65 TEST(MinidumpModuleWriter, EmptyModuleList) {
66 MinidumpFileWriter minidump_file_writer;
67 auto module_list_writer = std::make_unique<MinidumpModuleListWriter>();
68
69 ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer)));
70
71 StringFile string_file;
72 ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
73
74 ASSERT_EQ(string_file.string().size(),
75 sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
76 sizeof(MINIDUMP_MODULE_LIST));
77
78 const MINIDUMP_MODULE_LIST* module_list = nullptr;
79 ASSERT_NO_FATAL_FAILURE(
80 GetModuleListStream(string_file.string(), &module_list));
81
82 EXPECT_EQ(module_list->NumberOfModules, 0u);
83 }
84
85 // If |expected_pdb_name| is not nullptr, |codeview_record| is used to locate a
86 // CodeView record in |file_contents|, and its fields are compared against the
87 // |expected_pdb_*| values. If |expected_pdb_uuid| is supplied, the CodeView
88 // record must be a PDB 7.0 link, otherwise, it must be a PDB 2.0 link. If
89 // |expected_pdb_name| is nullptr, |codeview_record| must not point to anything.
ExpectCodeViewRecord(const MINIDUMP_LOCATION_DESCRIPTOR * codeview_record,const std::string & file_contents,const char * expected_pdb_name,const UUID * expected_pdb_uuid,time_t expected_pdb_timestamp,uint32_t expected_pdb_age)90 void ExpectCodeViewRecord(const MINIDUMP_LOCATION_DESCRIPTOR* codeview_record,
91 const std::string& file_contents,
92 const char* expected_pdb_name,
93 const UUID* expected_pdb_uuid,
94 time_t expected_pdb_timestamp,
95 uint32_t expected_pdb_age) {
96 if (expected_pdb_name) {
97 EXPECT_NE(codeview_record->Rva, 0u);
98
99 std::string observed_pdb_name;
100 if (expected_pdb_uuid) {
101 // The CodeView record should be a PDB 7.0 link.
102 const CodeViewRecordPDB70* codeview_pdb70_record =
103 MinidumpWritableAtLocationDescriptor<CodeViewRecordPDB70>(
104 file_contents, *codeview_record);
105 ASSERT_TRUE(codeview_pdb70_record);
106 EXPECT_EQ(memcmp(expected_pdb_uuid,
107 &codeview_pdb70_record->uuid,
108 sizeof(codeview_pdb70_record->uuid)),
109 0);
110 EXPECT_EQ(codeview_pdb70_record->age, expected_pdb_age);
111
112 observed_pdb_name.assign(
113 reinterpret_cast<const char*>(&codeview_pdb70_record->pdb_name[0]),
114 codeview_record->DataSize - offsetof(CodeViewRecordPDB70, pdb_name));
115 } else {
116 // The CodeView record should be a PDB 2.0 link.
117 const CodeViewRecordPDB20* codeview_pdb20_record =
118 MinidumpWritableAtLocationDescriptor<CodeViewRecordPDB20>(
119 file_contents, *codeview_record);
120 ASSERT_TRUE(codeview_pdb20_record);
121 EXPECT_EQ(codeview_pdb20_record->timestamp,
122 static_cast<uint32_t>(expected_pdb_timestamp));
123 EXPECT_EQ(codeview_pdb20_record->age, expected_pdb_age);
124
125 observed_pdb_name.assign(
126 reinterpret_cast<const char*>(&codeview_pdb20_record->pdb_name[0]),
127 codeview_record->DataSize - offsetof(CodeViewRecordPDB20, pdb_name));
128 }
129
130 // Check for, and then remove, the NUL terminator.
131 EXPECT_EQ(observed_pdb_name[observed_pdb_name.size() - 1], '\0');
132 observed_pdb_name.resize(observed_pdb_name.size() - 1);
133
134 EXPECT_EQ(observed_pdb_name, expected_pdb_name);
135 } else {
136 // There should be no CodeView record.
137 EXPECT_EQ(codeview_record->DataSize, 0u);
138 EXPECT_EQ(codeview_record->Rva, 0u);
139 }
140 }
141
142 // If |expected_debug_name| is not nullptr, |misc_record| is used to locate a
143 // miscellanous debugging record in |file_contents|, and its fields are compared
144 // against the the |expected_debug_*| values. If |expected_debug_name| is
145 // nullptr, |misc_record| must not point to anything.
ExpectMiscellaneousDebugRecord(const MINIDUMP_LOCATION_DESCRIPTOR * misc_record,const std::string & file_contents,const char * expected_debug_name,uint32_t expected_debug_type,bool expected_debug_utf16)146 void ExpectMiscellaneousDebugRecord(
147 const MINIDUMP_LOCATION_DESCRIPTOR* misc_record,
148 const std::string& file_contents,
149 const char* expected_debug_name,
150 uint32_t expected_debug_type,
151 bool expected_debug_utf16) {
152 if (expected_debug_name) {
153 EXPECT_NE(misc_record->Rva, 0u);
154 const IMAGE_DEBUG_MISC* misc_debug_record =
155 MinidumpWritableAtLocationDescriptor<IMAGE_DEBUG_MISC>(file_contents,
156 *misc_record);
157 ASSERT_TRUE(misc_debug_record);
158 EXPECT_EQ(misc_debug_record->DataType, expected_debug_type);
159 EXPECT_EQ(misc_debug_record->Unicode != 0, expected_debug_utf16);
160 EXPECT_EQ(misc_debug_record->Reserved[0], 0u);
161 EXPECT_EQ(misc_debug_record->Reserved[1], 0u);
162 EXPECT_EQ(misc_debug_record->Reserved[2], 0u);
163
164 // Check for the NUL terminator.
165 size_t bytes_available =
166 misc_debug_record->Length - offsetof(IMAGE_DEBUG_MISC, Data);
167 EXPECT_EQ(misc_debug_record->Data[bytes_available - 1], '\0');
168 std::string observed_data(
169 reinterpret_cast<const char*>(misc_debug_record->Data));
170
171 size_t bytes_used;
172 if (misc_debug_record->Unicode) {
173 base::string16 observed_data_utf16(
174 reinterpret_cast<const base::char16*>(misc_debug_record->Data));
175 bytes_used = (observed_data_utf16.size() + 1) * sizeof(base::char16);
176 observed_data = base::UTF16ToUTF8(observed_data_utf16);
177 } else {
178 observed_data = reinterpret_cast<const char*>(misc_debug_record->Data);
179 bytes_used = (observed_data.size() + 1) * sizeof(char);
180 }
181 EXPECT_LE(bytes_used, bytes_available);
182
183 // Make sure that any padding bytes after the first NUL are also NUL.
184 for (size_t index = bytes_used; index < bytes_available; ++index) {
185 EXPECT_EQ(misc_debug_record->Data[index], '\0');
186 }
187
188 EXPECT_EQ(observed_data, expected_debug_name);
189 } else {
190 // There should be no miscellaneous debugging record.
191 EXPECT_EQ(misc_record->DataSize, 0u);
192 EXPECT_EQ(misc_record->Rva, 0u);
193 }
194 }
195
196 // ExpectModule() verifies that |expected| matches |observed|. Fields that are
197 // supposed to contain constant magic numbers are verified against the expected
198 // constants instead of |expected|. Reserved fields are verified to be 0. RVA
199 // and MINIDUMP_LOCATION_DESCRIPTOR fields are not verified against |expected|.
200 // Instead, |ModuleNameRva| is used to locate the module name, which is compared
201 // against |expected_module_name|. ExpectCodeViewRecord() and
202 // ExpectMiscellaneousDebugRecord() are used to verify the |CvRecord| and
203 // |MiscRecord| fields against |expected_pdb_*| and |expected_debug_*|
204 // parameters, respectively.
ExpectModule(const MINIDUMP_MODULE * expected,const MINIDUMP_MODULE * observed,const std::string & file_contents,const std::string & expected_module_name,const char * expected_pdb_name,const UUID * expected_pdb_uuid,time_t expected_pdb_timestamp,uint32_t expected_pdb_age,const char * expected_debug_name,uint32_t expected_debug_type,bool expected_debug_utf16)205 void ExpectModule(const MINIDUMP_MODULE* expected,
206 const MINIDUMP_MODULE* observed,
207 const std::string& file_contents,
208 const std::string& expected_module_name,
209 const char* expected_pdb_name,
210 const UUID* expected_pdb_uuid,
211 time_t expected_pdb_timestamp,
212 uint32_t expected_pdb_age,
213 const char* expected_debug_name,
214 uint32_t expected_debug_type,
215 bool expected_debug_utf16) {
216 EXPECT_EQ(observed->BaseOfImage, expected->BaseOfImage);
217 EXPECT_EQ(observed->SizeOfImage, expected->SizeOfImage);
218 EXPECT_EQ(observed->CheckSum, expected->CheckSum);
219 EXPECT_EQ(observed->TimeDateStamp, expected->TimeDateStamp);
220 EXPECT_EQ(observed->VersionInfo.dwSignature,
221 implicit_cast<uint32_t>(VS_FFI_SIGNATURE));
222 EXPECT_EQ(observed->VersionInfo.dwStrucVersion,
223 implicit_cast<uint32_t>(VS_FFI_STRUCVERSION));
224 EXPECT_EQ(observed->VersionInfo.dwFileVersionMS,
225 expected->VersionInfo.dwFileVersionMS);
226 EXPECT_EQ(observed->VersionInfo.dwFileVersionLS,
227 expected->VersionInfo.dwFileVersionLS);
228 EXPECT_EQ(observed->VersionInfo.dwProductVersionMS,
229 expected->VersionInfo.dwProductVersionMS);
230 EXPECT_EQ(observed->VersionInfo.dwProductVersionLS,
231 expected->VersionInfo.dwProductVersionLS);
232 EXPECT_EQ(observed->VersionInfo.dwFileFlagsMask,
233 expected->VersionInfo.dwFileFlagsMask);
234 EXPECT_EQ(observed->VersionInfo.dwFileFlags,
235 expected->VersionInfo.dwFileFlags);
236 EXPECT_EQ(observed->VersionInfo.dwFileOS, expected->VersionInfo.dwFileOS);
237 EXPECT_EQ(observed->VersionInfo.dwFileType, expected->VersionInfo.dwFileType);
238 EXPECT_EQ(observed->VersionInfo.dwFileSubtype,
239 expected->VersionInfo.dwFileSubtype);
240 EXPECT_EQ(observed->VersionInfo.dwFileDateMS,
241 expected->VersionInfo.dwFileDateMS);
242 EXPECT_EQ(observed->VersionInfo.dwFileDateLS,
243 expected->VersionInfo.dwFileDateLS);
244 EXPECT_EQ(observed->Reserved0, 0u);
245 EXPECT_EQ(observed->Reserved1, 0u);
246
247 EXPECT_NE(observed->ModuleNameRva, 0u);
248 base::string16 observed_module_name_utf16 =
249 MinidumpStringAtRVAAsString(file_contents, observed->ModuleNameRva);
250 base::string16 expected_module_name_utf16 =
251 base::UTF8ToUTF16(expected_module_name);
252 EXPECT_EQ(observed_module_name_utf16, expected_module_name_utf16);
253
254 ASSERT_NO_FATAL_FAILURE(ExpectCodeViewRecord(&observed->CvRecord,
255 file_contents,
256 expected_pdb_name,
257 expected_pdb_uuid,
258 expected_pdb_timestamp,
259 expected_pdb_age));
260
261 ASSERT_NO_FATAL_FAILURE(ExpectMiscellaneousDebugRecord(&observed->MiscRecord,
262 file_contents,
263 expected_debug_name,
264 expected_debug_type,
265 expected_debug_utf16));
266 }
267
268 // ExpectModuleWithBuildIDCv() is like ExpectModule( but expects the module to
269 // have a BuildID CodeView Record.
ExpectModuleWithBuildIDCv(const MINIDUMP_MODULE * expected,const MINIDUMP_MODULE * observed,const std::string & file_contents,const std::string & expected_module_name,const std::vector<uint8_t> & expected_build_id)270 void ExpectModuleWithBuildIDCv(const MINIDUMP_MODULE* expected,
271 const MINIDUMP_MODULE* observed,
272 const std::string& file_contents,
273 const std::string& expected_module_name,
274 const std::vector<uint8_t>& expected_build_id) {
275 EXPECT_EQ(observed->BaseOfImage, expected->BaseOfImage);
276 EXPECT_EQ(observed->SizeOfImage, expected->SizeOfImage);
277 EXPECT_EQ(observed->CheckSum, expected->CheckSum);
278 EXPECT_EQ(observed->TimeDateStamp, expected->TimeDateStamp);
279 EXPECT_EQ(observed->VersionInfo.dwSignature,
280 implicit_cast<uint32_t>(VS_FFI_SIGNATURE));
281 EXPECT_EQ(observed->VersionInfo.dwStrucVersion,
282 implicit_cast<uint32_t>(VS_FFI_STRUCVERSION));
283 EXPECT_EQ(observed->VersionInfo.dwFileVersionMS,
284 expected->VersionInfo.dwFileVersionMS);
285 EXPECT_EQ(observed->VersionInfo.dwFileVersionLS,
286 expected->VersionInfo.dwFileVersionLS);
287 EXPECT_EQ(observed->VersionInfo.dwProductVersionMS,
288 expected->VersionInfo.dwProductVersionMS);
289 EXPECT_EQ(observed->VersionInfo.dwProductVersionLS,
290 expected->VersionInfo.dwProductVersionLS);
291 EXPECT_EQ(observed->VersionInfo.dwFileFlagsMask,
292 expected->VersionInfo.dwFileFlagsMask);
293 EXPECT_EQ(observed->VersionInfo.dwFileFlags,
294 expected->VersionInfo.dwFileFlags);
295 EXPECT_EQ(observed->VersionInfo.dwFileOS, expected->VersionInfo.dwFileOS);
296 EXPECT_EQ(observed->VersionInfo.dwFileType, expected->VersionInfo.dwFileType);
297 EXPECT_EQ(observed->VersionInfo.dwFileSubtype,
298 expected->VersionInfo.dwFileSubtype);
299 EXPECT_EQ(observed->VersionInfo.dwFileDateMS,
300 expected->VersionInfo.dwFileDateMS);
301 EXPECT_EQ(observed->VersionInfo.dwFileDateLS,
302 expected->VersionInfo.dwFileDateLS);
303 EXPECT_EQ(observed->Reserved0, 0u);
304 EXPECT_EQ(observed->Reserved1, 0u);
305
306 EXPECT_NE(observed->ModuleNameRva, 0u);
307 base::string16 observed_module_name_utf16 =
308 MinidumpStringAtRVAAsString(file_contents, observed->ModuleNameRva);
309 base::string16 expected_module_name_utf16 =
310 base::UTF8ToUTF16(expected_module_name);
311 EXPECT_EQ(observed_module_name_utf16, expected_module_name_utf16);
312
313 const CodeViewRecordBuildID* codeview_build_id_record =
314 MinidumpWritableAtLocationDescriptor<CodeViewRecordBuildID>(
315 file_contents, observed->CvRecord);
316 ASSERT_TRUE(codeview_build_id_record);
317 EXPECT_EQ(memcmp(expected_build_id.data(),
318 &codeview_build_id_record->build_id,
319 expected_build_id.size()),
320 0);
321 }
322
TEST(MinidumpModuleWriter,EmptyModule)323 TEST(MinidumpModuleWriter, EmptyModule) {
324 MinidumpFileWriter minidump_file_writer;
325 auto module_list_writer = std::make_unique<MinidumpModuleListWriter>();
326
327 static constexpr char kModuleName[] = "test_executable";
328
329 auto module_writer = std::make_unique<MinidumpModuleWriter>();
330 module_writer->SetName(kModuleName);
331
332 module_list_writer->AddModule(std::move(module_writer));
333 ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer)));
334
335 StringFile string_file;
336 ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
337
338 ASSERT_GT(string_file.string().size(),
339 sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
340 sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE));
341
342 const MINIDUMP_MODULE_LIST* module_list = nullptr;
343 ASSERT_NO_FATAL_FAILURE(
344 GetModuleListStream(string_file.string(), &module_list));
345
346 EXPECT_EQ(module_list->NumberOfModules, 1u);
347
348 MINIDUMP_MODULE expected = {};
349 ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected,
350 &module_list->Modules[0],
351 string_file.string(),
352 kModuleName,
353 nullptr,
354 nullptr,
355 0,
356 0,
357 nullptr,
358 0,
359 false));
360 }
361
TEST(MinidumpModuleWriter,OneModule)362 TEST(MinidumpModuleWriter, OneModule) {
363 MinidumpFileWriter minidump_file_writer;
364 auto module_list_writer = std::make_unique<MinidumpModuleListWriter>();
365
366 static constexpr char kModuleName[] = "statically_linked";
367 constexpr uint64_t kModuleBase = 0x10da69000;
368 constexpr uint32_t kModuleSize = 0x1000;
369 constexpr uint32_t kChecksum = 0x76543210;
370 constexpr time_t kTimestamp = 0x386d4380;
371 constexpr uint32_t kFileVersionMS = 0x00010002;
372 constexpr uint32_t kFileVersionLS = 0x00030004;
373 constexpr uint32_t kProductVersionMS = 0x00050006;
374 constexpr uint32_t kProductVersionLS = 0x00070008;
375 constexpr uint32_t kFileFlagsMask = VS_FF_DEBUG | VS_FF_PRERELEASE |
376 VS_FF_PATCHED | VS_FF_PRIVATEBUILD |
377 VS_FF_INFOINFERRED | VS_FF_SPECIALBUILD;
378 constexpr uint32_t kFileFlags = VS_FF_PRIVATEBUILD | VS_FF_SPECIALBUILD;
379 constexpr uint32_t kFileOS = VOS_DOS;
380 constexpr uint32_t kFileType = VFT_DRV;
381 constexpr uint32_t kFileSubtype = VFT2_DRV_KEYBOARD;
382 static constexpr char kPDBName[] = "statical.pdb";
383 static constexpr uint8_t kPDBUUIDBytes[16] = {0xfe,
384 0xdc,
385 0xba,
386 0x98,
387 0x76,
388 0x54,
389 0x32,
390 0x10,
391 0x08,
392 0x19,
393 0x2a,
394 0x3b,
395 0x4c,
396 0x5d,
397 0x6e,
398 0x7f};
399 UUID pdb_uuid;
400 pdb_uuid.InitializeFromBytes(kPDBUUIDBytes);
401 constexpr uint32_t kPDBAge = 1;
402 constexpr uint32_t kDebugType = IMAGE_DEBUG_MISC_EXENAME;
403 static constexpr char kDebugName[] = "statical.dbg";
404 constexpr bool kDebugUTF16 = false;
405
406 auto module_writer = std::make_unique<MinidumpModuleWriter>();
407 module_writer->SetName(kModuleName);
408 module_writer->SetImageBaseAddress(kModuleBase);
409 module_writer->SetImageSize(kModuleSize);
410 module_writer->SetChecksum(kChecksum);
411 module_writer->SetTimestamp(kTimestamp);
412 module_writer->SetFileVersion(kFileVersionMS >> 16,
413 kFileVersionMS & 0xffff,
414 kFileVersionLS >> 16,
415 kFileVersionLS & 0xffff);
416 module_writer->SetProductVersion(kProductVersionMS >> 16,
417 kProductVersionMS & 0xffff,
418 kProductVersionLS >> 16,
419 kProductVersionLS & 0xffff);
420 module_writer->SetFileFlagsAndMask(kFileFlags, kFileFlagsMask);
421 module_writer->SetFileOS(kFileOS);
422 module_writer->SetFileTypeAndSubtype(kFileType, kFileSubtype);
423
424 auto codeview_pdb70_writer =
425 std::make_unique<MinidumpModuleCodeViewRecordPDB70Writer>();
426 codeview_pdb70_writer->SetPDBName(kPDBName);
427 codeview_pdb70_writer->SetUUIDAndAge(pdb_uuid, kPDBAge);
428 module_writer->SetCodeViewRecord(std::move(codeview_pdb70_writer));
429
430 auto misc_debug_writer =
431 std::make_unique<MinidumpModuleMiscDebugRecordWriter>();
432 misc_debug_writer->SetDataType(kDebugType);
433 misc_debug_writer->SetData(kDebugName, kDebugUTF16);
434 module_writer->SetMiscDebugRecord(std::move(misc_debug_writer));
435
436 module_list_writer->AddModule(std::move(module_writer));
437 ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer)));
438
439 StringFile string_file;
440 ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
441
442 ASSERT_GT(string_file.string().size(),
443 sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
444 sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE));
445
446 const MINIDUMP_MODULE_LIST* module_list = nullptr;
447 ASSERT_NO_FATAL_FAILURE(
448 GetModuleListStream(string_file.string(), &module_list));
449
450 EXPECT_EQ(module_list->NumberOfModules, 1u);
451
452 MINIDUMP_MODULE expected = {};
453 expected.BaseOfImage = kModuleBase;
454 expected.SizeOfImage = kModuleSize;
455 expected.CheckSum = kChecksum;
456 expected.TimeDateStamp = kTimestamp;
457 expected.VersionInfo.dwFileVersionMS = kFileVersionMS;
458 expected.VersionInfo.dwFileVersionLS = kFileVersionLS;
459 expected.VersionInfo.dwProductVersionMS = kProductVersionMS;
460 expected.VersionInfo.dwProductVersionLS = kProductVersionLS;
461 expected.VersionInfo.dwFileFlagsMask = kFileFlagsMask;
462 expected.VersionInfo.dwFileFlags = kFileFlags;
463 expected.VersionInfo.dwFileOS = kFileOS;
464 expected.VersionInfo.dwFileType = kFileType;
465 expected.VersionInfo.dwFileSubtype = kFileSubtype;
466
467 ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected,
468 &module_list->Modules[0],
469 string_file.string(),
470 kModuleName,
471 kPDBName,
472 &pdb_uuid,
473 0,
474 kPDBAge,
475 kDebugName,
476 kDebugType,
477 kDebugUTF16));
478 }
479
TEST(MinidumpModuleWriter,OneModule_CodeViewUsesPDB20_MiscUsesUTF16)480 TEST(MinidumpModuleWriter, OneModule_CodeViewUsesPDB20_MiscUsesUTF16) {
481 // MinidumpModuleWriter.OneModule tested with a PDB 7.0 link as the CodeView
482 // record and an IMAGE_DEBUG_MISC record in UTF-8. This test exercises the
483 // alternatives, a PDB 2.0 link as the CodeView record and an IMAGE_DEBUG_MISC
484 // record with UTF-16 data.
485 MinidumpFileWriter minidump_file_writer;
486 auto module_list_writer = std::make_unique<MinidumpModuleListWriter>();
487
488 static constexpr char kModuleName[] = "dinosaur";
489 static constexpr char kPDBName[] = "d1n05.pdb";
490 constexpr time_t kPDBTimestamp = 0x386d4380;
491 constexpr uint32_t kPDBAge = 1;
492 constexpr uint32_t kDebugType = IMAGE_DEBUG_MISC_EXENAME;
493 static constexpr char kDebugName[] = "d1n05.dbg";
494 constexpr bool kDebugUTF16 = true;
495
496 auto module_writer = std::make_unique<MinidumpModuleWriter>();
497 module_writer->SetName(kModuleName);
498
499 auto codeview_pdb20_writer =
500 std::make_unique<MinidumpModuleCodeViewRecordPDB20Writer>();
501 codeview_pdb20_writer->SetPDBName(kPDBName);
502 codeview_pdb20_writer->SetTimestampAndAge(kPDBTimestamp, kPDBAge);
503 module_writer->SetCodeViewRecord(std::move(codeview_pdb20_writer));
504
505 auto misc_debug_writer =
506 std::make_unique<MinidumpModuleMiscDebugRecordWriter>();
507 misc_debug_writer->SetDataType(kDebugType);
508 misc_debug_writer->SetData(kDebugName, kDebugUTF16);
509 module_writer->SetMiscDebugRecord(std::move(misc_debug_writer));
510
511 module_list_writer->AddModule(std::move(module_writer));
512 ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer)));
513
514 StringFile string_file;
515 ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
516
517 ASSERT_GT(string_file.string().size(),
518 sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
519 sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE));
520
521 const MINIDUMP_MODULE_LIST* module_list = nullptr;
522 ASSERT_NO_FATAL_FAILURE(
523 GetModuleListStream(string_file.string(), &module_list));
524
525 EXPECT_EQ(module_list->NumberOfModules, 1u);
526
527 MINIDUMP_MODULE expected = {};
528
529 ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected,
530 &module_list->Modules[0],
531 string_file.string(),
532 kModuleName,
533 kPDBName,
534 nullptr,
535 kPDBTimestamp,
536 kPDBAge,
537 kDebugName,
538 kDebugType,
539 kDebugUTF16));
540 }
541
TEST(MinidumpModuleWriter,OneModule_CodeViewBuildID)542 TEST(MinidumpModuleWriter, OneModule_CodeViewBuildID) {
543 // MinidumpModuleWriter.OneModule tested with a BuildID CodeView
544 MinidumpFileWriter minidump_file_writer;
545 auto module_list_writer = std::make_unique<MinidumpModuleListWriter>();
546
547 static constexpr char kModuleName[] = "dinosaur";
548 static constexpr char kBuildID[] =
549 "averylonghashcodeormaybeitsjustrandomnumbershardtosay";
550
551 std::vector<uint8_t> build_id_data(kBuildID, kBuildID + 53);
552
553 auto module_writer = std::make_unique<MinidumpModuleWriter>();
554 module_writer->SetName(kModuleName);
555
556 auto codeview_build_id_writer =
557 std::make_unique<MinidumpModuleCodeViewRecordBuildIDWriter>();
558 codeview_build_id_writer->SetBuildID(build_id_data);
559 module_writer->SetCodeViewRecord(std::move(codeview_build_id_writer));
560
561 module_list_writer->AddModule(std::move(module_writer));
562 ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer)));
563
564 StringFile string_file;
565 ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
566
567 ASSERT_GT(string_file.string().size(),
568 sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
569 sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE));
570
571 const MINIDUMP_MODULE_LIST* module_list = nullptr;
572 ASSERT_NO_FATAL_FAILURE(
573 GetModuleListStream(string_file.string(), &module_list));
574
575 EXPECT_EQ(module_list->NumberOfModules, 1u);
576
577 MINIDUMP_MODULE expected = {};
578
579 ASSERT_NO_FATAL_FAILURE(ExpectModuleWithBuildIDCv(&expected,
580 &module_list->Modules[0],
581 string_file.string(),
582 kModuleName,
583 build_id_data));
584 }
585
TEST(MinidumpModuleWriter,ThreeModules)586 TEST(MinidumpModuleWriter, ThreeModules) {
587 // As good exercise, this test uses three modules, one with a PDB 7.0 link as
588 // its CodeView record, one with no CodeView record, and one with a PDB 2.0
589 // link as its CodeView record.
590 MinidumpFileWriter minidump_file_writer;
591 auto module_list_writer = std::make_unique<MinidumpModuleListWriter>();
592
593 static constexpr char kModuleName0[] = "main";
594 constexpr uint64_t kModuleBase0 = 0x100101000;
595 constexpr uint32_t kModuleSize0 = 0xf000;
596 static constexpr char kPDBName0[] = "main";
597 static constexpr uint8_t kPDBUUIDBytes0[16] = {0xaa,
598 0xbb,
599 0xcc,
600 0xdd,
601 0xee,
602 0xff,
603 0x00,
604 0x11,
605 0x22,
606 0x33,
607 0x44,
608 0x55,
609 0x66,
610 0x77,
611 0x88,
612 0x99};
613 UUID pdb_uuid_0;
614 pdb_uuid_0.InitializeFromBytes(kPDBUUIDBytes0);
615 constexpr uint32_t kPDBAge0 = 0;
616
617 static constexpr char kModuleName1[] = "ld.so";
618 constexpr uint64_t kModuleBase1 = 0x200202000;
619 constexpr uint32_t kModuleSize1 = 0x1e000;
620
621 static constexpr char kModuleName2[] = "libc.so";
622 constexpr uint64_t kModuleBase2 = 0x300303000;
623 constexpr uint32_t kModuleSize2 = 0x2d000;
624 static constexpr char kPDBName2[] = "libc.so";
625 constexpr time_t kPDBTimestamp2 = 0x386d4380;
626 constexpr uint32_t kPDBAge2 = 2;
627
628 auto module_writer_0 = std::make_unique<MinidumpModuleWriter>();
629 module_writer_0->SetName(kModuleName0);
630 module_writer_0->SetImageBaseAddress(kModuleBase0);
631 module_writer_0->SetImageSize(kModuleSize0);
632
633 auto codeview_pdb70_writer_0 =
634 std::make_unique<MinidumpModuleCodeViewRecordPDB70Writer>();
635 codeview_pdb70_writer_0->SetPDBName(kPDBName0);
636 codeview_pdb70_writer_0->SetUUIDAndAge(pdb_uuid_0, kPDBAge0);
637 module_writer_0->SetCodeViewRecord(std::move(codeview_pdb70_writer_0));
638
639 module_list_writer->AddModule(std::move(module_writer_0));
640
641 auto module_writer_1 = std::make_unique<MinidumpModuleWriter>();
642 module_writer_1->SetName(kModuleName1);
643 module_writer_1->SetImageBaseAddress(kModuleBase1);
644 module_writer_1->SetImageSize(kModuleSize1);
645
646 module_list_writer->AddModule(std::move(module_writer_1));
647
648 auto module_writer_2 = std::make_unique<MinidumpModuleWriter>();
649 module_writer_2->SetName(kModuleName2);
650 module_writer_2->SetImageBaseAddress(kModuleBase2);
651 module_writer_2->SetImageSize(kModuleSize2);
652
653 auto codeview_pdb70_writer_2 =
654 std::make_unique<MinidumpModuleCodeViewRecordPDB20Writer>();
655 codeview_pdb70_writer_2->SetPDBName(kPDBName2);
656 codeview_pdb70_writer_2->SetTimestampAndAge(kPDBTimestamp2, kPDBAge2);
657 module_writer_2->SetCodeViewRecord(std::move(codeview_pdb70_writer_2));
658
659 module_list_writer->AddModule(std::move(module_writer_2));
660
661 ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer)));
662
663 StringFile string_file;
664 ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
665
666 ASSERT_GT(string_file.string().size(),
667 sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
668 sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE));
669
670 const MINIDUMP_MODULE_LIST* module_list = nullptr;
671 ASSERT_NO_FATAL_FAILURE(
672 GetModuleListStream(string_file.string(), &module_list));
673
674 EXPECT_EQ(module_list->NumberOfModules, 3u);
675
676 MINIDUMP_MODULE expected = {};
677
678 {
679 SCOPED_TRACE("module 0");
680
681 expected.BaseOfImage = kModuleBase0;
682 expected.SizeOfImage = kModuleSize0;
683
684 ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected,
685 &module_list->Modules[0],
686 string_file.string(),
687 kModuleName0,
688 kPDBName0,
689 &pdb_uuid_0,
690 0,
691 kPDBAge0,
692 nullptr,
693 0,
694 false));
695 }
696
697 {
698 SCOPED_TRACE("module 1");
699
700 expected.BaseOfImage = kModuleBase1;
701 expected.SizeOfImage = kModuleSize1;
702
703 ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected,
704 &module_list->Modules[1],
705 string_file.string(),
706 kModuleName1,
707 nullptr,
708 nullptr,
709 0,
710 0,
711 nullptr,
712 0,
713 false));
714 }
715
716 {
717 SCOPED_TRACE("module 2");
718
719 expected.BaseOfImage = kModuleBase2;
720 expected.SizeOfImage = kModuleSize2;
721
722 ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected,
723 &module_list->Modules[2],
724 string_file.string(),
725 kModuleName2,
726 kPDBName2,
727 nullptr,
728 kPDBTimestamp2,
729 kPDBAge2,
730 nullptr,
731 0,
732 false));
733 }
734 }
735
InitializeTestModuleSnapshotFromMinidumpModule(TestModuleSnapshot * module_snapshot,const MINIDUMP_MODULE & minidump_module,const std::string & name,const std::string & pdb_name,const crashpad::UUID & uuid,uint32_t age)736 void InitializeTestModuleSnapshotFromMinidumpModule(
737 TestModuleSnapshot* module_snapshot,
738 const MINIDUMP_MODULE& minidump_module,
739 const std::string& name,
740 const std::string& pdb_name,
741 const crashpad::UUID& uuid,
742 uint32_t age) {
743 module_snapshot->SetName(name);
744
745 module_snapshot->SetAddressAndSize(minidump_module.BaseOfImage,
746 minidump_module.SizeOfImage);
747 module_snapshot->SetTimestamp(minidump_module.TimeDateStamp);
748 module_snapshot->SetFileVersion(
749 minidump_module.VersionInfo.dwFileVersionMS >> 16,
750 minidump_module.VersionInfo.dwFileVersionMS & 0xffff,
751 minidump_module.VersionInfo.dwFileVersionLS >> 16,
752 minidump_module.VersionInfo.dwFileVersionLS & 0xffff);
753 module_snapshot->SetSourceVersion(
754 minidump_module.VersionInfo.dwProductVersionMS >> 16,
755 minidump_module.VersionInfo.dwProductVersionMS & 0xffff,
756 minidump_module.VersionInfo.dwProductVersionLS >> 16,
757 minidump_module.VersionInfo.dwProductVersionLS & 0xffff);
758
759 ModuleSnapshot::ModuleType module_type;
760 switch (minidump_module.VersionInfo.dwFileType) {
761 case VFT_APP:
762 module_type = ModuleSnapshot::kModuleTypeExecutable;
763 break;
764 case VFT_DLL:
765 module_type = ModuleSnapshot::kModuleTypeSharedLibrary;
766 break;
767 default:
768 module_type = ModuleSnapshot::kModuleTypeUnknown;
769 break;
770 }
771 module_snapshot->SetModuleType(module_type);
772
773 module_snapshot->SetUUIDAndAge(uuid, age);
774 module_snapshot->SetDebugFileName(pdb_name);
775 }
776
TEST(MinidumpModuleWriter,InitializeFromSnapshot)777 TEST(MinidumpModuleWriter, InitializeFromSnapshot) {
778 MINIDUMP_MODULE expect_modules[3] = {};
779 const char* module_paths[base::size(expect_modules)] = {};
780 const char* module_pdbs[base::size(expect_modules)] = {};
781 UUID uuids[base::size(expect_modules)] = {};
782 uint32_t ages[base::size(expect_modules)] = {};
783
784 expect_modules[0].BaseOfImage = 0x100101000;
785 expect_modules[0].SizeOfImage = 0xf000;
786 expect_modules[0].TimeDateStamp = 0x01234567;
787 expect_modules[0].VersionInfo.dwFileVersionMS = 0x00010002;
788 expect_modules[0].VersionInfo.dwFileVersionLS = 0x00030004;
789 expect_modules[0].VersionInfo.dwProductVersionMS = 0x00050006;
790 expect_modules[0].VersionInfo.dwProductVersionLS = 0x00070008;
791 expect_modules[0].VersionInfo.dwFileType = VFT_APP;
792 module_paths[0] = "/usr/bin/true";
793 module_pdbs[0] = "true";
794 static constexpr uint8_t kUUIDBytes0[16] = {0x00,
795 0x11,
796 0x22,
797 0x33,
798 0x44,
799 0x55,
800 0x66,
801 0x77,
802 0x88,
803 0x99,
804 0xaa,
805 0xbb,
806 0xcc,
807 0xdd,
808 0xee,
809 0xff};
810 uuids[0].InitializeFromBytes(kUUIDBytes0);
811 ages[0] = 10;
812
813 expect_modules[1].BaseOfImage = 0x200202000;
814 expect_modules[1].SizeOfImage = 0x1e1000;
815 expect_modules[1].TimeDateStamp = 0x89abcdef;
816 expect_modules[1].VersionInfo.dwFileVersionMS = 0x0009000a;
817 expect_modules[1].VersionInfo.dwFileVersionLS = 0x000b000c;
818 expect_modules[1].VersionInfo.dwProductVersionMS = 0x000d000e;
819 expect_modules[1].VersionInfo.dwProductVersionLS = 0x000f0000;
820 expect_modules[1].VersionInfo.dwFileType = VFT_DLL;
821 module_paths[1] = "/usr/lib/libSystem.B.dylib";
822 module_pdbs[1] = "libSystem.B.dylib.pdb";
823 static constexpr uint8_t kUUIDBytes1[16] = {0x00,
824 0x01,
825 0x02,
826 0x03,
827 0x04,
828 0x05,
829 0x06,
830 0x07,
831 0x08,
832 0x09,
833 0x0a,
834 0x0b,
835 0x0c,
836 0x0d,
837 0x0e,
838 0x0f};
839 uuids[1].InitializeFromBytes(kUUIDBytes1);
840 ages[1] = 20;
841
842 expect_modules[2].BaseOfImage = 0x300303000;
843 expect_modules[2].SizeOfImage = 0x2d000;
844 expect_modules[2].TimeDateStamp = 0x76543210;
845 expect_modules[2].VersionInfo.dwFileVersionMS = 0x11112222;
846 expect_modules[2].VersionInfo.dwFileVersionLS = 0x33334444;
847 expect_modules[2].VersionInfo.dwProductVersionMS = 0x9999aaaa;
848 expect_modules[2].VersionInfo.dwProductVersionLS = 0xbbbbcccc;
849 expect_modules[2].VersionInfo.dwFileType = VFT_UNKNOWN;
850 module_paths[2] = "/usr/lib/dyld";
851 module_pdbs[2] = "/usr/lib/dyld.pdb";
852 static constexpr uint8_t kUUIDBytes2[16] = {0xff,
853 0xfe,
854 0xfd,
855 0xfc,
856 0xfb,
857 0xfa,
858 0xf9,
859 0xf8,
860 0xf7,
861 0xf6,
862 0xf5,
863 0xf4,
864 0xf3,
865 0xf2,
866 0xf1,
867 0xf0};
868 uuids[2].InitializeFromBytes(kUUIDBytes2);
869 ages[2] = 30;
870
871 std::vector<std::unique_ptr<TestModuleSnapshot>> module_snapshots_owner;
872 std::vector<const ModuleSnapshot*> module_snapshots;
873 for (size_t index = 0; index < base::size(expect_modules); ++index) {
874 module_snapshots_owner.push_back(std::make_unique<TestModuleSnapshot>());
875 TestModuleSnapshot* module_snapshot = module_snapshots_owner.back().get();
876 InitializeTestModuleSnapshotFromMinidumpModule(module_snapshot,
877 expect_modules[index],
878 module_paths[index],
879 module_pdbs[index],
880 uuids[index],
881 ages[index]);
882 module_snapshots.push_back(module_snapshot);
883 }
884
885 auto module_list_writer = std::make_unique<MinidumpModuleListWriter>();
886 module_list_writer->InitializeFromSnapshot(module_snapshots);
887
888 MinidumpFileWriter minidump_file_writer;
889 ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer)));
890
891 StringFile string_file;
892 ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
893
894 const MINIDUMP_MODULE_LIST* module_list = nullptr;
895 ASSERT_NO_FATAL_FAILURE(
896 GetModuleListStream(string_file.string(), &module_list));
897
898 ASSERT_EQ(module_list->NumberOfModules, 3u);
899
900 for (size_t index = 0; index < module_list->NumberOfModules; ++index) {
901 SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index));
902 ASSERT_NO_FATAL_FAILURE(ExpectModule(&expect_modules[index],
903 &module_list->Modules[index],
904 string_file.string(),
905 module_paths[index],
906 module_pdbs[index],
907 &uuids[index],
908 0,
909 ages[index],
910 nullptr,
911 0,
912 false));
913 }
914 }
915
TEST(MinidumpModuleWriterDeathTest,NoModuleName)916 TEST(MinidumpModuleWriterDeathTest, NoModuleName) {
917 MinidumpFileWriter minidump_file_writer;
918 auto module_list_writer = std::make_unique<MinidumpModuleListWriter>();
919 auto module_writer = std::make_unique<MinidumpModuleWriter>();
920 module_list_writer->AddModule(std::move(module_writer));
921 ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer)));
922
923 StringFile string_file;
924 ASSERT_DEATH_CHECK(minidump_file_writer.WriteEverything(&string_file),
925 "name_");
926 }
927
928 } // namespace
929 } // namespace test
930 } // namespace crashpad
931