取消引用寄存器 (%r11) 时objc_msgSend崩溃,但我不明白为什么



所以我有一个有 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 是来自selfisa指针。如果此时self是垃圾内存,那么它的第一个词指向垃圾也就不足为奇了。

需要明确的是,当你说"在打电话之前检查一下是否(self)就足够了吗objc_msgSend?我假设你不是说你自己在打电话给objc_msgSend。(永远不要这样做。如果它是垃圾,则在调用此方法之前检查self将无济于事。这是一个非 0 指针,所以这是真的。(如果它是 0,我们已经通过 objc_msgSend 顶部的 nil 消息进行保释。

你以某种方式破坏了这段记忆。也许过度释放(尽管在这种情况下我对此表示怀疑)。也许你有 C 数据结构并粉碎了你的堆栈(感觉更有可能)。也许对象正在另一个线程上释放?这可能是很多事情。

最新更新