[lldb] Support new objective-c hash table layout

Update LLDB for thew new Objective-C hash table layout in the dyld
shared cache found in macOS Monterey.

rdar://72863911
This commit is contained in:
Jonas Devlieghere 2021-02-09 19:52:11 -08:00
parent c7dee6aefd
commit fc71a5c6e8
4 changed files with 252 additions and 18 deletions

View File

@ -252,6 +252,7 @@ bool ClassDescriptorV2::method_list_t::Read(Process *process,
}
bool ClassDescriptorV2::method_t::Read(Process *process, lldb::addr_t addr,
lldb::addr_t relative_selector_base_addr,
bool is_small, bool has_direct_sel) {
size_t ptr_size = process->GetAddressByteSize();
size_t size = GetSize(process, is_small);
@ -281,6 +282,8 @@ bool ClassDescriptorV2::method_t::Read(Process *process, lldb::addr_t addr,
0, error);
if (!error.Success())
return false;
} else if (relative_selector_base_addr != LLDB_INVALID_ADDRESS) {
m_name_ptr = relative_selector_base_addr + nameref_offset;
}
m_types_ptr = addr + 4 + types_offset;
m_imp_ptr = addr + 8 + imp_offset;
@ -389,14 +392,14 @@ bool ClassDescriptorV2::Describe(
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>();
std::unique_ptr<method_t> method = std::make_unique<method_t>();
lldb::addr_t relative_selector_base_addr =
m_runtime.GetRelativeSelectorBaseAddr();
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),
is_small, has_direct_selector);
relative_selector_base_addr, is_small, has_direct_selector);
if (instance_method_func(method->m_name.c_str(), method->m_types.c_str()))
break;

View File

@ -166,7 +166,8 @@ private:
+ field_size; // IMP imp;
}
bool Read(Process *process, lldb::addr_t addr, bool, bool);
bool Read(Process *process, lldb::addr_t addr,
lldb::addr_t relative_method_lists_base_addr, bool, bool);
};
struct ivar_list_t {

View File

@ -263,6 +263,12 @@ struct objc_classheader_t {
int32_t hiOffset;
};
struct objc_classheader_v16_t {
uint64_t isDuplicate : 1,
objectCacheOffset : 47, // Offset from the shared cache base
dylibObjCIndex : 16;
};
struct objc_clsopt_t {
uint32_t capacity;
uint32_t occupied;
@ -280,6 +286,22 @@ struct objc_clsopt_t {
// objc_classheader_t duplicateOffsets[duplicateCount];
};
struct objc_clsopt_v16_t {
uint32_t version;
uint32_t capacity;
uint32_t occupied;
uint32_t shift;
uint32_t mask;
uint64_t salt;
uint32_t scramble[256];
uint8_t tab[0]; // tab[mask+1]
// uint8_t checkbytes[capacity];
// int32_t offset[capacity];
// objc_classheader_t clsOffsets[capacity];
// uint32_t duplicateCount;
// objc_classheader_t duplicateOffsets[duplicateCount];
};
struct objc_opt_t {
uint32_t version;
int32_t selopt_offset;
@ -295,6 +317,20 @@ struct objc_opt_v14_t {
int32_t clsopt_offset;
};
struct objc_opt_v16_t {
uint32_t version;
uint32_t flags;
int32_t selopt_offset;
int32_t headeropt_ro_offset;
int32_t unused_clsopt_offset;
int32_t unused_protocolopt_offset;
int32_t headeropt_rw_offset;
int32_t unused_protocolopt2_offset;
int32_t largeSharedCachesClassOffset;
int32_t largeSharedCachesProtocolOffset;
uint64_t relativeMethodSelectorBaseAddressCacheOffset;
};
struct ClassInfo
{
Class isa;
@ -303,19 +339,33 @@ struct ClassInfo
uint32_t
__lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr,
void *shared_cache_base_ptr,
void *class_infos_ptr,
uint64_t *relative_selector_offset,
uint32_t class_infos_byte_size,
uint32_t should_log)
{
*relative_selector_offset = 0;
uint32_t idx = 0;
DEBUG_PRINTF ("objc_opt_ro_ptr = %p\n", objc_opt_ro_ptr);
DEBUG_PRINTF ("shared_cache_base_ptr = %p\n", shared_cache_base_ptr);
DEBUG_PRINTF ("class_infos_ptr = %p\n", class_infos_ptr);
DEBUG_PRINTF ("class_infos_byte_size = %u (%llu class infos)\n", class_infos_byte_size, (uint64_t)(class_infos_byte_size/sizeof(ClassInfo)));
if (objc_opt_ro_ptr)
{
const objc_opt_t *objc_opt = (objc_opt_t *)objc_opt_ro_ptr;
const objc_opt_v14_t* objc_opt_v14 = (objc_opt_v14_t*)objc_opt_ro_ptr;
if (objc_opt->version >= 14)
const objc_opt_v16_t* objc_opt_v16 = (objc_opt_v16_t*)objc_opt_ro_ptr;
if (objc_opt->version >= 16)
{
*relative_selector_offset = objc_opt_v16->relativeMethodSelectorBaseAddressCacheOffset;
DEBUG_PRINTF ("objc_opt->version = %u\n", objc_opt_v16->version);
DEBUG_PRINTF ("objc_opt->flags = %u\n", objc_opt_v16->flags);
DEBUG_PRINTF ("objc_opt->selopt_offset = %d\n", objc_opt_v16->selopt_offset);
DEBUG_PRINTF ("objc_opt->headeropt_ro_offset = %d\n", objc_opt_v16->headeropt_ro_offset);
DEBUG_PRINTF ("objc_opt->relativeMethodSelectorBaseAddressCacheOffset = %d\n", *relative_selector_offset);
}
else if (objc_opt->version >= 14)
{
DEBUG_PRINTF ("objc_opt->version = %u\n", objc_opt_v14->version);
DEBUG_PRINTF ("objc_opt->flags = %u\n", objc_opt_v14->flags);
@ -330,7 +380,123 @@ __lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr,
DEBUG_PRINTF ("objc_opt->headeropt_offset = %d\n", objc_opt->headeropt_offset);
DEBUG_PRINTF ("objc_opt->clsopt_offset = %d\n", objc_opt->clsopt_offset);
}
if (objc_opt->version == 12 || objc_opt->version == 13 || objc_opt->version == 14 || objc_opt->version == 15)
if (objc_opt->version == 16)
{
const objc_clsopt_v16_t* clsopt = (const objc_clsopt_v16_t*)((uint8_t *)objc_opt + objc_opt_v16->largeSharedCachesClassOffset);
const size_t max_class_infos = class_infos_byte_size/sizeof(ClassInfo);
DEBUG_PRINTF("max_class_infos = %llu\n", (uint64_t)max_class_infos);
ClassInfo *class_infos = (ClassInfo *)class_infos_ptr;
const uint8_t *checkbytes = &clsopt->tab[clsopt->mask+1];
const int32_t *offsets = (const int32_t *)(checkbytes + clsopt->capacity);
const objc_classheader_v16_t *classOffsets = (const objc_classheader_v16_t *)(offsets + clsopt->capacity);
DEBUG_PRINTF ("clsopt->capacity = %u\n", clsopt->capacity);
DEBUG_PRINTF ("clsopt->mask = 0x%8.8x\n", clsopt->mask);
DEBUG_PRINTF ("classOffsets = %p\n", classOffsets);
for (uint32_t i=0; i<clsopt->capacity; ++i)
{
const uint64_t objectCacheOffset = classOffsets[i].objectCacheOffset;
DEBUG_PRINTF("objectCacheOffset[%u] = %u\n", i, objectCacheOffset);
if (classOffsets[i].isDuplicate) {
DEBUG_PRINTF("isDuplicate = true\n");
continue; // duplicate
}
if (objectCacheOffset == 0) {
DEBUG_PRINTF("objectCacheOffset == invalidEntryOffset\n");
continue; // invalid offset
}
if (class_infos && idx < max_class_infos)
{
class_infos[idx].isa = (Class)((uint8_t *)shared_cache_base_ptr + objectCacheOffset);
// Lookup the class name.
const char *name = class_name_lookup_func(class_infos[idx].isa);
DEBUG_PRINTF("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, name);
// Hash the class name so we don't have to read it.
const char *s = name;
uint32_t h = 5381;
for (unsigned char c = *s; c; c = *++s)
{
// class_getName demangles swift names and the hash must
// be calculated on the mangled name. hash==0 means lldb
// will fetch the mangled name and compute the hash in
// ParseClassInfoArray.
if (c == '.')
{
h = 0;
break;
}
h = ((h << 5) + h) + c;
}
class_infos[idx].hash = h;
}
else
{
DEBUG_PRINTF("not(class_infos && idx < max_class_infos)\n");
}
++idx;
}
const uint32_t *duplicate_count_ptr = (uint32_t *)&classOffsets[clsopt->capacity];
const uint32_t duplicate_count = *duplicate_count_ptr;
const objc_classheader_v16_t *duplicateClassOffsets = (const objc_classheader_v16_t *)(&duplicate_count_ptr[1]);
DEBUG_PRINTF ("duplicate_count = %u\n", duplicate_count);
DEBUG_PRINTF ("duplicateClassOffsets = %p\n", duplicateClassOffsets);
for (uint32_t i=0; i<duplicate_count; ++i)
{
const uint64_t objectCacheOffset = classOffsets[i].objectCacheOffset;
DEBUG_PRINTF("objectCacheOffset[%u] = %u\n", i, objectCacheOffset);
if (classOffsets[i].isDuplicate) {
DEBUG_PRINTF("isDuplicate = true\n");
continue; // duplicate
}
if (objectCacheOffset == 0) {
DEBUG_PRINTF("objectCacheOffset == invalidEntryOffset\n");
continue; // invalid offset
}
if (class_infos && idx < max_class_infos)
{
class_infos[idx].isa = (Class)((uint8_t *)shared_cache_base_ptr + objectCacheOffset);
// Lookup the class name.
const char *name = class_name_lookup_func(class_infos[idx].isa);
DEBUG_PRINTF("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, name);
// Hash the class name so we don't have to read it.
const char *s = name;
uint32_t h = 5381;
for (unsigned char c = *s; c; c = *++s)
{
// class_getName demangles swift names and the hash must
// be calculated on the mangled name. hash==0 means lldb
// will fetch the mangled name and compute the hash in
// ParseClassInfoArray.
if (c == '.')
{
h = 0;
break;
}
h = ((h << 5) + h) + c;
}
class_infos[idx].hash = h;
}
}
}
else if (objc_opt->version >= 12 && objc_opt->version <= 15)
{
const objc_clsopt_t* clsopt = NULL;
if (objc_opt->version >= 14)
@ -491,7 +657,8 @@ AppleObjCRuntimeV2::AppleObjCRuntimeV2(Process *process,
m_dynamic_class_info_extractor(*this),
m_shared_cache_class_info_extractor(*this), m_decl_vendor_up(),
m_tagged_pointer_obfuscator(LLDB_INVALID_ADDRESS),
m_isa_hash_table_ptr(LLDB_INVALID_ADDRESS), m_hash_signature(),
m_isa_hash_table_ptr(LLDB_INVALID_ADDRESS),
m_relative_selector_base(LLDB_INVALID_ADDRESS), m_hash_signature(),
m_has_object_getClass(false), m_has_objc_copyRealizedClassList(false),
m_loaded_objc_opt(false), m_non_pointer_isa_cache_up(),
m_tagged_pointer_vendor_up(
@ -1515,6 +1682,9 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::
ast->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32);
CompilerType clang_void_pointer_type =
ast->GetBasicType(eBasicTypeVoid).GetPointerType();
CompilerType clang_uint64_t_pointer_type =
ast->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 64)
.GetPointerType();
// Next make the function caller for our implementation utility function.
ValueList arguments;
@ -1523,6 +1693,11 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::
value.SetCompilerType(clang_void_pointer_type);
arguments.PushValue(value);
arguments.PushValue(value);
arguments.PushValue(value);
value.SetValueType(Value::ValueType::Scalar);
value.SetCompilerType(clang_uint64_t_pointer_type);
arguments.PushValue(value);
value.SetValueType(Value::ValueType::Scalar);
value.SetCompilerType(clang_uint32_t_type);
@ -1816,8 +1991,11 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() {
uint32_t num_class_infos = 0;
const lldb::addr_t objc_opt_ptr = m_runtime.GetSharedCacheReadOnlyAddress();
const lldb::addr_t shared_cache_base_addr =
m_runtime.GetSharedCacheBaseAddress();
if (objc_opt_ptr == LLDB_INVALID_ADDRESS)
if (objc_opt_ptr == LLDB_INVALID_ADDRESS ||
shared_cache_base_addr == LLDB_INVALID_ADDRESS)
return DescriptorMapUpdateResult::Fail();
const uint32_t num_classes = 128 * 1024;
@ -1840,6 +2018,10 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() {
const uint32_t class_infos_byte_size = num_classes * class_info_byte_size;
lldb::addr_t class_infos_addr = process->AllocateMemory(
class_infos_byte_size, ePermissionsReadable | ePermissionsWritable, err);
const uint32_t relative_selector_offset_addr_size = 64;
lldb::addr_t relative_selector_offset_addr =
process->AllocateMemory(relative_selector_offset_addr_size,
ePermissionsReadable | ePermissionsWritable, err);
if (class_infos_addr == LLDB_INVALID_ADDRESS) {
LLDB_LOGF(log,
@ -1853,14 +2035,16 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() {
// Fill in our function argument values
arguments.GetValueAtIndex(0)->GetScalar() = objc_opt_ptr;
arguments.GetValueAtIndex(1)->GetScalar() = class_infos_addr;
arguments.GetValueAtIndex(2)->GetScalar() = class_infos_byte_size;
arguments.GetValueAtIndex(1)->GetScalar() = shared_cache_base_addr;
arguments.GetValueAtIndex(2)->GetScalar() = class_infos_addr;
arguments.GetValueAtIndex(3)->GetScalar() = relative_selector_offset_addr;
arguments.GetValueAtIndex(4)->GetScalar() = class_infos_byte_size;
// Only dump the runtime classes from the expression evaluation if the log is
// verbose:
Log *type_log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_TYPES);
bool dump_log = type_log && type_log->GetVerbose();
arguments.GetValueAtIndex(3)->GetScalar() = dump_log ? 1 : 0;
arguments.GetValueAtIndex(5)->GetScalar() = dump_log ? 1 : 0;
bool success = false;
@ -1907,13 +2091,35 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() {
success = true;
}
// Read the relative selector offset.
DataBufferHeap relative_selector_offset_buffer(64, 0);
if (process->ReadMemory(relative_selector_offset_addr,
relative_selector_offset_buffer.GetBytes(),
relative_selector_offset_buffer.GetByteSize(),
err) ==
relative_selector_offset_buffer.GetByteSize()) {
DataExtractor relative_selector_offset_data(
relative_selector_offset_buffer.GetBytes(),
relative_selector_offset_buffer.GetByteSize(),
process->GetByteOrder(), addr_size);
lldb::offset_t offset = 0;
uint64_t relative_selector_offset =
relative_selector_offset_data.GetU64(&offset);
if (relative_selector_offset > 0) {
// The offset is relative to the objc_opt struct.
m_runtime.SetRelativeSelectorBaseAddr(objc_opt_ptr +
relative_selector_offset);
}
}
// Read the ClassInfo structures
DataBufferHeap buffer(num_class_infos * class_info_byte_size, 0);
if (process->ReadMemory(class_infos_addr, buffer.GetBytes(),
buffer.GetByteSize(),
err) == buffer.GetByteSize()) {
DataExtractor class_infos_data(buffer.GetBytes(),
buffer.GetByteSize(),
DataBufferHeap class_infos_buffer(
num_class_infos * class_info_byte_size, 0);
if (process->ReadMemory(class_infos_addr, class_infos_buffer.GetBytes(),
class_infos_buffer.GetByteSize(),
err) == class_infos_buffer.GetByteSize()) {
DataExtractor class_infos_data(class_infos_buffer.GetBytes(),
class_infos_buffer.GetByteSize(),
process->GetByteOrder(), addr_size);
m_runtime.ParseClassInfoArray(class_infos_data, num_class_infos);
@ -1973,6 +2179,19 @@ lldb::addr_t AppleObjCRuntimeV2::GetSharedCacheReadOnlyAddress() {
return LLDB_INVALID_ADDRESS;
}
lldb::addr_t AppleObjCRuntimeV2::GetSharedCacheBaseAddress() {
StructuredData::ObjectSP info = m_process->GetSharedCacheInfo();
if (!info)
return LLDB_INVALID_ADDRESS;
StructuredData::Dictionary *info_dict = info->GetAsDictionary();
if (!info_dict)
return LLDB_INVALID_ADDRESS;
return info_dict->GetValueForKey("shared_cache_base_address")
->GetIntegerValue(LLDB_INVALID_ADDRESS);
}
void AppleObjCRuntimeV2::UpdateISAToDescriptorMapIfNeeded() {
LLDB_SCOPED_TIMER();

View File

@ -85,6 +85,15 @@ public:
lldb::addr_t GetTaggedPointerObfuscator();
/// Returns the base address for relative method list selector strings.
lldb::addr_t GetRelativeSelectorBaseAddr() {
return m_relative_selector_base;
}
void SetRelativeSelectorBaseAddr(lldb::addr_t relative_selector_base) {
m_relative_selector_base = relative_selector_base;
}
void GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true,
lldb::addr_t &cf_false) override;
@ -394,6 +403,7 @@ private:
void WarnIfNoClassesCached(SharedCacheWarningReason reason);
lldb::addr_t GetSharedCacheReadOnlyAddress();
lldb::addr_t GetSharedCacheBaseAddress();
bool GetCFBooleanValuesIfNeeded();
@ -416,6 +426,7 @@ private:
std::unique_ptr<DeclVendor> m_decl_vendor_up;
lldb::addr_t m_tagged_pointer_obfuscator;
lldb::addr_t m_isa_hash_table_ptr;
lldb::addr_t m_relative_selector_base;
HashTableSignature m_hash_signature;
bool m_has_object_getClass;
bool m_has_objc_copyRealizedClassList;