diff options
author | Ted Mielczarek <ted@mielczarek.org> | 2016-02-10 09:00:02 -0500 |
---|---|---|
committer | Ted Mielczarek <ted@mielczarek.org> | 2016-02-10 09:00:02 -0500 |
commit | 4912669df1fb34fb8549925a8f7e70e5ffc9e890 (patch) | |
tree | b93d28b1f168c8d6de08739fd160c4bddd997ca4 /src/processor | |
parent | Fix usage of deprecated method sendSynchronousRequest:returningResponse:error:. (diff) | |
download | breakpad-4912669df1fb34fb8549925a8f7e70e5ffc9e890.tar.xz |
Change MDCVInfoELF into something usable.
This patch changes MDCVInfoELF (which is currently unused, apparently
a vestigal bit of code landed as part of Solaris support) into a supported
CodeView format that simply contains a build id as raw bytes.
Modern ELF toolchains support build ids nicely:
https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Developer_Guide/compiling-build-id.html
It would be useful to have the original build ids of loaded modules in
Linux minidumps, since tools like Fedora's darkserver allow querying by build
id and the current Breakpad code truncates the build id to the size of a GUID,
which loses information:
https://darkserver.fedoraproject.org/
A follow-up patch will change the Linux minidump generation code to produce
MDCVInfoELF in minidumps instead of MDCVInfoPDB70. This patch should be landed
first to ensure that crash processors are able to handle this format before
dumps are generated containing it.
The full build id is exposed as the return value of Minidump::code_identifier(),
which currently just returns "id" for modules in Linux dumps. For
backwards-compatibility, Minidump::debug_identifier() continues to treat
the build id as a GUID, so debug identifiers for existing modules will not
change.
BUG=
R=mark@chromium.org
Review URL: https://codereview.chromium.org/1675413002 .
Diffstat (limited to 'src/processor')
-rw-r--r-- | src/processor/minidump.cc | 106 | ||||
-rw-r--r-- | src/processor/minidump_unittest.cc | 223 |
2 files changed, 290 insertions, 39 deletions
diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc index 753c6df7..a9b56a50 100644 --- a/src/processor/minidump.cc +++ b/src/processor/minidump.cc @@ -1853,11 +1853,30 @@ string MinidumpModule::code_identifier() const { break; } + case MD_OS_ANDROID: + case MD_OS_LINUX: { + // If ELF CodeView data is present, return the debug id. + if (cv_record_ && cv_record_signature_ == MD_CVINFOELF_SIGNATURE) { + const MDCVInfoELF* cv_record_elf = + reinterpret_cast<const MDCVInfoELF*>(&(*cv_record_)[0]); + assert(cv_record_elf->cv_signature == MD_CVINFOELF_SIGNATURE); + + for (unsigned int build_id_index = 0; + build_id_index < (cv_record_->size() - MDCVInfoELF_minsize); + ++build_id_index) { + char hexbyte[3]; + snprintf(hexbyte, sizeof(hexbyte), "%02x", + cv_record_elf->build_id[build_id_index]); + identifier += hexbyte; + } + break; + } + // Otherwise fall through to the case below. + } + case MD_OS_MAC_OS_X: case MD_OS_IOS: case MD_OS_SOLARIS: - case MD_OS_ANDROID: - case MD_OS_LINUX: case MD_OS_NACL: case MD_OS_PS3: { // TODO(mmentovai): support uuid extension if present, otherwise fall @@ -1908,6 +1927,14 @@ string MinidumpModule::debug_file() const { // GetCVRecord guarantees pdb_file_name is null-terminated. file = reinterpret_cast<const char*>(cv_record_20->pdb_file_name); + } else if (cv_record_signature_ == MD_CVINFOELF_SIGNATURE) { + // It's actually an MDCVInfoELF structure. + const MDCVInfoELF* cv_record_elf = + reinterpret_cast<const MDCVInfoELF*>(&(*cv_record_)[0]); + assert(cv_record_elf->cv_signature == MD_CVINFOELF_SIGNATURE); + + // For MDCVInfoELF, the debug file is the code file. + file = *name_; } // If there's a CodeView record but it doesn't match a known signature, @@ -1959,6 +1986,25 @@ string MinidumpModule::debug_file() const { return file; } +static string guid_and_age_to_debug_id(const MDGUID& guid, + uint32_t age) { + char identifier_string[41]; + snprintf(identifier_string, sizeof(identifier_string), + "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x", + guid.data1, + guid.data2, + guid.data3, + guid.data4[0], + guid.data4[1], + guid.data4[2], + guid.data4[3], + guid.data4[4], + guid.data4[5], + guid.data4[6], + guid.data4[7], + age); + return identifier_string; +} string MinidumpModule::debug_identifier() const { if (!valid_) { @@ -1981,22 +2027,8 @@ string MinidumpModule::debug_identifier() const { // Use the same format that the MS symbol server uses in filesystem // hierarchies. - char identifier_string[41]; - snprintf(identifier_string, sizeof(identifier_string), - "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x", - cv_record_70->signature.data1, - cv_record_70->signature.data2, - cv_record_70->signature.data3, - cv_record_70->signature.data4[0], - cv_record_70->signature.data4[1], - cv_record_70->signature.data4[2], - cv_record_70->signature.data4[3], - cv_record_70->signature.data4[4], - cv_record_70->signature.data4[5], - cv_record_70->signature.data4[6], - cv_record_70->signature.data4[7], - cv_record_70->age); - identifier = identifier_string; + identifier = guid_and_age_to_debug_id(cv_record_70->signature, + cv_record_70->age); } else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) { // It's actually an MDCVInfoPDB20 structure. const MDCVInfoPDB20* cv_record_20 = @@ -2009,6 +2041,21 @@ string MinidumpModule::debug_identifier() const { snprintf(identifier_string, sizeof(identifier_string), "%08X%x", cv_record_20->signature, cv_record_20->age); identifier = identifier_string; + } else if (cv_record_signature_ == MD_CVINFOELF_SIGNATURE) { + // It's actually an MDCVInfoELF structure. + const MDCVInfoELF* cv_record_elf = + reinterpret_cast<const MDCVInfoELF*>(&(*cv_record_)[0]); + assert(cv_record_elf->cv_signature == MD_CVINFOELF_SIGNATURE); + + // For backwards-compatibility, stuff as many bytes as will fit into + // a MDGUID and use the MS symbol server format as MDCVInfoPDB70 does + // with age = 0. Historically Breakpad would do this during dump + // writing to fit the build id data into a MDCVInfoPDB70 struct. + // The full build id is available by calling code_identifier. + MDGUID guid = {0}; + memcpy(&guid, &cv_record_elf->build_id, + cv_record_->size() - MDCVInfoELF_minsize); + identifier = guid_and_age_to_debug_id(guid, 0); } } @@ -2167,6 +2214,15 @@ const uint8_t* MinidumpModule::GetCVRecord(uint32_t* size) { "0-terminated"; return NULL; } + } else if (signature == MD_CVINFOELF_SIGNATURE) { + // Now that the structure type is known, recheck the size. + if (MDCVInfoELF_minsize > module_.cv_record.data_size) { + BPLOG(ERROR) << "MinidumpModule CodeViewELF record size mismatch, " << + MDCVInfoELF_minsize << " > " << + module_.cv_record.data_size; + return NULL; + } + // There's nothing to swap in CVInfoELF, it's just raw bytes. } // If the signature doesn't match something above, it's not something @@ -2367,6 +2423,20 @@ void MinidumpModule::Print() { cv_record_20->age); printf(" (cv_record).pdb_file_name = \"%s\"\n", cv_record_20->pdb_file_name); + } else if (cv_record_signature_ == MD_CVINFOELF_SIGNATURE) { + const MDCVInfoELF* cv_record_elf = + reinterpret_cast<const MDCVInfoELF*>(cv_record); + assert(cv_record_elf->cv_signature == MD_CVINFOELF_SIGNATURE); + + printf(" (cv_record).cv_signature = 0x%x\n", + cv_record_elf->cv_signature); + printf(" (cv_record).build_id = "); + for (unsigned int build_id_index = 0; + build_id_index < (cv_record_size - MDCVInfoELF_minsize); + ++build_id_index) { + printf("%02x", cv_record_elf->build_id[build_id_index]); + } + printf("\n"); } else { printf(" (cv_record) = "); for (unsigned int cv_byte_index = 0; diff --git a/src/processor/minidump_unittest.cc b/src/processor/minidump_unittest.cc index bb7dac64..4c5a7b2f 100644 --- a/src/processor/minidump_unittest.cc +++ b/src/processor/minidump_unittest.cc @@ -63,6 +63,7 @@ using google_breakpad::SynthMinidump::Dump; using google_breakpad::SynthMinidump::Exception; using google_breakpad::SynthMinidump::Memory; using google_breakpad::SynthMinidump::Module; +using google_breakpad::SynthMinidump::Section; using google_breakpad::SynthMinidump::Stream; using google_breakpad::SynthMinidump::String; using google_breakpad::SynthMinidump::SystemInfo; @@ -90,7 +91,15 @@ TEST_F(MinidumpTest, TestMinidumpFromFile) { const MDRawHeader* header = minidump.header(); ASSERT_NE(header, (MDRawHeader*)NULL); ASSERT_EQ(header->signature, uint32_t(MD_HEADER_SIGNATURE)); - //TODO: add more checks here + + MinidumpModuleList *md_module_list = minidump.GetModuleList(); + ASSERT_TRUE(md_module_list != NULL); + const MinidumpModule *md_module = md_module_list->GetModuleAtIndex(0); + ASSERT_TRUE(md_module != NULL); + ASSERT_EQ("c:\\test_app.exe", md_module->code_file()); + ASSERT_EQ("c:\\test_app.pdb", md_module->debug_file()); + ASSERT_EQ("45D35F6C2d000", md_module->code_identifier()); + ASSERT_EQ("5A9832E5287241C1838ED98914E9B7FF1", md_module->debug_identifier()); } TEST_F(MinidumpTest, TestMinidumpFromStream) { @@ -385,44 +394,61 @@ TEST(Dump, ThreadMissingContext) { ASSERT_EQ(reinterpret_cast<MinidumpContext*>(NULL), md_context); } -TEST(Dump, OneModule) { - static const MDVSFixedFileInfo fixed_file_info = { - 0xb2fba33a, // signature - 0x33d7a728, // struct_version - 0x31afcb20, // file_version_hi - 0xe51cdab1, // file_version_lo - 0xd1ea6907, // product_version_hi - 0x03032857, // product_version_lo - 0x11bf71d7, // file_flags_mask - 0x5fb8cdbf, // file_flags - 0xe45d0d5d, // file_os - 0x107d9562, // file_type - 0x5a8844d4, // file_subtype - 0xa8d30b20, // file_date_hi - 0x651c3e4e // file_date_lo - }; +static const MDVSFixedFileInfo fixed_file_info = { + 0xb2fba33a, // signature + 0x33d7a728, // struct_version + 0x31afcb20, // file_version_hi + 0xe51cdab1, // file_version_lo + 0xd1ea6907, // product_version_hi + 0x03032857, // product_version_lo + 0x11bf71d7, // file_flags_mask + 0x5fb8cdbf, // file_flags + 0xe45d0d5d, // file_os + 0x107d9562, // file_type + 0x5a8844d4, // file_subtype + 0xa8d30b20, // file_date_hi + 0x651c3e4e // file_date_lo +}; +TEST(Dump, OneModule) { Dump dump(0, kBigEndian); String module_name(dump, "single module"); + Section cv_info(dump); + cv_info + .D32(MD_CVINFOPDB70_SIGNATURE) // signature + // signature, a MDGUID + .D32(0xabcd1234) + .D16(0xf00d) + .D16(0xbeef) + .Append("\x01\x02\x03\x04\x05\x06\x07\x08") + .D32(1) // age + .AppendCString("c:\\foo\\file.pdb"); // pdb_file_name + + String csd_version(dump, "Windows 9000"); + SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version); + Module module(dump, 0xa90206ca83eb2852ULL, 0xada542bd, module_name, 0xb1054d2a, 0x34571371, fixed_file_info, // from synth_minidump_unittest_data.h - NULL, NULL); + &cv_info, nullptr); dump.Add(&module); dump.Add(&module_name); + dump.Add(&cv_info); + dump.Add(&system_info); + dump.Add(&csd_version); dump.Finish(); - + string contents; ASSERT_TRUE(dump.GetContents(&contents)); istringstream minidump_stream(contents); Minidump minidump(minidump_stream); ASSERT_TRUE(minidump.Read()); - ASSERT_EQ(1U, minidump.GetDirectoryEntryCount()); + ASSERT_EQ(2U, minidump.GetDirectoryEntryCount()); - const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0); + const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(1); ASSERT_TRUE(dir != NULL); EXPECT_EQ((uint32_t) MD_MODULE_LIST_STREAM, dir->stream_type); @@ -435,6 +461,10 @@ TEST(Dump, OneModule) { ASSERT_EQ(0xa90206ca83eb2852ULL, md_module->base_address()); ASSERT_EQ(0xada542bd, md_module->size()); ASSERT_EQ("single module", md_module->code_file()); + ASSERT_EQ("c:\\foo\\file.pdb", md_module->debug_file()); + // time_date_stamp and size_of_image concatenated + ASSERT_EQ("B1054D2Aada542bd", md_module->code_identifier()); + ASSERT_EQ("ABCD1234F00DBEEF01020304050607081", md_module->debug_identifier()); const MDRawModule *md_raw_module = md_module->module(); ASSERT_TRUE(md_raw_module != NULL); @@ -444,6 +474,157 @@ TEST(Dump, OneModule) { sizeof(fixed_file_info)) == 0); } +// Test that a module with a MDCVInfoELF CV record is handled properly. +TEST(Dump, OneModuleCVELF) { + Dump dump(0, kLittleEndian); + String module_name(dump, "elf module"); + Section cv_info(dump); + cv_info + .D32(MD_CVINFOELF_SIGNATURE) // signature + // build_id + .Append("\x5f\xa9\xcd\xb4\x10\x53\xdf\x1b\x86\xfa\xb7\x33\xb4\xdf" + "\x37\x38\xce\xa3\x4a\x87"); + + const MDRawSystemInfo linux_x86 = { + MD_CPU_ARCHITECTURE_X86, // processor_architecture + 6, // processor_level + 0xd08, // processor_revision + 1, // number_of_processors + 0, // product_type + 0, // major_version + 0, // minor_version + 0, // build_number + MD_OS_LINUX, // platform_id + 0xdeadbeef, // csd_version_rva + 0x100, // suite_mask + 0, // reserved2 + { // cpu + { // x86_cpu_info + { 0x756e6547, 0x49656e69, 0x6c65746e }, // vendor_id + 0x6d8, // version_information + 0xafe9fbff, // feature_information + 0xffffffff // amd_extended_cpu_features + } + } + }; + String csd_version(dump, "Literally Linux"); + SystemInfo system_info(dump, linux_x86, csd_version); + + Module module(dump, 0xa90206ca83eb2852ULL, 0xada542bd, + module_name, + 0xb1054d2a, + 0x34571371, + fixed_file_info, // from synth_minidump_unittest_data.h + &cv_info, nullptr); + + dump.Add(&module); + dump.Add(&module_name); + dump.Add(&cv_info); + dump.Add(&system_info); + dump.Add(&csd_version); + dump.Finish(); + + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + istringstream minidump_stream(contents); + Minidump minidump(minidump_stream); + ASSERT_TRUE(minidump.Read()); + + MinidumpModuleList *md_module_list = minidump.GetModuleList(); + ASSERT_TRUE(md_module_list != NULL); + ASSERT_EQ(1U, md_module_list->module_count()); + + const MinidumpModule *md_module = md_module_list->GetModuleAtIndex(0); + ASSERT_TRUE(md_module != NULL); + ASSERT_EQ(0xa90206ca83eb2852ULL, md_module->base_address()); + ASSERT_EQ(0xada542bd, md_module->size()); + ASSERT_EQ("elf module", md_module->code_file()); + // debug_file == code_file + ASSERT_EQ("elf module", md_module->debug_file()); + // just the build_id, directly + ASSERT_EQ("5fa9cdb41053df1b86fab733b4df3738cea34a87", + md_module->code_identifier()); + // build_id truncted to GUID length and treated as such, with zero + // age appended + ASSERT_EQ("B4CDA95F53101BDF86FAB733B4DF37380", md_module->debug_identifier()); + + const MDRawModule *md_raw_module = md_module->module(); + ASSERT_TRUE(md_raw_module != NULL); + ASSERT_EQ(0xb1054d2aU, md_raw_module->time_date_stamp); + ASSERT_EQ(0x34571371U, md_raw_module->checksum); + ASSERT_TRUE(memcmp(&md_raw_module->version_info, &fixed_file_info, + sizeof(fixed_file_info)) == 0); +} + +// Test that a build_id that's shorter than a GUID is handled properly. +TEST(Dump, CVELFShort) { + Dump dump(0, kLittleEndian); + String module_name(dump, "elf module"); + Section cv_info(dump); + cv_info + .D32(MD_CVINFOELF_SIGNATURE) // signature + // build_id, shorter than a GUID + .Append("\x5f\xa9\xcd\xb4"); + + const MDRawSystemInfo linux_x86 = { + MD_CPU_ARCHITECTURE_X86, // processor_architecture + 6, // processor_level + 0xd08, // processor_revision + 1, // number_of_processors + 0, // product_type + 0, // major_version + 0, // minor_version + 0, // build_number + MD_OS_LINUX, // platform_id + 0xdeadbeef, // csd_version_rva + 0x100, // suite_mask + 0, // reserved2 + { // cpu + { // x86_cpu_info + { 0x756e6547, 0x49656e69, 0x6c65746e }, // vendor_id + 0x6d8, // version_information + 0xafe9fbff, // feature_information + 0xffffffff // amd_extended_cpu_features + } + } + }; + String csd_version(dump, "Literally Linux"); + SystemInfo system_info(dump, linux_x86, csd_version); + + Module module(dump, 0xa90206ca83eb2852ULL, 0xada542bd, + module_name, + 0xb1054d2a, + 0x34571371, + fixed_file_info, // from synth_minidump_unittest_data.h + &cv_info, nullptr); + + dump.Add(&module); + dump.Add(&module_name); + dump.Add(&cv_info); + dump.Add(&system_info); + dump.Add(&csd_version); + dump.Finish(); + + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + istringstream minidump_stream(contents); + Minidump minidump(minidump_stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(2U, minidump.GetDirectoryEntryCount()); + + MinidumpModuleList *md_module_list = minidump.GetModuleList(); + ASSERT_TRUE(md_module_list != NULL); + ASSERT_EQ(1U, md_module_list->module_count()); + + const MinidumpModule *md_module = md_module_list->GetModuleAtIndex(0); + ASSERT_TRUE(md_module != NULL); + // just the build_id, directly + ASSERT_EQ("5fa9cdb4", md_module->code_identifier()); + // build_id expanded to GUID length and treated as such, with zero + // age appended + ASSERT_EQ("B4CDA95F0000000000000000000000000", md_module->debug_identifier()); +} + TEST(Dump, OneSystemInfo) { Dump dump(0, kLittleEndian); String csd_version(dump, "Petulant Pierogi"); |