【iOS/Mac OS】程式崩在objc_msgSend(),怎麼辦?

來源:互聯網
上載者:User

程式崩在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

當一個塊被釋放後,記憶體 Clerk會寫入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().

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.