如何Objective-C的运行时检索类和方法列表? [英] How does the Objective-C runtime retrieve the list of classes and methods?
问题描述
如果我得到以下的Objective-C源文件:
If I get the following Objective-C source file:
// test.m
#import <objc/Object.h>
@interface MySuperClass: Object {
}
-(void) myMessage1;
@end
@implementation MySuperClass
-(void) myMessage1 {
}
@end
@interface MyClass: MySuperClass {
}
-(void) myMessage2;
@end
@implementation MyClass
-(void) myMessage2 {
}
@end
int main() {
return 0;
}
和尝试从其生成汇编文件,铛-fobjc-非脆弱-ABI -fnext运行时-S test.m
,我得到以下组装code:
and try to generate an assembly file from it with clang -fobjc-nonfragile-abi -fnext-runtime -S test.m
, I get the following assembly code:
.file "test.m"
.text
.align 16, 0x90
.type _2D__5B_MySuperClass_20_myMessage1_5D_,@function
_2D__5B_MySuperClass_20_myMessage1_5D_: # @"\01-[MySuperClass myMessage1]"
.Ltmp0:
.cfi_startproc
# BB#0:
movq %rdi, -8(%rsp)
movq %rsi, -16(%rsp)
ret
.Ltmp1:
.size _2D__5B_MySuperClass_20_myMessage1_5D_, .Ltmp1-_2D__5B_MySuperClass_20_myMessage1_5D_
.Ltmp2:
.cfi_endproc
.Leh_func_end0:
.align 16, 0x90
.type _2D__5B_MyClass_20_myMessage2_5D_,@function
_2D__5B_MyClass_20_myMessage2_5D_: # @"\01-[MyClass myMessage2]"
.Ltmp3:
.cfi_startproc
# BB#0:
movq %rdi, -8(%rsp)
movq %rsi, -16(%rsp)
ret
.Ltmp4:
.size _2D__5B_MyClass_20_myMessage2_5D_, .Ltmp4-_2D__5B_MyClass_20_myMessage2_5D_
.Ltmp5:
.cfi_endproc
.Leh_func_end1:
.globl main
.align 16, 0x90
.type main,@function
main: # @main
.Ltmp6:
.cfi_startproc
# BB#0:
movl $0, %eax
movl $0, -4(%rsp)
ret
.Ltmp7:
.size main, .Ltmp7-main
.Ltmp8:
.cfi_endproc
.Leh_func_end2:
.type L_OBJC_CLASS_NAME_,@object # @"\01L_OBJC_CLASS_NAME_"
.section "__TEXT,__objc_classname,cstring_literals","aw",@progbits
L_OBJC_CLASS_NAME_:
.asciz "MySuperClass"
.size L_OBJC_CLASS_NAME_, 13
.type l_OBJC_METACLASS_RO_$_MySuperClass,@object # @"\01l_OBJC_METACLASS_RO_$_MySuperClass"
.section "__DATA, __objc_const","aw",@progbits
.align 8
l_OBJC_METACLASS_RO_$_MySuperClass:
.long 1 # 0x1
.long 40 # 0x28
.long 40 # 0x28
.zero 4
.quad 0
.quad L_OBJC_CLASS_NAME_
.quad 0
.quad 0
.quad 0
.quad 0
.quad 0
.size l_OBJC_METACLASS_RO_$_MySuperClass, 72
.type OBJC_METACLASS_$_MySuperClass,@object # @"OBJC_METACLASS_$_MySuperClass"
.section "__DATA, __objc_data","aw",@progbits
.globl OBJC_METACLASS_$_MySuperClass
.align 8
OBJC_METACLASS_$_MySuperClass:
.quad OBJC_METACLASS_$_Object
.quad OBJC_METACLASS_$_Object
.quad _objc_empty_cache
.quad _objc_empty_vtable
.quad l_OBJC_METACLASS_RO_$_MySuperClass
.size OBJC_METACLASS_$_MySuperClass, 40
.type L_OBJC_METH_VAR_NAME_,@object # @"\01L_OBJC_METH_VAR_NAME_"
.section "__TEXT,__objc_methname,cstring_literals","aw",@progbits
L_OBJC_METH_VAR_NAME_:
.asciz "myMessage1"
.size L_OBJC_METH_VAR_NAME_, 11
.type L_OBJC_METH_VAR_TYPE_,@object # @"\01L_OBJC_METH_VAR_TYPE_"
.section "__TEXT,__objc_methtype,cstring_literals","aw",@progbits
L_OBJC_METH_VAR_TYPE_:
.asciz "v16@0:8"
.size L_OBJC_METH_VAR_TYPE_, 8
.type l_OBJC_$_INSTANCE_METHODS_MySuperClass,@object # @"\01l_OBJC_$_INSTANCE_METHODS_MySuperClass"
.section "__DATA, __objc_const","aw",@progbits
.align 8
l_OBJC_$_INSTANCE_METHODS_MySuperClass:
.long 24 # 0x18
.long 1 # 0x1
.quad L_OBJC_METH_VAR_NAME_
.quad L_OBJC_METH_VAR_TYPE_
.quad _2D__5B_MySuperClass_20_myMessage1_5D_
.size l_OBJC_$_INSTANCE_METHODS_MySuperClass, 32
.type l_OBJC_CLASS_RO_$_MySuperClass,@object # @"\01l_OBJC_CLASS_RO_$_MySuperClass"
.align 8
l_OBJC_CLASS_RO_$_MySuperClass:
.long 0 # 0x0
.long 8 # 0x8
.long 8 # 0x8
.zero 4
.quad 0
.quad L_OBJC_CLASS_NAME_
.quad l_OBJC_$_INSTANCE_METHODS_MySuperClass
.quad 0
.quad 0
.quad 0
.quad 0
.size l_OBJC_CLASS_RO_$_MySuperClass, 72
.type OBJC_CLASS_$_MySuperClass,@object # @"OBJC_CLASS_$_MySuperClass"
.section "__DATA, __objc_data","aw",@progbits
.globl OBJC_CLASS_$_MySuperClass
.align 8
OBJC_CLASS_$_MySuperClass:
.quad OBJC_METACLASS_$_MySuperClass
.quad OBJC_CLASS_$_Object
.quad _objc_empty_cache
.quad _objc_empty_vtable
.quad l_OBJC_CLASS_RO_$_MySuperClass
.size OBJC_CLASS_$_MySuperClass, 40
.type L_OBJC_CLASS_NAME_1,@object # @"\01L_OBJC_CLASS_NAME_1"
.section "__TEXT,__objc_classname,cstring_literals","aw",@progbits
L_OBJC_CLASS_NAME_1:
.asciz "MyClass"
.size L_OBJC_CLASS_NAME_1, 8
.type l_OBJC_METACLASS_RO_$_MyClass,@object # @"\01l_OBJC_METACLASS_RO_$_MyClass"
.section "__DATA, __objc_const","aw",@progbits
.align 8
l_OBJC_METACLASS_RO_$_MyClass:
.long 1 # 0x1
.long 40 # 0x28
.long 40 # 0x28
.zero 4
.quad 0
.quad L_OBJC_CLASS_NAME_1
.quad 0
.quad 0
.quad 0
.quad 0
.quad 0
.size l_OBJC_METACLASS_RO_$_MyClass, 72
.type OBJC_METACLASS_$_MyClass,@object # @"OBJC_METACLASS_$_MyClass"
.section "__DATA, __objc_data","aw",@progbits
.globl OBJC_METACLASS_$_MyClass
.align 8
OBJC_METACLASS_$_MyClass:
.quad OBJC_METACLASS_$_Object
.quad OBJC_METACLASS_$_MySuperClass
.quad _objc_empty_cache
.quad _objc_empty_vtable
.quad l_OBJC_METACLASS_RO_$_MyClass
.size OBJC_METACLASS_$_MyClass, 40
.type L_OBJC_METH_VAR_NAME_2,@object # @"\01L_OBJC_METH_VAR_NAME_2"
.section "__TEXT,__objc_methname,cstring_literals","aw",@progbits
L_OBJC_METH_VAR_NAME_2:
.asciz "myMessage2"
.size L_OBJC_METH_VAR_NAME_2, 11
.type l_OBJC_$_INSTANCE_METHODS_MyClass,@object # @"\01l_OBJC_$_INSTANCE_METHODS_MyClass"
.section "__DATA, __objc_const","aw",@progbits
.align 8
l_OBJC_$_INSTANCE_METHODS_MyClass:
.long 24 # 0x18
.long 1 # 0x1
.quad L_OBJC_METH_VAR_NAME_2
.quad L_OBJC_METH_VAR_TYPE_
.quad _2D__5B_MyClass_20_myMessage2_5D_
.size l_OBJC_$_INSTANCE_METHODS_MyClass, 32
.type l_OBJC_CLASS_RO_$_MyClass,@object # @"\01l_OBJC_CLASS_RO_$_MyClass"
.align 8
l_OBJC_CLASS_RO_$_MyClass:
.long 0 # 0x0
.long 8 # 0x8
.long 8 # 0x8
.zero 4
.quad 0
.quad L_OBJC_CLASS_NAME_1
.quad l_OBJC_$_INSTANCE_METHODS_MyClass
.quad 0
.quad 0
.quad 0
.quad 0
.size l_OBJC_CLASS_RO_$_MyClass, 72
.type OBJC_CLASS_$_MyClass,@object # @"OBJC_CLASS_$_MyClass"
.section "__DATA, __objc_data","aw",@progbits
.globl OBJC_CLASS_$_MyClass
.align 8
OBJC_CLASS_$_MyClass:
.quad OBJC_METACLASS_$_MyClass
.quad OBJC_CLASS_$_MySuperClass
.quad _objc_empty_cache
.quad _objc_empty_vtable
.quad l_OBJC_CLASS_RO_$_MyClass
.size OBJC_CLASS_$_MyClass, 40
.type L_OBJC_LABEL_CLASS_$,@object # @"\01L_OBJC_LABEL_CLASS_$"
.section "__DATA, __objc_classlist, regular, no_dead_strip","aw",@progbits
.align 8
L_OBJC_LABEL_CLASS_$:
.quad OBJC_CLASS_$_MySuperClass
.quad OBJC_CLASS_$_MyClass
.size L_OBJC_LABEL_CLASS_$, 16
.type L_OBJC_IMAGE_INFO,@object # @"\01L_OBJC_IMAGE_INFO"
.section "__DATA, __objc_imageinfo, regular, no_dead_strip","a",@progbits
.align 4
L_OBJC_IMAGE_INFO:
.long 0 # 0x0
.long 16 # 0x10
.size L_OBJC_IMAGE_INFO, 8
.section ".note.GNU-stack","",@progbits
我的问题是:如何做的Objective-C运行库,它必须与 test.o
这样的可执行文件可以成功创建链接,检索方法列表,以创建,例如,一个V表?是否有可能使用 .section伪...,@function
, .section伪...,@object
或 .section伪...,@progbits
装配指令来获得这些信息,至少在连接时间?
My question is: how does the Objective-C runtime library, which must be linked against test.o
so that the executable file can be successfully created, retrieves the methods list in order to create, for example, a vtable? Is it possible to use the .section ..., @function
, .section ..., @object
or .section ..., @progbits
assembly directives to get this information, at least on linking time?
推荐答案
编译器,链接器和运行时一起工作。
The compiler, linker, and runtime work together.
首先,编译器解析源$ C $ C为每个类,并发出指令,如。长
, .zero
和 .quad
描述类的实例变量,属性选择器和方法。汇编程序打开这些指令到的原始数据。
First, the compiler parses the source code for each class and emits directives like .long
, .zero
, and .quad
describing the class's instance variables, properties, selectors, and methods. The assembler turns these directives into raw data.
的数据是在运行时能够理解的格式。例如,开始在符号中的数据 OBJC_CLASS _ $ _ MyClass的
的布局相匹配的运行时的结构class_t
(在<定义href=\"http://opensource.apple.com/source/objc4/objc4-532/runtime/objc-runtime-new.h\"><$c$c>objc-runtime-new.h$c$c>).在符号数据 l_OBJC_CLASS_RO _ $ _ MyClass的
相匹配的布局运行时的结构class_ro_t
(虽然大多数字段为0,因为运行时更新它们时,它加载类)。在结构class_ro_t
有类型的 baseMethods
字段 method_list_t *
,这在 l_OBJC_CLASS_RO的情况下_ $ _ MyClass的
初始化为 l_OBJC _ $ _ INSTANCE_METHODS_MyClass
。在 l_OBJC _ $ _ INSTANCE_METHODS_MyClass
,你会发现奠定了像一个数据结构method_list_t
,与数组结束结构method_t
- 。一个类中的每个方法,在你的榜样,这不是很有趣,因为每个班只有一个方法
The data is in a format that the runtime understands. For example, the data starting at symbol OBJC_CLASS_$_MyClass
matches the layout of the runtime's struct class_t
(defined in objc-runtime-new.h
). The data at symbol l_OBJC_CLASS_RO_$_MyClass
matches the layout of the runtime's struct class_ro_t
(although most of the fields are 0 because the runtime updates them when it loads the class). The struct class_ro_t
has a baseMethods
field of type method_list_t *
, which in the case of l_OBJC_CLASS_RO_$_MyClass
is initialized to l_OBJC_$_INSTANCE_METHODS_MyClass
. At l_OBJC_$_INSTANCE_METHODS_MyClass
you will find data laid out like a struct method_list_t
, which ends with an array of struct method_t
- one for each method in the class. In your example, it's not very interesting because each of your classes has only one method.
编译器使用 .section伪
指令告诉链接器如何组数据块在一起。例如,所有的结构class_t
的块将在名为节 __ objc_classlist
放在一起。通过这种方式,运行时可以只是仰望评为部分 __ objc_classlist
,然后处理整款为结构class_t $ C $的数组C>。看看在<一的
GETSECT
宏href=\"http://opensource.apple.com/source/objc4/objc4-532/runtime/objc-file.mm\"><$c$c>objc-file.mm$c$c>.
The compiler uses the .section
directives to tell the linker how to group chunks of that data together. For example, all of the struct class_t
chunks will be put together in a section named __objc_classlist
. This way, the runtime can just look up the section named __objc_classlist
, and then process the entire section as an array of struct class_t
. Take a look at the GETSECT
macro in objc-file.mm
.
链接器安排功能 _objc_init
(在<一个href=\"http://opensource.apple.com/source/objc4/objc4-532/runtime/objc-os.mm\"><$c$c>objc-os.mm$c$c>)运行的非常的在你的过程中,生命周期早期的前主
的 _objc_init
功能注册一些回调的动态加载程序。尤其是,它指示loader调用 map_images
(在<一个href=\"http://opensource.apple.com/source/objc4/objc4-532/runtime/objc-runtime-new.mm\"><$c$c>objc-runtime-new.mm$c$c>),这就要求 map_images_nolock
,最终调用 _read_images
的 _read_images
函数实际上解析编译器发出的数据,这些数据块,并把它们放入数据结构 objc_msgSend
使用实际发送消息的对象。
The linker arranges for the function _objc_init
(in objc-os.mm
) to run very early in the lifetime of your process, before main
. The _objc_init
function registers some callbacks with the dynamic loader. In particular, it tells the loader to call map_images
(in objc-runtime-new.mm
), which calls map_images_nolock
, which eventually calls _read_images
. The _read_images
function actually parses those chunks of data emitted by the compiler and turns them into the data structures that objc_msgSend
uses to actually send messages to objects.
您可以下载Mac OS X 10.8的Objective-C运行时源$ C $的存档ç以了解更多信息。此档案还包含源文件的iOS / ARM(甚至是Windows操作系统!),虽然它可能不符合的究竟的到iOS的任何版本。
You can download an archive of the Mac OS X 10.8 Objective-C runtime source code to learn more. This archive also contains source files for iOS/ARM (and even Windows!), although it might not correspond exactly to any version of iOS.
这篇关于如何Objective-C的运行时检索类和方法列表?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!