Skip to content

Commit b906ddc

Browse files
author
git apple-llvm automerger
committed
Merge commit '93fffe98d5c2' from llvm.org/release/11.x into apple/stable/20200714
2 parents f571cb9 + 93fffe9 commit b906ddc

File tree

4 files changed

+133
-49
lines changed

4 files changed

+133
-49
lines changed

lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp

Lines changed: 62 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,53 @@ bool ProcessMinidump::UpdateThreadList(ThreadList &old_thread_list,
510510
return new_thread_list.GetSize(false) > 0;
511511
}
512512

513+
ModuleSP ProcessMinidump::GetOrCreateModule(UUID minidump_uuid,
514+
llvm::StringRef name,
515+
ModuleSpec module_spec) {
516+
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
517+
Status error;
518+
519+
ModuleSP module_sp =
520+
GetTarget().GetOrCreateModule(module_spec, true /* notify */, &error);
521+
if (!module_sp)
522+
return module_sp;
523+
// We consider the module to be a match if the minidump UUID is a
524+
// prefix of the actual UUID, or if either of the UUIDs are empty.
525+
const auto dmp_bytes = minidump_uuid.GetBytes();
526+
const auto mod_bytes = module_sp->GetUUID().GetBytes();
527+
const bool match = dmp_bytes.empty() || mod_bytes.empty() ||
528+
mod_bytes.take_front(dmp_bytes.size()) == dmp_bytes;
529+
if (match) {
530+
LLDB_LOG(log, "Partial uuid match for {0}.", name);
531+
return module_sp;
532+
}
533+
534+
// Breakpad generates minindump files, and if there is no GNU build
535+
// ID in the binary, it will calculate a UUID by hashing first 4096
536+
// bytes of the .text section and using that as the UUID for a module
537+
// in the minidump. Facebook uses a modified breakpad client that
538+
// uses a slightly modified this hash to avoid collisions. Check for
539+
// UUIDs from the minindump that match these cases and accept the
540+
// module we find if they do match.
541+
std::vector<uint8_t> breakpad_uuid;
542+
std::vector<uint8_t> facebook_uuid;
543+
HashElfTextSection(module_sp, breakpad_uuid, facebook_uuid);
544+
if (dmp_bytes == llvm::ArrayRef<uint8_t>(breakpad_uuid)) {
545+
LLDB_LOG(log, "Breakpad .text hash match for {0}.", name);
546+
return module_sp;
547+
}
548+
if (dmp_bytes == llvm::ArrayRef<uint8_t>(facebook_uuid)) {
549+
LLDB_LOG(log, "Facebook .text hash match for {0}.", name);
550+
return module_sp;
551+
}
552+
// The UUID wasn't a partial match and didn't match the .text hash
553+
// so remove the module from the target, we will need to create a
554+
// placeholder object file.
555+
GetTarget().GetImages().Remove(module_sp);
556+
module_sp.reset();
557+
return module_sp;
558+
}
559+
513560
void ProcessMinidump::ReadModuleList() {
514561
std::vector<const minidump::Module *> filtered_modules =
515562
m_minidump_parser->GetFilteredModuleList();
@@ -539,54 +586,22 @@ void ProcessMinidump::ReadModuleList() {
539586
// add the module to the target if it finds one.
540587
lldb::ModuleSP module_sp = GetTarget().GetOrCreateModule(module_spec,
541588
true /* notify */, &error);
542-
if (!module_sp) {
543-
// Try and find a module without specifying the UUID and only looking for
544-
// the file given a basename. We then will look for a partial UUID match
545-
// if we find any matches. This function will add the module to the
546-
// target if it finds one, so we need to remove the module from the target
547-
// if the UUID doesn't match during our manual UUID verification. This
548-
// allows the "target.exec-search-paths" setting to specify one or more
549-
// directories that contain executables that can be searched for matches.
550-
ModuleSpec basename_module_spec(module_spec);
551-
basename_module_spec.GetUUID().Clear();
552-
basename_module_spec.GetFileSpec().GetDirectory().Clear();
553-
module_sp = GetTarget().GetOrCreateModule(basename_module_spec,
554-
true /* notify */, &error);
555-
if (module_sp) {
556-
// We consider the module to be a match if the minidump UUID is a
557-
// prefix of the actual UUID, or if either of the UUIDs are empty.
558-
const auto dmp_bytes = uuid.GetBytes();
559-
const auto mod_bytes = module_sp->GetUUID().GetBytes();
560-
const bool match = dmp_bytes.empty() || mod_bytes.empty() ||
561-
mod_bytes.take_front(dmp_bytes.size()) == dmp_bytes;
562-
if (!match) {
563-
// Breakpad generates minindump files, and if there is no GNU build
564-
// ID in the binary, it will calculate a UUID by hashing first 4096
565-
// bytes of the .text section and using that as the UUID for a module
566-
// in the minidump. Facebook uses a modified breakpad client that
567-
// uses a slightly modified this hash to avoid collisions. Check for
568-
// UUIDs from the minindump that match these cases and accept the
569-
// module we find if they do match.
570-
std::vector<uint8_t> breakpad_uuid;
571-
std::vector<uint8_t> facebook_uuid;
572-
HashElfTextSection(module_sp, breakpad_uuid, facebook_uuid);
573-
if (dmp_bytes == llvm::ArrayRef<uint8_t>(breakpad_uuid)) {
574-
LLDB_LOG(log, "Breakpad .text hash match for {0}.", name);
575-
} else if (dmp_bytes == llvm::ArrayRef<uint8_t>(facebook_uuid)) {
576-
LLDB_LOG(log, "Facebook .text hash match for {0}.", name);
577-
} else {
578-
// The UUID wasn't a partial match and didn't match the .text hash
579-
// so remove the module from the target, we will need to create a
580-
// placeholder object file.
581-
GetTarget().GetImages().Remove(module_sp);
582-
module_sp.reset();
583-
}
584-
} else {
585-
LLDB_LOG(log, "Partial uuid match for {0}.", name);
586-
}
587-
}
588-
} else {
589+
if (module_sp) {
589590
LLDB_LOG(log, "Full uuid match for {0}.", name);
591+
} else {
592+
// We couldn't find a module with an exactly-matching UUID. Sometimes
593+
// a minidump UUID is only a partial match or is a hash. So try again
594+
// without specifying the UUID, then again without specifying the
595+
// directory if that fails. This will allow us to find modules with
596+
// partial matches or hash UUIDs in user-provided sysroots or search
597+
// directories (target.exec-search-paths).
598+
ModuleSpec partial_module_spec = module_spec;
599+
partial_module_spec.GetUUID().Clear();
600+
module_sp = GetOrCreateModule(uuid, name, partial_module_spec);
601+
if (!module_sp) {
602+
partial_module_spec.GetFileSpec().GetDirectory().Clear();
603+
module_sp = GetOrCreateModule(uuid, name, partial_module_spec);
604+
}
590605
}
591606
if (module_sp) {
592607
// Watch out for place holder modules that have different paths, but the

lldb/source/Plugins/Process/minidump/ProcessMinidump.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ class ProcessMinidump : public Process {
102102

103103
void ReadModuleList();
104104

105+
lldb::ModuleSP GetOrCreateModule(lldb_private::UUID minidump_uuid,
106+
llvm::StringRef name,
107+
lldb_private::ModuleSpec module_spec);
108+
105109
JITLoaderList &GetJITLoaders() override;
106110

107111
private:

lldb/test/API/functionalities/postmortem/minidump-new/TestMiniDumpUUID.py

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,14 @@ class MiniDumpUUIDTestCase(TestBase):
2121
def verify_module(self, module, verify_path, verify_uuid):
2222
# Compare the filename and the directory separately. We are avoiding
2323
# SBFileSpec.fullpath because it causes a slash/backslash confusion
24-
# on Windows.
24+
# on Windows. Similarly, we compare the directories using normcase
25+
# because they may contain a Linux-style relative path from the
26+
# minidump appended to a Windows-style root path from the host.
2527
self.assertEqual(
2628
os.path.basename(verify_path), module.GetFileSpec().basename)
2729
self.assertEqual(
28-
os.path.dirname(verify_path), module.GetFileSpec().dirname or "")
30+
os.path.normcase(os.path.dirname(verify_path)),
31+
os.path.normcase(module.GetFileSpec().dirname or ""))
2932
self.assertEqual(verify_uuid, module.GetUUIDString())
3033

3134
def get_minidump_modules(self, yaml_file):
@@ -200,6 +203,50 @@ def test_breakpad_hash_match(self):
200203
# will check that this matches.
201204
self.verify_module(modules[0], so_path, "D9C480E8")
202205

206+
def test_breakpad_hash_match_sysroot(self):
207+
"""
208+
Check that we can match the breakpad .text section hash when the
209+
module is located under a user-provided sysroot.
210+
"""
211+
sysroot_path = os.path.join(self.getBuildDir(), "mock_sysroot")
212+
# Create the directory under the sysroot where the minidump reports
213+
# the module.
214+
so_dir = os.path.join(sysroot_path, "invalid", "path", "on", "current", "system")
215+
so_path = os.path.join(so_dir, "libbreakpad.so")
216+
lldbutil.mkdir_p(so_dir)
217+
self.yaml2obj("libbreakpad.yaml", so_path)
218+
self.runCmd("platform select remote-linux --sysroot '%s'" % sysroot_path)
219+
modules = self.get_minidump_modules("linux-arm-breakpad-uuid-match.yaml")
220+
self.assertEqual(1, len(modules))
221+
# LLDB makes up its own UUID as well when there is no build ID so we
222+
# will check that this matches.
223+
self.verify_module(modules[0], so_path, "D9C480E8")
224+
225+
def test_breakpad_hash_match_sysroot_decoy(self):
226+
"""
227+
Check that we can match the breakpad .text section hash when there is
228+
a module with the right name but wrong contents under a user-provided
229+
sysroot, and the right module is at the given search path..
230+
"""
231+
sysroot_path = os.path.join(self.getBuildDir(), "mock_sysroot")
232+
# Create the directory under the sysroot where the minidump reports
233+
# the module.
234+
decoy_dir = os.path.join(sysroot_path, "invalid", "path", "on", "current", "system")
235+
decoy_path = os.path.join(decoy_dir, "libbreakpad.so")
236+
lldbutil.mkdir_p(decoy_dir)
237+
self.yaml2obj("libbreakpad-decoy.yaml", decoy_path)
238+
self.runCmd("platform select remote-linux --sysroot '%s'" % sysroot_path)
239+
so_dir = os.path.join(self.getBuildDir(), "searchpath_dir")
240+
so_path = os.path.join(so_dir, "libbreakpad.so")
241+
lldbutil.mkdir_p(so_dir)
242+
self.yaml2obj("libbreakpad.yaml", so_path)
243+
self.runCmd('settings set target.exec-search-paths "%s"' % so_dir)
244+
modules = self.get_minidump_modules("linux-arm-breakpad-uuid-match.yaml")
245+
self.assertEqual(1, len(modules))
246+
# LLDB makes up its own UUID as well when there is no build ID so we
247+
# will check that this matches.
248+
self.verify_module(modules[0], so_path, "D9C480E8")
249+
203250
def test_breakpad_overflow_hash_match(self):
204251
"""
205252
This is a similar to test_breakpad_hash_match, but it verifies that
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# This has different .text contents than libbreakpad-yaml,
2+
# to simulate having different versions of the module (to
3+
# test that we pick the one matching the minidump UUID).
4+
--- !ELF
5+
FileHeader:
6+
Class: ELFCLASS32
7+
Data: ELFDATA2LSB
8+
Type: ET_DYN
9+
Machine: EM_ARM
10+
Flags: [ EF_ARM_SOFT_FLOAT, EF_ARM_EABI_VER5 ]
11+
Sections:
12+
Sections:
13+
- Name: .text
14+
Type: SHT_PROGBITS
15+
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
16+
Address: 0x0000000000010000
17+
AddressAlign: 0x0000000000000004
18+
Content: 040000001400000003000000474E5500CC

0 commit comments

Comments
 (0)