2016-12-05 29 views
1

所以我有一个有5个参数的方法。正如预期的那样,寄存器版权状态,它被称为前:在取消引用寄存器(%r11)时崩溃了objc_msgSend,但我不明白为什么

$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解除引用注册?根据System V ABI这是一个临时寄存器。但它每次被取消引用objc_msgSend,我真的不知道它用于什么。

我崩溃发生的事情时,有在%r11

无效的指针,看起来像在+23,该%rdi寄存器(指针接收器)取消引用和andq“随着%r11万桶,但我不不要做什么。但是如果接收器在这里被释放,%r11可能会被垃圾填满?

这一理论得到这个assembly source w/ comments

在哪里,我认为它指出了%r11用于isa财产
“级=自我> ISA”证实。

这将意味着该对象被释放,因为isa属性junked

如果是这样的话,我怎么能防止呢?

在致电objc_msgSend之前查看if(self)就足够了吗?

+0

msgSend所做的第一件事就是如果在调用之前这样做并不会有多大帮助。正如你所说,'self'指向垃圾,但不幸的是它不是'NULL'。 – Jester

+0

有什么办法来检查'self'的地址是否包含有效的对象,而不是垃圾? –

回答

4

不幸的是,您链接的网站已过时。它没有解释你正在使用的objc_msgSend的确切版本。

为了理解反汇编器的输出,你需要了解的是Objective-C运行时现在有一个叫做“非指针isa”的特性。 Another page on that site解释非指针isa,但我会总结。

历史上,对象的isa字段是指向对象类的指针。这个指针不需要完整的64位,因为Apple的操作系统都不使用完整的64位地址空间。该类地址的许多位始终为零。

而不是浪费每个对象中的所有位,非指针isa使用其他的东西,如存储对象的引用计数。这意味着当你需要指向类的指针时,你需要将这些其他位设回零以获得有效地址。计算isa & 0x7ffffffffff8关闭(屏蔽掉)所有的非指针位,所以你得到一个有效的指针类...

...如果isa字段没有被破坏。如果isa字段已损坏,则会发生垃圾。如果垃圾是无效地址,则会发生崩溃。

这里发生了什么是你覆盖了包含该对象的内存,使得isa字段不再有效。

要调试问题,请阅读how to find zombies。如果这没有帮助,请看this WWDC video about using the address sanitizer

+0

感谢您的彻底答案和链接:)问题是,我不能再现这个问题,它通过HockeyApp进来,所以我严格从调查崩溃日志和程序集。你知道我如何覆盖包含原始接收器对象的内存吗?我会假定系统能够防止这种情况发生 - 只在已释放的内存中写入内容。 –

+0

在单个过程中没有类似的保护。 ARC(和Swift)可以帮助您避免内存管理错误,但您仍然可以制作它们。我建议您在Address Sanitizer下尽可能多地测试您的应用程序,并查看是否有任何警告。 –

+0

感谢您的链接。我观看了视频,如果你不介意的话,还有另外一个问题。在25点12分,她谈到阴影贴图。并解释Address Sanitizer如何工作,在每次访问内存之前添加一条指令来检查地址是否“中毒”,如果发现为真,则** crash **。我的问题是,如果你没有运行Sanitizer,那么应用程序不会在无效存储器访问时崩溃? –

2

如您所说,此时的%r11是来自selfisa指针。如果self在这一点上是垃圾内存,那么它的第一个单词指向垃圾就不足为奇了。

要清楚,当你说“在调用objc_msgSend之前检查是否(自我)就足够了吗?”我假设你并不是说你自己打电话给objc_msgSend。 (从不这样做。)在调用此方法之前检查self对于垃圾是无济于事的。这是一个非0指针,所以这是真的。 (如果它是0,我们已经通过在objc_msgSend顶部的无消息传递保护)。

您已经以某种方式删除了该内存。也许是过度释放(尽管我在这种情况下怀疑)。也许你有C数据结构并砸碎你的堆栈(感觉更可能)。也许这个对象正在被另一个线程解除分配?这可能是很多事情。

+0

感谢您的时间^^。这很难调试,因为我无法复制它,我要从HockeyApp的崩溃日志中查看,然后在Xcode中查看程序集。您能否详细说明C数据结构如何粉碎堆栈,以及这意味着什么?或者帮我读一个链接?这是你在说什么吗? http://stackoverflow.com/a/1347464/2415178 –

+0

我说“堆栈粉碎”,当我真的意味着“运行C数据结构,通常是一个数组结束”。考虑到崩溃,它会比堆栈上的堆更可能。看看Xcode的“Malloc Guard边缘”来帮助找到那种错误。 “配置文件”>“运行”>“诊断”中的其他内存管理选项也可能对此有所帮助(请参阅rob mayoff对链接的回答)。 –

+0

(顺便说一句,你所知道的和不知道的东西对我来说真的很独特,这表明你在相当先进的水平上学习非常努力,没有很多背景知识,我印象深刻,但是这使得它有点难以回答,你对ABI和反向工程objc_msgSend有了一个奇怪的认识,但并不知道C存储管理的普遍适用性,再次,我留下了深刻的印象,但如果我们假设你知道的比你多实际上可能会在错误的级别上回答。) –

相关问题