summaryrefslogtreecommitdiff
path: root/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp')
-rw-r--r--lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp193
1 files changed, 136 insertions, 57 deletions
diff --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
index fc8ee346f449..05a48acc2f7a 100644
--- a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
+++ b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
@@ -121,6 +121,72 @@ private:
lldb::addr_t m_base;
lldb::addr_t m_size;
};
+
+/// Duplicate the HashElfTextSection() from the breakpad sources.
+///
+/// Breakpad, a Google crash log reporting tool suite, creates minidump files
+/// for many different architectures. When using Breakpad to create ELF
+/// minidumps, it will check for a GNU build ID when creating a minidump file
+/// and if one doesn't exist in the file, it will say the UUID of the file is a
+/// checksum of up to the first 4096 bytes of the .text section. Facebook also
+/// uses breakpad and modified this hash to avoid collisions so we can
+/// calculate and check for this as well.
+///
+/// The breakpad code might end up hashing up to 15 bytes that immediately
+/// follow the .text section in the file, so this code must do exactly what it
+/// does so we can get an exact match for the UUID.
+///
+/// \param[in] module_sp The module to grab the .text section from.
+///
+/// \param[in/out] breakpad_uuid A vector that will receive the calculated
+/// breakpad .text hash.
+///
+/// \param[in/out] facebook_uuid A vector that will receive the calculated
+/// facebook .text hash.
+///
+void HashElfTextSection(ModuleSP module_sp, std::vector<uint8_t> &breakpad_uuid,
+ std::vector<uint8_t> &facebook_uuid) {
+ SectionList *sect_list = module_sp->GetSectionList();
+ if (sect_list == nullptr)
+ return;
+ SectionSP sect_sp = sect_list->FindSectionByName(ConstString(".text"));
+ if (!sect_sp)
+ return;
+ constexpr size_t kMDGUIDSize = 16;
+ constexpr size_t kBreakpadPageSize = 4096;
+ // The breakpad code has a bug where it might access beyond the end of a
+ // .text section by up to 15 bytes, so we must ensure we round up to the
+ // next kMDGUIDSize byte boundary.
+ DataExtractor data;
+ const size_t text_size = sect_sp->GetFileSize();
+ const size_t read_size = std::min<size_t>(
+ llvm::alignTo(text_size, kMDGUIDSize), kBreakpadPageSize);
+ sect_sp->GetObjectFile()->GetData(sect_sp->GetFileOffset(), read_size, data);
+
+ breakpad_uuid.assign(kMDGUIDSize, 0);
+ facebook_uuid.assign(kMDGUIDSize, 0);
+
+ // The only difference between the breakpad hash and the facebook hash is the
+ // hashing of the text section size into the hash prior to hashing the .text
+ // contents.
+ for (size_t i = 0; i < kMDGUIDSize; i++)
+ facebook_uuid[i] ^= text_size % 255;
+
+ // This code carefully duplicates how the hash was created in Breakpad
+ // sources, including the error where it might has an extra 15 bytes past the
+ // end of the .text section if the .text section is less than a page size in
+ // length.
+ const uint8_t *ptr = data.GetDataStart();
+ const uint8_t *ptr_end = data.GetDataEnd();
+ while (ptr < ptr_end) {
+ for (unsigned i = 0; i < kMDGUIDSize; i++) {
+ breakpad_uuid[i] ^= ptr[i];
+ facebook_uuid[i] ^= ptr[i];
+ }
+ ptr += kMDGUIDSize;
+ }
+}
+
} // namespace
ConstString ProcessMinidump::GetPluginNameStatic() {
@@ -134,8 +200,9 @@ const char *ProcessMinidump::GetPluginDescriptionStatic() {
lldb::ProcessSP ProcessMinidump::CreateInstance(lldb::TargetSP target_sp,
lldb::ListenerSP listener_sp,
- const FileSpec *crash_file) {
- if (!crash_file)
+ const FileSpec *crash_file,
+ bool can_connect) {
+ if (!crash_file || can_connect)
return nullptr;
lldb::ProcessSP process_sp;
@@ -168,7 +235,7 @@ ProcessMinidump::ProcessMinidump(lldb::TargetSP target_sp,
lldb::ListenerSP listener_sp,
const FileSpec &core_file,
DataBufferSP core_data)
- : Process(target_sp, listener_sp), m_core_file(core_file),
+ : PostMortemProcess(target_sp, listener_sp), m_core_file(core_file),
m_core_data(std::move(core_data)), m_is_wow64(false) {}
ProcessMinidump::~ProcessMinidump() {
@@ -338,32 +405,6 @@ ArchSpec ProcessMinidump::GetArchitecture() {
return ArchSpec(triple);
}
-static MemoryRegionInfo GetMemoryRegionInfo(const MemoryRegionInfos &regions,
- lldb::addr_t load_addr) {
- MemoryRegionInfo region;
- auto pos = llvm::upper_bound(regions, load_addr);
- if (pos != regions.begin() &&
- std::prev(pos)->GetRange().Contains(load_addr)) {
- return *std::prev(pos);
- }
-
- if (pos == regions.begin())
- region.GetRange().SetRangeBase(0);
- else
- region.GetRange().SetRangeBase(std::prev(pos)->GetRange().GetRangeEnd());
-
- if (pos == regions.end())
- region.GetRange().SetRangeEnd(UINT64_MAX);
- else
- region.GetRange().SetRangeEnd(pos->GetRange().GetRangeBase());
-
- region.SetReadable(MemoryRegionInfo::eNo);
- region.SetWritable(MemoryRegionInfo::eNo);
- region.SetExecutable(MemoryRegionInfo::eNo);
- region.SetMapped(MemoryRegionInfo::eNo);
- return region;
-}
-
void ProcessMinidump::BuildMemoryRegions() {
if (m_memory_regions)
return;
@@ -388,7 +429,7 @@ void ProcessMinidump::BuildMemoryRegions() {
MemoryRegionInfo::RangeType section_range(load_addr,
section_sp->GetByteSize());
MemoryRegionInfo region =
- ::GetMemoryRegionInfo(*m_memory_regions, load_addr);
+ MinidumpParser::GetMemoryRegionInfo(*m_memory_regions, load_addr);
if (region.GetMapped() != MemoryRegionInfo::eYes &&
region.GetRange().GetRangeBase() <= section_range.GetRangeBase() &&
section_range.GetRangeEnd() <= region.GetRange().GetRangeEnd()) {
@@ -409,7 +450,7 @@ void ProcessMinidump::BuildMemoryRegions() {
Status ProcessMinidump::GetMemoryRegionInfo(lldb::addr_t load_addr,
MemoryRegionInfo &region) {
BuildMemoryRegions();
- region = ::GetMemoryRegionInfo(*m_memory_regions, load_addr);
+ region = MinidumpParser::GetMemoryRegionInfo(*m_memory_regions, load_addr);
return Status();
}
@@ -421,8 +462,8 @@ Status ProcessMinidump::GetMemoryRegions(MemoryRegionInfos &region_list) {
void ProcessMinidump::Clear() { Process::m_thread_list.Clear(); }
-bool ProcessMinidump::UpdateThreadList(ThreadList &old_thread_list,
- ThreadList &new_thread_list) {
+bool ProcessMinidump::DoUpdateThreadList(ThreadList &old_thread_list,
+ ThreadList &new_thread_list) {
for (const minidump::Thread &thread : m_thread_list) {
LocationDescriptor context_location = thread.Context;
@@ -444,6 +485,53 @@ bool ProcessMinidump::UpdateThreadList(ThreadList &old_thread_list,
return new_thread_list.GetSize(false) > 0;
}
+ModuleSP ProcessMinidump::GetOrCreateModule(UUID minidump_uuid,
+ llvm::StringRef name,
+ ModuleSpec module_spec) {
+ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
+ Status error;
+
+ ModuleSP module_sp =
+ GetTarget().GetOrCreateModule(module_spec, true /* notify */, &error);
+ if (!module_sp)
+ return 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 = minidump_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) {
+ LLDB_LOG(log, "Partial uuid match for {0}.", name);
+ return module_sp;
+ }
+
+ // Breakpad generates minindump files, and if there is no GNU build
+ // ID in the binary, it will calculate a UUID by hashing first 4096
+ // bytes of the .text section and using that as the UUID for a module
+ // in the minidump. Facebook uses a modified breakpad client that
+ // uses a slightly modified this hash to avoid collisions. Check for
+ // UUIDs from the minindump that match these cases and accept the
+ // module we find if they do match.
+ std::vector<uint8_t> breakpad_uuid;
+ std::vector<uint8_t> facebook_uuid;
+ HashElfTextSection(module_sp, breakpad_uuid, facebook_uuid);
+ if (dmp_bytes == llvm::ArrayRef<uint8_t>(breakpad_uuid)) {
+ LLDB_LOG(log, "Breakpad .text hash match for {0}.", name);
+ return module_sp;
+ }
+ if (dmp_bytes == llvm::ArrayRef<uint8_t>(facebook_uuid)) {
+ LLDB_LOG(log, "Facebook .text hash match for {0}.", name);
+ return module_sp;
+ }
+ // The UUID wasn't a partial match and didn't match the .text hash
+ // so remove the module from the target, we will need to create a
+ // placeholder object file.
+ GetTarget().GetImages().Remove(module_sp);
+ module_sp.reset();
+ return module_sp;
+}
+
void ProcessMinidump::ReadModuleList() {
std::vector<const minidump::Module *> filtered_modules =
m_minidump_parser->GetFilteredModuleList();
@@ -473,30 +561,21 @@ void ProcessMinidump::ReadModuleList() {
// add the module to the target if it finds one.
lldb::ModuleSP module_sp = GetTarget().GetOrCreateModule(module_spec,
true /* notify */, &error);
- 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().GetOrCreateModule(basename_module_spec,
- true /* notify */, &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) {
+ LLDB_LOG(log, "Full uuid match for {0}.", name);
+ } else {
+ // We couldn't find a module with an exactly-matching UUID. Sometimes
+ // a minidump UUID is only a partial match or is a hash. So try again
+ // without specifying the UUID, then again without specifying the
+ // directory if that fails. This will allow us to find modules with
+ // partial matches or hash UUIDs in user-provided sysroots or search
+ // directories (target.exec-search-paths).
+ ModuleSpec partial_module_spec = module_spec;
+ partial_module_spec.GetUUID().Clear();
+ module_sp = GetOrCreateModule(uuid, name, partial_module_spec);
+ if (!module_sp) {
+ partial_module_spec.GetFileSpec().GetDirectory().Clear();
+ module_sp = GetOrCreateModule(uuid, name, partial_module_spec);
}
}
if (module_sp) {