Как среда выполнения Objective-C извлекает список классов и методов?
Если я получаю следующий Objective-C исходный файл:
// 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;
}
и попытайтесь сгенерировать файл сборки из него с помощью clang -fobjc-nonfragile-abi -fnext-runtime -S test.m
, я получаю следующий код сборки:
.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 "[email protected]: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
, чтобы исполняемый файл мог быть успешно создан, извлекает список методов для создания, например, таблицы vtable? Можно ли использовать директивы сборки .section ..., @function
, .section ..., @object
или .section ..., @progbits
для получения этой информации, по крайней мере, при связывании времени?
Ответы
Ответ 1
Компилятор, компоновщик и среда выполнения работают вместе.
Сначала компилятор анализирует исходный код для каждого класса и испускает директивы типа .long
, .zero
и .quad
, описывающие переменные экземпляра класса, свойства, селектора и методы. Ассемблер превращает эти директивы в необработанные данные.
Данные находятся в формате, который понимает среда выполнения. Например, данные, начинающиеся с символа OBJC_CLASS_$_MyClass
, соответствуют макету времени выполнения struct class_t
(определенному в objc-runtime-new.h
). Данные в символе l_OBJC_CLASS_RO_$_MyClass
соответствуют макету времени выполнения struct class_ro_t
(хотя большинство полей равны 0, поскольку среда выполнения обновляет их при загрузке класса). struct class_ro_t
имеет поле baseMethods
типа method_list_t *
, которое в случае l_OBJC_CLASS_RO_$_MyClass
инициализируется значением l_OBJC_$_INSTANCE_METHODS_MyClass
. В l_OBJC_$_INSTANCE_METHODS_MyClass
вы найдете данные, выложенные как a struct method_list_t
, который заканчивается массивом struct method_t
- по одному для каждого метода в классе. В вашем примере это не очень интересно, потому что каждый из ваших классов имеет только один метод.
Компилятор использует директивы .section
, чтобы сообщить компоновщику, как группировать фрагменты этих данных вместе. Например, все фрагменты struct class_t
будут собраны вместе в разделе с именем __objc_classlist
. Таким образом, среда выполнения может просто искать раздел с именем __objc_classlist
, а затем обрабатывать весь раздел как массив struct class_t
. Взгляните на макрос GETSECT
в objc-file.mm
.
Компоновщик организует функцию _objc_init
(objc-os.mm
) для запуска очень рано в течение всего процесса, до main
. Функция _objc_init
регистрирует некоторые обратные вызовы с помощью динамического загрузчика. В частности, он сообщает загрузчику позвонить map_images
(в objc-runtime-new.mm
), который вызывает map_images_nolock
, который в итоге вызывает _read_images
. Функция _read_images
фактически анализирует эти фрагменты данных, испускаемых компилятором, и превращает их в структуры данных, которые objc_msgSend
использует для фактической отправки сообщений объектам.
Вы можете загрузить архив исходного кода операционной системы Mac OS X 10.8 Objective-C, чтобы узнать больше. Этот архив также содержит исходные файлы для iOS/ARM (и даже Windows!), Хотя он может не соответствовать любой версии iOS.
Ответ 2
Это не то, что среда выполнения знает, как читать вашу программу, но что интерфейс objc переводит базу кода для использования среды выполнения.