in Chisel/Chisel/CHLObjcInstances.mm [77:136]
id CHLViableObjcInstance(vm_range_t range, const std::unordered_set<Class> &classSet)
{
// Check if this address points to an object embedded into Mach-O.
if (range.size == 0) {
return embeddedObjcInstance(range);
}
id obj = reinterpret_cast<id>(range.address);
// It's safe to call object_getClass on memory that isn't objc objects.
// Check that the returned Class points to an expected class.
Class cls = object_getClass(obj);
if (classSet.find(cls) == classSet.end()) {
return nil;
}
// Instance size is the byte count needed for an object's ivars, plus any padding.
// Allocation size is the byte count that malloc will actually allocate for instances of a Class.
const auto instanceSize = class_getInstanceSize(cls);
const auto expectedAllocationSize = malloc_good_size(instanceSize);
const auto extraSize = expectedAllocationSize - instanceSize;
const bool debug = getenv("FINDINSTANCES_DEBUG") != NULL;
if (range.size < expectedAllocationSize) {
if (debug) {
printf("%p has class %s but is too small\n", obj, class_getName(cls));
}
return nil;
}
if (range.size > expectedAllocationSize && isFixedSizeClass(cls)) {
// Range is too big and the class has no way of allocating larger instances.
if (debug) {
printf("%p has fixed size class %s but is too large\n", obj, class_getName(cls));
}
return nil;
}
if (range.size == expectedAllocationSize && extraSize) {
// ObjC instances are allocated with calloc, memory beyond the instance size should be zeros.
// Some classes have been known to store data in the extra space, ex NSConcreteValue.
static const unsigned char ZEROS[1024] = {0};
auto extra = object_getIndexedIvars(obj);
auto compareSize = std::min(extraSize, sizeof(ZEROS));
if (memcmp(extra, &ZEROS, compareSize) != 0) {
if (debug) {
printf("%p has class %s but has non-zero memory\n", obj, class_getName(cls));
}
return nil;
}
}
// Ignore deallocating objects.
if ([obj _isDeallocating]) {
return nil;
}
return obj;
}