XNUMBER-C运行时伊萨指针是defined,如下所示:
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
uintptr_t bits;
private:
// Accessing the class requires custom ptrauth operations, so
// force clients to go through setClass/getClass by making this
// private.
Class cls;
public:
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
bool isDeallocating() {
return extra_rc == 0 && has_sidetable_rc == 0;
}
void setDeallocating() {
extra_rc = 0;
has_sidetable_rc = 0;
}
#endif
void setClass(Class cls, objc_object *obj);
Class getClass(bool authenticated);
Class getDecodedClass(bool authenticated);
};
位字段可以通过定义here来读取。
当我从磁盘中读取一个macho并转到_objc_classlist
部分并遵循objc_class
,即defined:
struct objc_class : objc_object {
objc_class(const objc_class&) = delete;
objc_class(objc_class&&) = delete;
void operator=(const objc_class&) = delete;
void operator=(objc_class&&) = delete;
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
...
而objc_object
是defined:
struct objc_object {
private:
isa_t isa;
public:
...
这意味着我应该能够将objc_class
的前8个字节解释为isa
的bits
字段,但是当我这样做并试图解释这些位时,我得到了随机和错误的信息,另一方面,如果我将前8个字节解释为指针,它会将我引导到磁盘上的另一个objc_class
示例,这通常是该类的元类。我想知道为什么isa
联合的定义来自于XNUMX-C运行时及其bits
字段。当我们示例化一个某种类型的对象时,当从磁盘上阅读时,它只是一个指向Meta类定义的指针,这是否只正确地解释为isa
与bits
的联合?
编辑:
我从文件中读取objc_class
结构体的方式是使用python:
ISA_MASK = 0x0000000ffffffff8
@dataclass
class Isa():
bits: ctypes.c_size_t
_cls: ctypes.c_size_t
def __init__(self, fp, addr):
fp.seek(addr)
self.bits = struct.unpack("<Q", fp.read(8))[0]
self._cls = self.bits
def nonpointer(self):
return self.bits & 1
def has_assoc(self):
return (self.bits >> 1) & 1
def has_cxx_dtor(self):
return (self.bits >> 2) & 1
def shiftcls(self):
return (self.bits >> 3) & 0x7ffffffff
def magic(self):
return (self.bits >> 36) & 0x3f
def weakly_referenced(self):
return (self.bits >> 42) & 1
def unused(self):
return (self.bits >> 43) & 1
def has_sidetable_rc(self):
return (self.bits >> 44) & 1
def extra_rc(self):
return (self.bits >> 45) & 0x7ffff
def get_class(self):
clsbits = self.bits
clsbits &= ISA_MASK
return clsbits
@dataclass
class ObjcObject:
isa: Isa
_addr: ctypes.c_size_t
def __init__(self, fp, addr, isa_class, external_block_addr):
self.isa = None
self._addr = addr
fp.seek(addr)
isa_addr = struct.unpack("<Q", fp.read(8))[0]
if isa_addr != 0 and isa_addr < external_block_addr:
self.isa = Isa(fp, isa_addr, external_block_addr)
@dataclass
class ObjcClass(ObjcObject):
super_class: ObjcClass
cache: Cache
class_ro: ClassRo
def __init__(self, fp, addr, external_block_addr):
super().__init__(fp, addr, ObjcClass, external_block_addr)
...
...
例如,我有一个类,让我们称之为A
,在处理地址0x0025eed0
上的链接修复后,我有它的符号_OBJC_CLASS_$_A
和该地址中定义的objc_class
。
结构的前8个字节是伊萨,正如我们通过查看运行时的源代码所建立的那样。跟随它作为一个指针,而不是把它当作isa_t
联合体,我得到了另一个objc_class
结构体,它是这个类的元类,符号_OBJC_METACLASS_$_A
。
现在,如果不将objc_class
结构体的前8个字节视为指向元类的指针,而是尝试将它们解释为isa_t
联合体的位,就像我在提供的代码中所做的那样,例如,使用has_cxx_dtor
方法,我得到False
,这是不正确的,因为我可以清楚地在method_list_t
结构上找到这个方法。class_ro
,所以它与我解析的不匹配,因此isa_t
联合似乎与磁盘上类的实际数据无关。
请注意,从isa_t
的位提取数据的方法是查看isa.h
的源代码,并假设我在没有ptr auth的情况下读取ARM 64 macho,而不是从模拟器中读取。
1条答案
按热度按时间a14dhokn1#
在深入研究了运行时之后,似乎非指针isas是一个仅在运行时的概念,并且所有磁盘上的isas将始终是常规指针。
对象文件中Obj-C类的加载过程:
dyld
调用_objc_map_images
(objc-internal.h
/objc-runtime-new.mm
),传入对象头以从中读取和加载类_objc_map_images
在调用map_images
(objc-private.h
/objc-runtime-new.mm
)之前进行了一些设置map_images
获取运行时锁,然后调用map_images_nolock
(objc-private.h
/objc-os.mm
)map_images_nolock
迭代mach头,搜索Obj-C信息并执行一些验证。它将所有包含Obj-C类的头文件传递给_read_images
(objc-private.h
/objc-runtime-new.mm
)_read_images
是我们真正感兴趣的部分。它首先为运行时目标建立对非指针isas的支持,并建立一些表来存储类信息。在阅读并修复选择器之后,它开始阅读类信息(OBJC_RUNTIME_DISCOVER_CLASSES_START()
)classlist
,接收指向图像中每个类的直接指针readClass
(objc-runtime-new.mm
),这会解析损坏的类名、Swift类等--但在一天结束时,读取的classref_t
(指向dyld类的原始指针)要么被转换为Class
(类对象),要么被分配的Class
示例替换那么,非指针在哪里起作用呢?仅当在运行时设置对象的类时:
1.当你通过
objc_constructInstance
或class_createInstance
(runtime.h
)创建一个对象,或者通过object_setClass
设置一个对象的类时,这个对象被调用了objc_object::initInstanceIsa
或objc_object::initIsa
(objc-object.h
)(而initInstanceIsa
只是通过调用initIsa
)objc_object::initIsa
有两个实现(一个用于SUPPORT_NONPOINTER_ISA
,另一个用于不支持的),但都调用了isa_t::setClass
(objc-private.h
/objc-object.h
)isa_t::setClass
也有两种实现--当SUPPORT_NONPOINTER_ISA
为true时,该实现在伊萨值本身中设置适当的位,必要时设置shiftcls
;当SUPPORT_NONPOINTER_ISA
为false时,它直接设置类(Or如果你愿意,可以反过来说:
isa_t::setClass
* only * 从objc_object::initIsa
/objc_object::changeIsa
调用,它们本身 only 从objc_constructInstance
/class_createInstance
/object_setClass
调用。所以,当你在磁盘上读取这些对象文件时,你只会遇到对象和类的指针;在ISA内部实际设置的位是在运行时排他地完成的。如果您希望从这些位中读取一些细节,则需要自己从周围的mach-o数据中构建这些信息。