@@ -121,6 +121,72 @@ class PlaceholderObjectFile : public ObjectFile {
121
121
lldb::addr_t m_base;
122
122
lldb::addr_t m_size;
123
123
};
124
+
125
+ // / Duplicate the HashElfTextSection() from the breakpad sources.
126
+ // /
127
+ // / Breakpad, a Google crash log reporting tool suite, creates minidump files
128
+ // / for many different architectures. When using Breakpad to create ELF
129
+ // / minidumps, it will check for a GNU build ID when creating a minidump file
130
+ // / and if one doesn't exist in the file, it will say the UUID of the file is a
131
+ // / checksum of up to the first 4096 bytes of the .text section. Facebook also
132
+ // / uses breakpad and modified this hash to avoid collisions so we can
133
+ // / calculate and check for this as well.
134
+ // /
135
+ // / The breakpad code might end up hashing up to 15 bytes that immediately
136
+ // / follow the .text section in the file, so this code must do exactly what it
137
+ // / does so we can get an exact match for the UUID.
138
+ // /
139
+ // / \param[in] module_sp The module to grab the .text section from.
140
+ // /
141
+ // / \param[in/out] breakpad_uuid A vector that will receive the calculated
142
+ // / breakpad .text hash.
143
+ // /
144
+ // / \param[in/out] facebook_uuid A vector that will receive the calculated
145
+ // / facebook .text hash.
146
+ // /
147
+ void HashElfTextSection (ModuleSP module_sp, std::vector<uint8_t > &breakpad_uuid,
148
+ std::vector<uint8_t > &facebook_uuid) {
149
+ SectionList *sect_list = module_sp->GetSectionList ();
150
+ if (sect_list == nullptr )
151
+ return ;
152
+ SectionSP sect_sp = sect_list->FindSectionByName (ConstString (" .text" ));
153
+ if (!sect_sp)
154
+ return ;
155
+ constexpr size_t kMDGUIDSize = 16 ;
156
+ constexpr size_t kBreakpadPageSize = 4096 ;
157
+ // The breakpad code has a bug where it might access beyond the end of a
158
+ // .text section by up to 15 bytes, so we must ensure we round up to the
159
+ // next kMDGUIDSize byte boundary.
160
+ DataExtractor data;
161
+ const size_t text_size = sect_sp->GetFileSize ();
162
+ const size_t read_size = std::min<size_t >(
163
+ llvm::alignTo (text_size, kMDGUIDSize ), kBreakpadPageSize );
164
+ sect_sp->GetObjectFile ()->GetData (sect_sp->GetFileOffset (), read_size, data);
165
+
166
+ breakpad_uuid.assign (kMDGUIDSize , 0 );
167
+ facebook_uuid.assign (kMDGUIDSize , 0 );
168
+
169
+ // The only difference between the breakpad hash and the facebook hash is the
170
+ // hashing of the text section size into the hash prior to hashing the .text
171
+ // contents.
172
+ for (size_t i = 0 ; i < kMDGUIDSize ; i++)
173
+ facebook_uuid[i] ^= text_size % 255 ;
174
+
175
+ // This code carefully duplicates how the hash was created in Breakpad
176
+ // sources, including the error where it might has an extra 15 bytes past the
177
+ // end of the .text section if the .text section is less than a page size in
178
+ // length.
179
+ const uint8_t *ptr = data.GetDataStart ();
180
+ const uint8_t *ptr_end = data.GetDataEnd ();
181
+ while (ptr < ptr_end) {
182
+ for (unsigned i = 0 ; i < kMDGUIDSize ; i++) {
183
+ breakpad_uuid[i] ^= ptr[i];
184
+ facebook_uuid[i] ^= ptr[i];
185
+ }
186
+ ptr += kMDGUIDSize ;
187
+ }
188
+ }
189
+
124
190
} // namespace
125
191
126
192
ConstString ProcessMinidump::GetPluginNameStatic () {
@@ -444,6 +510,53 @@ bool ProcessMinidump::UpdateThreadList(ThreadList &old_thread_list,
444
510
return new_thread_list.GetSize (false ) > 0 ;
445
511
}
446
512
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
+
447
560
void ProcessMinidump::ReadModuleList () {
448
561
std::vector<const minidump::Module *> filtered_modules =
449
562
m_minidump_parser->GetFilteredModuleList ();
@@ -473,30 +586,21 @@ void ProcessMinidump::ReadModuleList() {
473
586
// add the module to the target if it finds one.
474
587
lldb::ModuleSP module_sp = GetTarget ().GetOrCreateModule (module_spec,
475
588
true /* notify */ , &error);
476
- if (!module_sp) {
477
- // Try and find a module without specifying the UUID and only looking for
478
- // the file given a basename. We then will look for a partial UUID match
479
- // if we find any matches. This function will add the module to the
480
- // target if it finds one, so we need to remove the module from the target
481
- // if the UUID doesn't match during our manual UUID verification. This
482
- // allows the "target.exec-search-paths" setting to specify one or more
483
- // directories that contain executables that can be searched for matches.
484
- ModuleSpec basename_module_spec (module_spec);
485
- basename_module_spec.GetUUID ().Clear ();
486
- basename_module_spec.GetFileSpec ().GetDirectory ().Clear ();
487
- module_sp = GetTarget ().GetOrCreateModule (basename_module_spec,
488
- true /* notify */ , &error);
489
- if (module_sp) {
490
- // We consider the module to be a match if the minidump UUID is a
491
- // prefix of the actual UUID, or if either of the UUIDs are empty.
492
- const auto dmp_bytes = uuid.GetBytes ();
493
- const auto mod_bytes = module_sp->GetUUID ().GetBytes ();
494
- const bool match = dmp_bytes.empty () || mod_bytes.empty () ||
495
- mod_bytes.take_front (dmp_bytes.size ()) == dmp_bytes;
496
- if (!match) {
497
- GetTarget ().GetImages ().Remove (module_sp);
498
- module_sp.reset ();
499
- }
589
+ if (module_sp) {
590
+ 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);
500
604
}
501
605
}
502
606
if (module_sp) {
0 commit comments