所以我有一个有 5 个参数的方法。 正如预期的那样,寄存器在被调用之前就处于状态:
$rdi: The receiver
$rsi: the selector for the method
$rdx: first arg
$rcx: second arg
$r8: third arg
$r9: fourth arg
$r10 fifth arg
在方法中,它做的第一件事是调用另一个objective-c方法
这反过来又调用objc_msgSend
(参见偏移量 +58):
MyApp`-[GTMOAuth2WindowController webView:resource:willSendRequest:redirectResponse:fromDataSource:]:
0x10044a1a0 <+0>: pushq %rbp
0x10044a1a1 <+1>: movq %rsp, %rbp
0x10044a1a4 <+4>: subq $0x40, %rsp
0x10044a1a8 <+8>: movq 0x10(%rbp), %rax
0x10044a1ac <+12>: movq %rdi, -0x10(%rbp)
0x10044a1b0 <+16>: movq %rsi, -0x18(%rbp)
0x10044a1b4 <+20>: movq %rdx, -0x20(%rbp)
0x10044a1b8 <+24>: movq %rcx, -0x28(%rbp)
0x10044a1bc <+28>: movq %r8, -0x30(%rbp)
0x10044a1c0 <+32>: movq %r9, -0x38(%rbp)
0x10044a1c4 <+36>: movq %rax, -0x40(%rbp)
0x10044a1c8 <+40>: movq -0x10(%rbp), %rax
0x10044a1cc <+44>: movq -0x38(%rbp), %rdx
0x10044a1d0 <+48>: movq 0x2ffda9(%rip), %rsi ; "handleCookiesForResponse:"
0x10044a1d7 <+55>: movq %rax, %rdi
0x10044a1da <+58>: callq 0x1005839a2 ; symbol stub for: objc_msgSend
然后转到objc_msgSend
的说明:
libobjc.A.dylib`objc_msgSend:
-> 0x7fff9084a0c0 <+0>: testq %rdi, %rdi
0x7fff9084a0c3 <+3>: je 0x7fff9084a140 ; <+128>
0x7fff9084a0c6 <+6>: testb $0x1, %dil
0x7fff9084a0ca <+10>: jne 0x7fff9084a14b ; <+139>
0x7fff9084a0cd <+13>: movabsq $0x7ffffffffff8, %r11
0x7fff9084a0d7 <+23>: andq (%rdi), %r11
0x7fff9084a0da <+26>: movq %rsi, %r10
0x7fff9084a0dd <+29>: andl 0x18(%r11), %r10d
我有时会在偏移+29
时崩溃,当 CPU 尝试取消引用%r11
寄存器时。
我的问题是,objc_msgSend
为什么要取消引用该寄存器? 根据系统 V ABI 这是一个暂存寄存器。 但是它每次objc_msgSend
都被取消引用,我真的无法弄清楚它的用途。
当%r11
中有无效指针时,我的崩溃正在发生
看起来在+23时,%rdi
寄存器(指向接收器的指针)被取消引用并andq
%r11
,但我不明白这是做什么的。 但也许如果接收器在这里被释放,%r11
会充满垃圾?
这个理论得到了这个带有评论的汇编来源的
证实我认为它指出%r11
用于isa
属性
"class = self->isa"。
这意味着对象正在被释放,因为isa
属性是垃圾
如果是这样的话,我该如何防范这种情况?
在打电话之前检查一下if( self )
就足够了吗objc_msgSend
?
不幸的是,您链接的网站已过时。它没有解释您正在使用objc_msgSend
的确切版本。
要理解反汇编器输出,你需要知道的是,Objective-C运行时现在有一个名为"非指针isa"的功能。该站点上的另一个页面解释了非指针 isa,但我会总结一下。
从历史上看,对象的 isa 字段是指向对象类的指针。此指针不需要完整的 64 位,因为 Apple 的操作系统都没有使用完整的 64 位地址空间。类地址的许多位始终为零。
非指针 isa 不是在每个对象中浪费所有这些位,而是将这些位用于其他事情,例如存储对象的引用计数。这意味着,当您想要指向该类的指针时,您需要将其他位设置回零以获得有效地址。计算isa & 0x7ffffffffff8
关闭(屏蔽)所有非指针位,因此您可以获得指向该类的有效指针...
。如果 ISA 字段尚未损坏。如果 isa 字段已损坏,则会得到垃圾。如果垃圾是无效地址,则会出现崩溃。
这里发生的事情是你已经覆盖了包含对象的内存,使得 isa 字段不再有效。
若要调试问题,请阅读有关如何查找僵尸的信息。如果这没有帮助,请观看有关使用地址清理器的 WWDC 视频。
正如您所说,此时的 %r11 是来自self
的isa
指针。如果此时self
是垃圾内存,那么它的第一个词指向垃圾也就不足为奇了。
需要明确的是,当你说"在打电话之前检查一下是否(self)就足够了吗objc_msgSend?我假设你不是说你自己在打电话给objc_msgSend
。(永远不要这样做。如果它是垃圾,则在调用此方法之前检查self
将无济于事。这是一个非 0 指针,所以这是真的。(如果它是 0,我们已经通过 objc_msgSend 顶部的 nil 消息进行保释。
你以某种方式破坏了这段记忆。也许过度释放(尽管在这种情况下我对此表示怀疑)。也许你有 C 数据结构并粉碎了你的堆栈(感觉更有可能)。也许对象正在另一个线程上释放?这可能是很多事情。