Index: packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/TestMiniDumpUUID.py =================================================================== --- packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/TestMiniDumpUUID.py +++ packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/TestMiniDumpUUID.py @@ -8,6 +8,7 @@ import shutil import lldb +import os from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil @@ -132,3 +133,54 @@ self.assertEqual(2, len(modules)) self.verify_module(modules[0], "/not/exist/a", None) self.verify_module(modules[1], "/not/exist/b", None) + + def test_partial_uuid_match(self): + """ + Breakpad has been known to create minidump files using CvRecord in each + module whose signature is set to PDB70 where the UUID only contains the + first 16 bytes of a 20 byte ELF build ID. Code was added to + ProcessMinidump.cpp to deal with this and allows partial UUID matching. + + This test verifies that if we have a minidump with a 16 byte UUID, that + we are able to associate a symbol file with a 20 byte UUID only if the + first 16 bytes match. In this case we will see the path from the file + we found in the test directory and the 20 byte UUID from the actual + file, not the 16 byte shortened UUID from the minidump. + """ + so_path = self.getBuildArtifact("libuuidmatch.so") + self.yaml2obj("libuuidmatch.yaml", so_path) + self.dbg.CreateTarget(None) + self.target = self.dbg.GetSelectedTarget() + cmd = 'settings set target.exec-search-paths "%s"' % (os.path.dirname(so_path)) + self.dbg.HandleCommand(cmd) + self.process = self.target.LoadCore("linux-arm-partial-uuids-match.dmp") + modules = self.target.modules + self.assertEqual(1, len(modules)) + self.verify_module(modules[0], + "libuuidmatch.so", + "7295E17C-6668-9E05-CBB5-DEE5003865D5-5267C116") + + def test_partial_uuid_mismatch(self): + """ + Breakpad has been known to create minidump files using CvRecord in each + module whose signature is set to PDB70 where the UUID only contains the + first 16 bytes of a 20 byte ELF build ID. Code was added to + ProcessMinidump.cpp to deal with this and allows partial UUID matching. + + This test verifies that if we have a minidump with a 16 byte UUID, that + we are not able to associate a symbol file with a 20 byte UUID only if + any of the first 16 bytes do not match. In this case we will see the UUID + from the minidump file and the path from the minidump file. + """ + so_path = self.getBuildArtifact("libuuidmismatch.so") + self.yaml2obj("libuuidmatch.yaml", so_path) + self.dbg.CreateTarget(None) + self.target = self.dbg.GetSelectedTarget() + cmd = 'settings set target.exec-search-paths "%s"' % (os.path.dirname(so_path)) + self.dbg.HandleCommand(cmd) + self.process = self.target.LoadCore("linux-arm-partial-uuids-mismatch.dmp") + modules = self.target.modules + self.assertEqual(1, len(modules)) + self.verify_module(modules[0], + "/invalid/path/on/current/system/libuuidmismatch.so", + "7295E17C-6668-9E05-CBB5-DEE5003865D5") Index: packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/libuuidmatch.yaml =================================================================== --- packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/libuuidmatch.yaml +++ packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/libuuidmatch.yaml @@ -0,0 +1,14 @@ +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_ARM + Flags: [ EF_ARM_SOFT_FLOAT, EF_ARM_EABI_VER5 ] +Sections: + - Name: .note.gnu.build-id + Type: SHT_NOTE + Flags: [ SHF_ALLOC ] + Address: 0x0000000000000114 + AddressAlign: 0x0000000000000004 + Content: 040000001400000003000000474E55007295E17C66689E05CBB5DEE5003865D55267C116 Index: packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/libuuidmismatch.yaml =================================================================== --- packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/libuuidmismatch.yaml +++ packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/libuuidmismatch.yaml @@ -0,0 +1,14 @@ +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_ARM + Flags: [ EF_ARM_SOFT_FLOAT, EF_ARM_EABI_VER5 ] +Sections: + - Name: .note.gnu.build-id + Type: SHT_NOTE + Flags: [ SHF_ALLOC ] + Address: 0x0000000000000114 + AddressAlign: 0x0000000000000004 + Content: 040000001400000003000000474E55008295E17C66689E05CBB5DEE5003865D55267C116 Index: source/Plugins/Process/minidump/ProcessMinidump.cpp =================================================================== --- source/Plugins/Process/minidump/ProcessMinidump.cpp +++ source/Plugins/Process/minidump/ProcessMinidump.cpp @@ -351,6 +351,8 @@ std::vector filtered_modules = m_minidump_parser->GetFilteredModuleList(); + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES)); + for (auto module : filtered_modules) { llvm::Optional name = m_minidump_parser->GetMinidumpString(module->module_name_rva); @@ -358,7 +360,6 @@ if (!name) continue; - Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES)); if (log) { log->Printf("ProcessMinidump::%s found module: name: %s %#010" PRIx64 "-%#010" PRIx64 " size: %" PRIu32, @@ -374,14 +375,46 @@ m_is_wow64 = true; } + if (log) { + log->Printf("ProcessMinidump::%s load module: name: %s", __FUNCTION__, + name.getValue().c_str()); + } + const auto uuid = m_minidump_parser->GetModuleUUID(module); auto file_spec = FileSpec(name.getValue(), GetArchitecture().GetTriple()); FileSystem::Instance().Resolve(file_spec); ModuleSpec module_spec(file_spec, uuid); module_spec.GetArchitecture() = GetArchitecture(); Status error; + // Try and find a module with a full UUID that matches. This function will + // add the module to the target if it finds one. lldb::ModuleSP module_sp = GetTarget().GetSharedModule(module_spec, &error); - if (!module_sp || error.Fail()) { + if (!module_sp) { + // Try and find a module without specifying the UUID and only looking for + // the file given a basename. We then will look for a partial UUID match + // if we find any matches. This function will add the module to the + // target if it finds one, so we need to remove the module from the target + // if the UUID doesn't match during our manual UUID verification. This + // allows the "target.exec-search-paths" setting to specify one or more + // directories that contain executables that can be searched for matches. + ModuleSpec basename_module_spec(module_spec); + basename_module_spec.GetUUID().Clear(); + basename_module_spec.GetFileSpec().GetDirectory().Clear(); + module_sp = GetTarget().GetSharedModule(basename_module_spec, &error); + if (module_sp) { + // We consider the module to be a match if the minidump UUID is a + // prefix of the actual UUID, or if either of the UUIDs are empty. + const auto dmp_bytes = uuid.GetBytes(); + const auto mod_bytes = module_sp->GetUUID().GetBytes(); + const bool match = dmp_bytes.empty() || mod_bytes.empty() || + mod_bytes.take_front(dmp_bytes.size()) == dmp_bytes; + if (!match) { + GetTarget().GetImages().Remove(module_sp); + module_sp.reset(); + } + } + } + if (!module_sp) { // We failed to locate a matching local object file. Fortunately, the // minidump format encodes enough information about each module's memory // range to allow us to create placeholder modules. @@ -400,11 +433,6 @@ GetTarget().GetImages().Append(module_sp); } - if (log) { - log->Printf("ProcessMinidump::%s load module: name: %s", __FUNCTION__, - name.getValue().c_str()); - } - bool load_addr_changed = false; module_sp->SetLoadAddress(GetTarget(), module->base_of_image, false, load_addr_changed);