【iOS/Mac OS】程序崩在objc_msgSend(),怎么办?

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 程序崩在objc_msgSend(),怎么办?   最可能的原因是,当你向一个已经释放的对象发送消息时,或者虽然指针是正确的,却被别的对象破坏了内容(比如内存越界),再或者使用了悬摆指针(dangling pointer)。

程序崩在objc_msgSend(),怎么办?

 

最可能的原因是,当你向一个已经释放的对象发送消息时,或者虽然指针是正确的,却被别的对象破坏了内容(比如内存越界),再或者使用了悬摆指针(dangling pointer)。偶尔的时候也会是因为内存错误导致运行时的数据结构被破坏,但通常问题还是在接收者本身。

 

无论用Debugger还是通过崩溃日志(crash log),都可以得到远比backtrace(调用堆栈)多的信息。

 

 

接收者和selector寄存器(Receiver and selector registers)

 

objc_msgSend() 会在CPU寄存器中存储接收者对象和selector,这些值可以用来帮助分析问题。

 

在不同的架构下使用的寄存器会有所不同。下表是针对Mac OS X Leopard (Snow Leopard也应该是相同的)。


  objc_msgSend
objc_msgSend_fpret
objc_msgSend_stret
  receiver SEL receiver SEL
i386 eax* ecx eax* ecx
x86_64 rdi rsi rsi rdx
ppc r3 r4 r4 r5
ppc64 r3 r4 r4 r5
arm r0 r1 r1 r2

* i386中的注释: 接收者对象在大多数崩溃中都是存在eax中的。如果调用的路径过长,eax有可能保存的是其它值。


解读接收者和非法地址(Interpreting the receiver and invalid address)

 

你可以通过使用接收者地址和非法地址来制造崩溃的情况,以此学习一些技巧。在崩溃日志中,接收者地址使用上表所列的寄存器存储在Thread State中,而非法地址则列在日志顶部位置(通常显示如 KERN_PROTECTION_FAILURE at <invalid address>)。在Debugger控制台中, 非法地址会在程序停止时输出,此时也可以使用上表所列的寄存器来输出接收者的地址。

    Program received signal EXC_BAD_ACCESS, Could not access memory.
    Reason: KERN_PROTECTION_FAILURE at address: 0x00000001
    0x00090ec4 in objc_msgSend ()
    (gdb) p/x $eax
    $1 = 0x1

这个测试程序崩在[(id)1 release]。


通常是有两种情况中的一项造成的:接收者的地址是错误的(此时非法地址也是一样的,或者偏移16或32字节),或者接收者的地址是合理的,而非法地址是接收者的isa指针。后者通常是因为你正尝试使用一个已经释放的对象。

 

既要针对性的查找这些值,也要查找它们附近的值。在一些架构中,一个非法的isa会导致程序崩在isa+16或isa+32位置。

 

未对齐,不能被16整除(Not divisble by 16 - misaligned)


malloc() 返回的是16字节对齐的内存块。如果你的接收者地址没有按16字节对齐,那么它很可能不是一个正确的对象指针。

前两位和后两位都被置位 - malloc的free list

当一个块被释放后,内存分配器会写入free list指针。如果你随后使用被释放的对象,它的isa指针的前两位和后两位都会被置位。


所有位被取反 - GC的free list
和上面的malloc的free list相像,GC的free list会使得地址值看起来是错误的,但~address(取反操作)的值却看起是合理的。


0xa1b1c1d3 - CF container
CoreFoundation containers 使用这个值来表示已经删除或清空的项目。


ASCII 文本
或许是一个已经释放的对象被重新分配为一个字串,亦或者将一个已经释放的字串重新分配为一个对象,也可能是内存越界的原因。使用asciify命令将不同字节对齐模式下的字串输出出来,以便于查看。下面是一个看似URL相关的字串:
  % asciify 0x2e777777
  ###.www###
  ###www.###

 

追查selector(Interrogating the selector)

编译优化会使调用堆栈中指向第二段的调用点(call site)可能并不是真正导致崩溃的调用。它可能已经调用成功了,而是这个方法的一个尾部调用(tail call)导致了崩溃。正是因为尾部调用(tail call)的优化会导致其中间调用帧会被忽略而没有显示在调用堆栈中。我们这里可以使用selector寄存器来确认真正的崩溃调用。

 

一个selector会指向一个唯一的C字串。未来有可能在新系统改变,不过现在可以很方便的用来调试。如果你的程序崩在debugger中,打开Debugger控制台,使用上表中列出的selector寄存器执行如下指令:

    (gdb) x/s $ecx
    0xa1029: "release"


Snow Leopard系统提供的崩溃日志已经添加了selector的名字:

    Application Specific Information:
    objc_msgSend() selector name: release

 

除此之外,要想只从崩溃日志中得到selector是很困难的。直到Snow Leopard,你可以使用下面的方法:
1. 从崩溃日志的Thread State,对照前面表中所列的selector寄存器值,如:
   
ecx: 0x000a1029


2.从崩溃日志的Binary Images位置, 找到某个镜像(image)包含了这个地址。它常常要么是程序本身,要么就是 libobjc.A.dylib. 如果没有找到对应的image,就放弃吧。
    0x8b000 -   0x106ff7  libobjc.A.dylib ??? (???) <9b5973b7fa88f9aab7885530c7b278dd> /usr/lib/libobjc.A.dylib


3.找到同崩溃日志中所列的镜像匹配的程序文件。可以使用UUID确认它们的一致性。
    % dwarfdump -u /usr/lib/libobjc.A.dylib
    UUID: 26650299-C6EA-B1C8-52D6-072AC874D400 (ppc) /usr/lib/libobjc.A.dylib
    UUID: 9B5973B7-FA88-F9AA-B788-5530C7B278DD (i386) /usr/lib/libobjc.A.dylib
    UUID: D2A4E8E1-3C1C-E0D9-2249-125B6DD621F8 (x86_64) /usr/lib/libobjc.A.dylib

同时确保系统版本的一致性。


4.计算SEL在镜像中的偏移地址。
    0xa1029 - 0x8b000 = 0x16029


5.打印镜像(image)中指定偏移地址处的C字串。记住指定正确的架构。
    % otool -v -arch i386 -s __TEXT __cstring /usr/lib/libobjc.A.dylib | grep 16029
    00016029  release

 

转载请注明出处:http://blog.csdn.net/horkychen

原文地址: So you crashed in objc_msgSend().

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
2月前
|
安全 Android开发 iOS开发
Android vs iOS:深入剖析两大移动操作系统的优劣与未来趋势####
【10月更文挑战第21天】 本文旨在通过技术视角,全面对比分析Android与iOS两大主流移动操作系统的架构差异、用户体验、安全性及生态系统等方面,探讨其各自优势与不足,并预测未来发展趋势。 ####
93 1
|
2月前
|
Android开发 Swift iOS开发
深入探索iOS与Android操作系统的架构差异及其对应用开发的影响
在当今数字化时代,移动设备已经成为我们日常生活和工作不可或缺的一部分。其中,iOS和Android作为全球最流行的两大移动操作系统,各自拥有独特的系统架构和设计理念。本文将深入探讨iOS与Android的系统架构差异,并分析这些差异如何影响应用开发者的开发策略和用户体验设计。通过对两者的比较,我们可以更好地理解它们各自的优势和局限性,从而为开发者提供有价值的见解,帮助他们在这两个平台上开发出更高效、更符合用户需求的应用。
|
3月前
|
Android开发 Swift iOS开发
iOS和安卓作为主流操作系统,开发者需了解两者差异以提高效率并确保优质用户体验。
【10月更文挑战第1天】随着移动互联网的发展,智能手机成为生活必需品,iOS和安卓作为主流操作系统,各有庞大的用户群。开发者需了解两者差异以提高效率并确保优质用户体验。iOS使用Swift或Objective-C开发,强调简洁直观的设计;安卓则采用Java或Kotlin,注重层次与动画。Swift和Kotlin均有现代编程特性。此外,iOS设备更易优化,而安卓需考虑更多兼容性问题。iOS应用仅能通过App Store发布,审核严格;安卓除Google Play外还可通过第三方市场发布,审核较宽松。开发者应根据需求选择合适平台,提供最佳应用体验。
97 3
|
3月前
|
Java Linux Android开发
移动应用开发与操作系统的交互:深入理解Android和iOS
在数字时代,移动应用成为我们日常生活的一部分。本文将深入探讨移动应用开发的核心概念、移动操作系统的工作原理以及它们如何相互作用。我们将通过实际代码示例,展示如何在Android和iOS平台上创建一个简单的“Hello World”应用,并解释其背后的技术原理。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和知识。
|
1月前
|
安全 生物认证 Android开发
深入探索iOS与Android操作系统的安全性差异
本文旨在通过对比分析iOS和Android两大主流移动操作系统在安全性方面的差异,揭示它们各自的安全机制、面临的挑战以及用户如何提升自身设备的安全保护。通过对系统架构、应用审核机制、数据加密方式及隐私政策的深入探讨,本文为读者提供了一个全面了解两大平台安全性的视角,并提出了实用的安全建议。
|
2月前
|
IDE 安全 Android开发
深入探索Android与iOS操作系统的架构差异
本文旨在对比分析Android和iOS两大主流移动操作系统在架构设计上的根本差异。通过详细解读两者的系统架构、开发环境、以及安全性等方面,揭示它们各自的特点及优势,为开发者选择合适的平台提供参考。
|
2月前
|
安全 定位技术 Android开发
深入探索Android与iOS操作系统的安全性差异
【10月更文挑战第21天】 在当今数字化时代,智能手机已成为我们生活中不可或缺的一部分。其中,Android和iOS作为两大主流操作系统,各自拥有庞大的用户群体。然而,它们在安全性方面的表现却大相径庭。本文将深入探讨Android与iOS在安全机制、隐私保护以及应对恶意软件方面的差异,帮助读者更全面地了解这两个平台的安全特性。
|
2月前
|
安全 Android开发 iOS开发
深入探讨Android与iOS操作系统的差异性
本文旨在通过对比分析Android和iOS两大主流移动操作系统,揭示它们在设计理念、用户体验、安全性、应用生态及系统更新等方面的根本差异。不同于传统的功能列表式摘要,本摘要强调了两大系统背后的哲学思想及其对用户日常使用的实际影响,为读者提供了一个宏观且深入的视角来理解这两种操作系统的独特之处。
|
2月前
|
搜索推荐 Android开发 iOS开发
安卓与iOS的较量:选择最适合你的移动操作系统
在智能手机市场上,安卓和iOS一直是两大主流操作系统。本文将深入探讨这两个系统的优缺点,帮助你根据自己的需求做出最佳选择。
|
2月前
|
搜索推荐 安全 Android开发
安卓与iOS的对决:选择最适合你的操作系统
【10月更文挑战第26天】 在当今数字化时代,智能手机已成为我们生活中不可或缺的一部分。而在众多手机品牌中,安卓和iOS无疑是最受欢迎的两大操作系统。本文将深入探讨安卓和iOS的特点、优缺点以及适用场景,帮助你更好地了解这两个操作系统,从而做出更明智的选择。
84 1