Skip to content

Commit 4b5399b

Browse files
committed
[lldb] Adjust for changes in objc runtime
The Objective-C runtime and the shared cache has changed slightly. Given a class_ro_t, the baseMethods ivar is now a pointer union and may either be a method_list_t pointer or a pointer to a relative list of lists. The entries of this relative list of lists are indexes that refer to a specific image in the shared cache in addition to a pointer offset to find the accompanying method_list_t. We have to go over each of these entries, parse it, and then if the relevant image is loaded in the process, we add those methods to the relevant clang Decl. In order to determine if an image is loaded, the Objective-C runtime exposes a symbol that lets us determine if a particular image is loaded. We maintain a data structure SharedCacheImageHeaders to keep track of that information. There is a known issue where if an image is loaded after we create a Decl for a class, the Decl will not have the relevant methods from that image (i.e. for Categories). rdar://107957209 Differential Revision: https://reviews.llvm.org/D153597 (cherry picked from commit 28fb39f)
1 parent a062717 commit 4b5399b

File tree

5 files changed

+400
-26
lines changed

5 files changed

+400
-26
lines changed

lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp

Lines changed: 161 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
373522
bool 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

lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@ class ClassDescriptorV2 : public ObjCLanguageRuntime::ClassDescriptor {
146146
bool Read(Process *process, lldb::addr_t addr);
147147
};
148148

149+
std::optional<method_list_t>
150+
GetMethodList(Process *process, lldb::addr_t method_list_ptr) const;
151+
149152
struct method_t {
150153
lldb::addr_t m_name_ptr;
151154
lldb::addr_t m_types_ptr;
@@ -201,6 +204,21 @@ class ClassDescriptorV2 : public ObjCLanguageRuntime::ClassDescriptor {
201204
bool Read(Process *process, lldb::addr_t addr);
202205
};
203206

207+
struct relative_list_entry_t {
208+
uint16_t m_image_index;
209+
int64_t m_list_offset;
210+
211+
bool Read(Process *process, lldb::addr_t addr);
212+
};
213+
214+
struct relative_list_list_t {
215+
uint32_t m_entsize;
216+
uint32_t m_count;
217+
lldb::addr_t m_first_ptr;
218+
219+
bool Read(Process *process, lldb::addr_t addr);
220+
};
221+
204222
class iVarsStorage {
205223
public:
206224
iVarsStorage();
@@ -223,7 +241,8 @@ class ClassDescriptorV2 : public ObjCLanguageRuntime::ClassDescriptor {
223241
ClassDescriptorV2(AppleObjCRuntimeV2 &runtime,
224242
ObjCLanguageRuntime::ObjCISA isa, const char *name)
225243
: m_runtime(runtime), m_objc_class_ptr(isa), m_name(name),
226-
m_ivars_storage() {}
244+
m_ivars_storage(), m_image_to_method_lists(), m_last_version_updated() {
245+
}
227246

228247
bool Read_objc_class(Process *process,
229248
std::unique_ptr<objc_class_t> &objc_class) const;
@@ -232,13 +251,26 @@ class ClassDescriptorV2 : public ObjCLanguageRuntime::ClassDescriptor {
232251
std::unique_ptr<class_ro_t> &class_ro,
233252
std::unique_ptr<class_rw_t> &class_rw) const;
234253

254+
bool ProcessMethodList(std::function<bool(const char *, const char *)> const
255+
&instance_method_func,
256+
method_list_t &method_list) const;
257+
258+
bool ProcessRelativeMethodLists(
259+
std::function<bool(const char *, const char *)> const
260+
&instance_method_func,
261+
lldb::addr_t relative_method_list_ptr) const;
262+
235263
AppleObjCRuntimeV2
236264
&m_runtime; // The runtime, so we can read information lazily.
237265
lldb::addr_t m_objc_class_ptr; // The address of the objc_class_t. (I.e.,
238266
// objects of this class type have this as
239267
// their ISA)
240268
ConstString m_name; // May be NULL
241269
iVarsStorage m_ivars_storage;
270+
271+
mutable std::map<uint16_t, std::vector<method_list_t>>
272+
m_image_to_method_lists;
273+
mutable std::optional<uint64_t> m_last_version_updated;
242274
};
243275

244276
// tagged pointer descriptor

0 commit comments

Comments
 (0)