[lldb/ObjCRuntime] Implement support for small method lists

On macOS 11 (and other aligned Apple OSs), the Objective-C runtime
has a new optimization which saves memory by making the method
lists smaller.
This patch adds support for this new method list encoding (while
also keeping backward compatibility). This is implicitely covered
by some existing Objective-C tests.
This commit is contained in:
Fred Riss 2020-02-26 14:57:50 -08:00
parent e1a31f52cd
commit 61d22ef236
2 changed files with 44 additions and 18 deletions

View File

@ -241,15 +241,19 @@ bool ClassDescriptorV2::method_list_t::Read(Process *process,
lldb::offset_t cursor = 0;
m_entsize = extractor.GetU32_unchecked(&cursor) & ~(uint32_t)3;
uint32_t entsize = extractor.GetU32_unchecked(&cursor);
m_is_small = (entsize & 0x80000000) != 0;
m_entsize = entsize & 0xfffc;
m_count = extractor.GetU32_unchecked(&cursor);
m_first_ptr = addr + cursor;
return true;
}
bool ClassDescriptorV2::method_t::Read(Process *process, lldb::addr_t addr) {
size_t size = GetSize(process);
bool ClassDescriptorV2::method_t::Read(Process *process, lldb::addr_t addr,
bool is_small) {
size_t ptr_size = process->GetAddressByteSize();
size_t size = GetSize(process, is_small);
DataBufferHeap buffer(size, '\0');
Status error;
@ -260,13 +264,27 @@ bool ClassDescriptorV2::method_t::Read(Process *process, lldb::addr_t addr) {
}
DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
process->GetAddressByteSize());
ptr_size);
lldb::offset_t cursor = 0;
m_name_ptr = extractor.GetAddress_unchecked(&cursor);
m_types_ptr = extractor.GetAddress_unchecked(&cursor);
m_imp_ptr = extractor.GetAddress_unchecked(&cursor);
if (is_small) {
uint32_t nameref_offset = extractor.GetU32_unchecked(&cursor);
uint32_t types_offset = extractor.GetU32_unchecked(&cursor);
uint32_t imp_offset = extractor.GetU32_unchecked(&cursor);
// The SEL offset points to a SELRef. We need to dereference twice.
lldb::addr_t selref_addr = addr + nameref_offset;
m_name_ptr =
process->ReadUnsignedIntegerFromMemory(selref_addr, ptr_size, 0, error);
if (!error.Success())
return false;
m_types_ptr = addr + 4 + types_offset;
m_imp_ptr = addr + 8 + imp_offset;
} else {
m_name_ptr = extractor.GetAddress_unchecked(&cursor);
m_types_ptr = extractor.GetAddress_unchecked(&cursor);
m_imp_ptr = extractor.GetAddress_unchecked(&cursor);
}
process->ReadCStringFromMemory(m_name_ptr, m_name, error);
if (error.Fail()) {
@ -361,15 +379,18 @@ bool ClassDescriptorV2::Describe(
if (!base_method_list->Read(process, class_ro->m_baseMethods_ptr))
return false;
if (base_method_list->m_entsize != method_t::GetSize(process))
bool is_small = base_method_list->m_is_small;
if (base_method_list->m_entsize != method_t::GetSize(process, is_small))
return false;
std::unique_ptr<method_t> method;
method = std::make_unique<method_t>();
for (uint32_t i = 0, e = base_method_list->m_count; i < e; ++i) {
method->Read(process, base_method_list->m_first_ptr +
(i * base_method_list->m_entsize));
method->Read(process,
base_method_list->m_first_ptr +
(i * base_method_list->m_entsize),
is_small);
if (instance_method_func(method->m_name.c_str(), method->m_types.c_str()))
break;

View File

@ -133,7 +133,8 @@ private:
};
struct method_list_t {
uint32_t m_entsize;
uint16_t m_entsize;
bool m_is_small;
uint32_t m_count;
lldb::addr_t m_first_ptr;
@ -148,15 +149,19 @@ private:
std::string m_name;
std::string m_types;
static size_t GetSize(Process *process) {
size_t ptr_size = process->GetAddressByteSize();
static size_t GetSize(Process *process, bool is_small) {
size_t field_size;
if (is_small)
field_size = 4; // uint32_t relative indirect fields
else
field_size = process->GetAddressByteSize();
return ptr_size // SEL name;
+ ptr_size // const char *types;
+ ptr_size; // IMP imp;
return field_size // SEL name;
+ field_size // const char *types;
+ field_size; // IMP imp;
}
bool Read(Process *process, lldb::addr_t addr);
bool Read(Process *process, lldb::addr_t addr, bool);
};
struct ivar_list_t {