选择器名称存储在__objc_methname
段的__TEXT
部分中:
:; otool -v -s __TEXT __objc_methname /System/Library/Frameworks/AppKit.framework/AppKit | head
/System/Library/Frameworks/AppKit.framework/AppKit:
Contents of (__TEXT,__objc_methname) section
0x000000000097cbd8 count
0x000000000097cbde countByEnumeratingWithState:objects:count:
0x000000000097cc09 alloc
0x000000000097cc0f initWithObjects:count:
0x000000000097cc26 release
0x000000000097cc2e autorelease
0x000000000097cc3a copy
0x000000000097cc3f timeIntervalSinceNow
指向选择器的指针存储在__objc_selrefs
段的__DATA
部分中:
:; otool -v -s __DATA __objc_selrefs /System/Library/Frameworks/AppKit.framework/AppKit | head
/System/Library/Frameworks/AppKit.framework/AppKit:
Contents of (__DATA,__objc_selrefs) section
0x0000000000d77d80 __TEXT:__objc_methname:initWithObjects:count:
0x0000000000d77d88 __TEXT:__objc_methname:copy
0x0000000000d77d90 __TEXT:__objc_methname:timeIntervalSinceNow
0x0000000000d77d98 __TEXT:__objc_methname:sharedAppleEventManager
0x0000000000d77da0 __TEXT:__objc_methname:_prepareForDispatch
0x0000000000d77da8 __TEXT:__objc_methname:_setLaunchTaskMaskBits:
0x0000000000d77db0 __TEXT:__objc_methname:_disableSuddenTermination
0x0000000000d77db8 __TEXT:__objc_methname:_appleEventActivationInProgress
源代码中的ASEL
实际上(当前)是指向选择器的 C 字符串名称的指针。所以如果你写这个:
SEL s = @selector(initWithObjects:count:);
然后s
实际上是 a char const *
,它指向字符串initWithObjects:count:
。直到最近,您还可以通过以下方式打印选择器名称:
NSLog(@"selector is %s", (char *)s);
但是,Apple 更改了编译器(我相信从 Xcode 4.6 开始)以禁止将 a 强制转换SEL
为 a char *
,因此他们将来可能会更改选择器的实现。
无论如何,棘手的部分是机器代码__objc_selrefs
使用 PC 相对寻址从该部分加载指针。PC 是“程序计数器”,即当前执行指令的地址。在 x86 架构上,它通常称为 IP(指令指针)或 EIP(扩展 IP)。
这就是您的反汇编相关说明中发生的事情:
1444 LDR R1, =(off_2038 - 0x145C)
...
1454 LDR R1, (PC,R1)
指向选择器的指针从地址 0x2038 处的字加载。但是常量 0x2038 实际上并没有出现在机器代码中。通过分析程序的数据流,您的反汇编程序帮助您计算了它。存储在第一LDR
条指令中的常量实际上是 0xBDC,因为 0xBDC + 0x145C = 0x2038。
您可能想知道为什么当第二LDR
条指令位于地址 0x1454 时它使用 0x145C。当 ARM 处理器使用 PC 相对寻址计算地址时,PC 的值实际上是当前执行指令的地址加 4 或加 8(取决于处理器模式)。 这记录在这里(可能还有其他地方)。