@@ -370,6 +370,155 @@ bool ClassDescriptorV2::ivar_t::Read(Process *process, lldb::addr_t addr) {
370370 return !error.Fail ();
371371}
372372
373+ bool ClassDescriptorV2::relative_list_entry_t::Read (Process *process,
374+ lldb::addr_t addr) {
375+ Log *log = GetLog (LLDBLog::Types);
376+ size_t size = sizeof (uint64_t ); // m_image_index : 16
377+ // m_list_offset : 48
378+
379+ DataBufferHeap buffer (size, ' \0 ' );
380+ Status error;
381+
382+ process->ReadMemory (addr, buffer.GetBytes (), size, error);
383+ // FIXME: Propagate this error up
384+ if (error.Fail ()) {
385+ LLDB_LOG (log, " Failed to read relative_list_entry_t at address {0:x}" ,
386+ addr);
387+ return false ;
388+ }
389+
390+ DataExtractor extractor (buffer.GetBytes (), size, process->GetByteOrder (),
391+ process->GetAddressByteSize ());
392+ lldb::offset_t cursor = 0 ;
393+ uint64_t raw_entry = extractor.GetU64_unchecked (&cursor);
394+ m_image_index = raw_entry & 0xFFFF ;
395+ m_list_offset = (int64_t )(raw_entry >> 16 );
396+ return true ;
397+ }
398+
399+ bool ClassDescriptorV2::relative_list_list_t::Read (Process *process,
400+ lldb::addr_t addr) {
401+ Log *log = GetLog (LLDBLog::Types);
402+ size_t size = sizeof (uint32_t ) // m_entsize
403+ + sizeof (uint32_t ); // m_count
404+
405+ DataBufferHeap buffer (size, ' \0 ' );
406+ Status error;
407+
408+ // FIXME: Propagate this error up
409+ process->ReadMemory (addr, buffer.GetBytes (), size, error);
410+ if (error.Fail ()) {
411+ LLDB_LOG (log, " Failed to read relative_list_list_t at address 0x" PRIx64,
412+ addr);
413+ return false ;
414+ }
415+
416+ DataExtractor extractor (buffer.GetBytes (), size, process->GetByteOrder (),
417+ process->GetAddressByteSize ());
418+ lldb::offset_t cursor = 0 ;
419+ m_entsize = extractor.GetU32_unchecked (&cursor);
420+ m_count = extractor.GetU32_unchecked (&cursor);
421+ m_first_ptr = addr + cursor;
422+ return true ;
423+ }
424+
425+ std::optional<ClassDescriptorV2::method_list_t >
426+ ClassDescriptorV2::GetMethodList (Process *process,
427+ lldb::addr_t method_list_ptr) const {
428+ Log *log = GetLog (LLDBLog::Types);
429+ ClassDescriptorV2::method_list_t method_list;
430+ if (!method_list.Read (process, method_list_ptr))
431+ return std::nullopt ;
432+
433+ const size_t method_size = method_t::GetSize (process, method_list.m_is_small );
434+ if (method_list.m_entsize != method_size) {
435+ LLDB_LOG (log,
436+ " method_list_t at address 0x" PRIx64 " has an entsize of " PRIu16
437+ " but method size should be " PRIu64,
438+ method_list_ptr, method_list.m_entsize , method_size);
439+ return std::nullopt ;
440+ }
441+
442+ return method_list;
443+ }
444+
445+ bool ClassDescriptorV2::ProcessMethodList (
446+ std::function<bool (const char *, const char *)> const &instance_method_func,
447+ ClassDescriptorV2::method_list_t &method_list) const {
448+ lldb_private::Process *process = m_runtime.GetProcess ();
449+ auto method = std::make_unique<method_t >();
450+ lldb::addr_t relative_selector_base_addr =
451+ m_runtime.GetRelativeSelectorBaseAddr ();
452+ for (uint32_t i = 0 , e = method_list.m_count ; i < e; ++i) {
453+ method->Read (process, method_list.m_first_ptr + (i * method_list.m_entsize ),
454+ relative_selector_base_addr, method_list.m_is_small ,
455+ method_list.m_has_direct_selector );
456+ if (instance_method_func (method->m_name .c_str (), method->m_types .c_str ()))
457+ break ;
458+ }
459+ return true ;
460+ }
461+
462+ // The relevant data structures:
463+ // - relative_list_list_t
464+ // - uint32_t count
465+ // - uint32_t entsize
466+ // - Followed by <count> number of relative_list_entry_t of size <entsize>
467+ //
468+ // - relative_list_entry_t
469+ // - uint64_t image_index : 16
470+ // - int64_t list_offset : 48
471+ // - Note: The above 2 fit into 8 bytes always
472+ //
473+ // image_index corresponds to an image in the shared cache
474+ // list_offset is used to calculate the address of the method_list_t we want
475+ bool ClassDescriptorV2::ProcessRelativeMethodLists (
476+ std::function<bool (const char *, const char *)> const &instance_method_func,
477+ lldb::addr_t relative_method_list_ptr) const {
478+ lldb_private::Process *process = m_runtime.GetProcess ();
479+ auto relative_method_lists = std::make_unique<relative_list_list_t >();
480+
481+ // 1. Process the count and entsize of the relative_list_list_t
482+ if (!relative_method_lists->Read (process, relative_method_list_ptr))
483+ return false ;
484+
485+ auto entry = std::make_unique<relative_list_entry_t >();
486+ for (uint32_t i = 0 ; i < relative_method_lists->m_count ; i++) {
487+ // 2. Extract the image index and the list offset from the
488+ // relative_list_entry_t
489+ const lldb::addr_t entry_addr = relative_method_lists->m_first_ptr +
490+ (i * relative_method_lists->m_entsize );
491+ if (!entry->Read (process, entry_addr))
492+ return false ;
493+
494+ // 3. Calculate the pointer to the method_list_t from the
495+ // relative_list_entry_t
496+ const lldb::addr_t method_list_addr = entry_addr + entry->m_list_offset ;
497+
498+ // 4. Get the method_list_t from the pointer
499+ std::optional<method_list_t > method_list =
500+ GetMethodList (process, method_list_addr);
501+ if (!method_list)
502+ return false ;
503+
504+ // 5. Cache the result so we don't need to reconstruct it later.
505+ m_image_to_method_lists[entry->m_image_index ].emplace_back (*method_list);
506+
507+ // 6. If the relevant image is loaded, add the methods to the Decl
508+ if (!m_runtime.IsSharedCacheImageLoaded (entry->m_image_index ))
509+ continue ;
510+
511+ if (!ProcessMethodList (instance_method_func, *method_list))
512+ return false ;
513+ }
514+
515+ // We need to keep track of the last time we updated so we can re-update the
516+ // type information in the future
517+ m_last_version_updated = m_runtime.GetSharedCacheImageHeaderVersion ();
518+
519+ return true ;
520+ }
521+
373522bool ClassDescriptorV2::Describe (
374523 std::function<void (ObjCLanguageRuntime::ObjCISA)> const &superclass_func,
375524 std::function<bool(const char *, const char *)> const &instance_method_func,
@@ -393,29 +542,18 @@ bool ClassDescriptorV2::Describe(
393542 superclass_func (objc_class->m_superclass );
394543
395544 if (instance_method_func) {
396- std::unique_ptr<method_list_t > base_method_list;
397-
398- base_method_list = std::make_unique<method_list_t >();
399- if (!base_method_list->Read (process, class_ro->m_baseMethods_ptr ))
400- return false ;
401-
402- bool is_small = base_method_list->m_is_small ;
403- bool has_direct_selector = base_method_list->m_has_direct_selector ;
404-
405- if (base_method_list->m_entsize != method_t::GetSize (process, is_small))
406- return false ;
407-
408- std::unique_ptr<method_t > method = std::make_unique<method_t >();
409- lldb::addr_t relative_selector_base_addr =
410- m_runtime.GetRelativeSelectorBaseAddr ();
411- for (uint32_t i = 0 , e = base_method_list->m_count ; i < e; ++i) {
412- method->Read (process,
413- base_method_list->m_first_ptr +
414- (i * base_method_list->m_entsize ),
415- relative_selector_base_addr, is_small, has_direct_selector);
416-
417- if (instance_method_func (method->m_name .c_str (), method->m_types .c_str ()))
418- break ;
545+ // This is a relative list of lists
546+ if (class_ro->m_baseMethods_ptr & 1 ) {
547+ if (!ProcessRelativeMethodLists (instance_method_func,
548+ class_ro->m_baseMethods_ptr ^ 1 ))
549+ return false ;
550+ } else {
551+ std::optional<method_list_t > base_method_list =
552+ GetMethodList (process, class_ro->m_baseMethods_ptr );
553+ if (!base_method_list)
554+ return false ;
555+ if (!ProcessMethodList (instance_method_func, *base_method_list))
556+ return false ;
419557 }
420558 }
421559
0 commit comments