在取消引用寄存器 (%r11) 时在 objc_msgSend 中崩溃,但我不明白为什么
Crashing in objc_msgSend while dereferencing a register (%r11), but I don't get why
所以我有一个有 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 [=12=]x40, %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 [=13=]x1, %dil
0x7fff9084a0ca <+10>: jne 0x7fff9084a14b ; <+139>
0x7fff9084a0cd <+13>: movabsq [=13=]x7ffffffffff8, %r11
0x7fff9084a0d7 <+23>: andq (%rdi), %r11
0x7fff9084a0da <+26>: movq %rsi, %r10
0x7fff9084a0dd <+29>: andl 0x18(%r11), %r10d
当 cpu 试图取消引用 %r11
寄存器时,我有时会在偏移量 +29
上崩溃。
我的问题是,为什么 objc_msgSend
取消引用该寄存器?根据System V ABI那是一个scratch register。但它每次都被取消引用 objc_msgSend
,我真的搞不懂它的用途。
%r11
中存在无效指针时发生崩溃
它 看起来 像 +23,%rdi
寄存器(指向接收者的指针)被解引用并且 andq
与 %r11
,但我不明白那是做什么的。但也许如果接收器在这里被释放,%r11
会充满垃圾?
这一理论得到了证实 assembly source w/ comments
我认为它指出 %r11
用于 isa
属性
"class = self->isa"。
这意味着该对象正在被释放,因为 isa
属性 已被丢弃
如果是这种情况,我该如何防范?
在调用 objc_msgSend
之前查看 if( self )
是否足够?
正如您所说,此时的 %r11 是来自 self
的 isa
指针。如果此时 self
是垃圾内存,那么它的第一个字指向垃圾也就不足为奇了。
需要说明的是,当您说 "Would a check to see if( self ) before calling objc_msgSend suffice?" 时,我认为您并不是说您自己在给 objc_msgSend
打电话。 (永远不要那样做。)如果它是垃圾,则在调用此方法之前检查 self
将无济于事。它是一个非 0 指针,所以这是真的。 (如果它是 0,我们已经通过 objc_msgSend 顶部的 nil-messaging 保释了。)
你以某种方式破坏了这段记忆。也许过度释放(尽管我怀疑在这种情况下)。也许你有 C 数据结构并粉碎了你的堆栈(感觉更有可能)。也许对象正在另一个线程上被释放?可能有很多东西。
很遗憾,您链接的网站已过期。它没有解释您正在使用的 objc_msgSend
的确切版本。
要了解反汇编程序的输出,您需要知道的是 Objective-C 运行时现在有一个称为“非指针 isa”的功能。 Another page on that site解释了非指针isa,但我会总结一下。
从历史上看,对象的 isa 字段是指向对象 class 的指针。这个指针不需要完整的 64 位地址,因为 none 的 Apple 操作系统使用完整的 64 位地址 space。 class 地址的许多位始终为零。
非指针 isa 不会在每个对象中浪费所有这些位,而是将这些位用于其他用途,例如存储对象的引用计数。这意味着当您想要指向 class 的指针时,您需要将其他位设置回零以获得有效地址。计算 isa & 0x7ffffffffff8
会关闭(屏蔽掉)所有非指针位,因此您会得到一个指向 class…
的有效指针
…如果 isa 字段没有损坏。如果 isa 字段已损坏,您将得到垃圾。如果垃圾是无效地址,你会崩溃。
这里发生的事情是您覆盖了包含该对象的内存,因此 isa 字段不再有效。
要调试问题,请阅读 how to find zombies. If that doesn't help, watch this WWDC video about using the address sanitizer。
所以我有一个有 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 [=12=]x40, %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 [=13=]x1, %dil
0x7fff9084a0ca <+10>: jne 0x7fff9084a14b ; <+139>
0x7fff9084a0cd <+13>: movabsq [=13=]x7ffffffffff8, %r11
0x7fff9084a0d7 <+23>: andq (%rdi), %r11
0x7fff9084a0da <+26>: movq %rsi, %r10
0x7fff9084a0dd <+29>: andl 0x18(%r11), %r10d
当 cpu 试图取消引用 %r11
寄存器时,我有时会在偏移量 +29
上崩溃。
我的问题是,为什么 objc_msgSend
取消引用该寄存器?根据System V ABI那是一个scratch register。但它每次都被取消引用 objc_msgSend
,我真的搞不懂它的用途。
%r11
它 看起来 像 +23,%rdi
寄存器(指向接收者的指针)被解引用并且 andq
与 %r11
,但我不明白那是做什么的。但也许如果接收器在这里被释放,%r11
会充满垃圾?
这一理论得到了证实 assembly source w/ comments
我认为它指出 %r11
用于 isa
属性
"class = self->isa"。
这意味着该对象正在被释放,因为 isa
属性 已被丢弃
如果是这种情况,我该如何防范?
在调用 objc_msgSend
之前查看 if( self )
是否足够?
正如您所说,此时的 %r11 是来自 self
的 isa
指针。如果此时 self
是垃圾内存,那么它的第一个字指向垃圾也就不足为奇了。
需要说明的是,当您说 "Would a check to see if( self ) before calling objc_msgSend suffice?" 时,我认为您并不是说您自己在给 objc_msgSend
打电话。 (永远不要那样做。)如果它是垃圾,则在调用此方法之前检查 self
将无济于事。它是一个非 0 指针,所以这是真的。 (如果它是 0,我们已经通过 objc_msgSend 顶部的 nil-messaging 保释了。)
你以某种方式破坏了这段记忆。也许过度释放(尽管我怀疑在这种情况下)。也许你有 C 数据结构并粉碎了你的堆栈(感觉更有可能)。也许对象正在另一个线程上被释放?可能有很多东西。
很遗憾,您链接的网站已过期。它没有解释您正在使用的 objc_msgSend
的确切版本。
要了解反汇编程序的输出,您需要知道的是 Objective-C 运行时现在有一个称为“非指针 isa”的功能。 Another page on that site解释了非指针isa,但我会总结一下。
从历史上看,对象的 isa 字段是指向对象 class 的指针。这个指针不需要完整的 64 位地址,因为 none 的 Apple 操作系统使用完整的 64 位地址 space。 class 地址的许多位始终为零。
非指针 isa 不会在每个对象中浪费所有这些位,而是将这些位用于其他用途,例如存储对象的引用计数。这意味着当您想要指向 class 的指针时,您需要将其他位设置回零以获得有效地址。计算 isa & 0x7ffffffffff8
会关闭(屏蔽掉)所有非指针位,因此您会得到一个指向 class…
…如果 isa 字段没有损坏。如果 isa 字段已损坏,您将得到垃圾。如果垃圾是无效地址,你会崩溃。
这里发生的事情是您覆盖了包含该对象的内存,因此 isa 字段不再有效。
要调试问题,请阅读 how to find zombies. If that doesn't help, watch this WWDC video about using the address sanitizer。