在iOS App開發過程中,我們會利用Xcode打包,生成.xcarchive的包文件,通過Xcode的Organizer工具可以管理、導出發布文件,相信iOS開發對于這些過程都相當地熟悉,這里就不再贅述。主要想說的是,打包之后的dSYM文件。
通過以下方式獲取dSYM文件,首先打開Archives管理窗口,如下圖:

每Archive一次,都會生成一條記錄,找到當前記錄所在的目錄,如下圖:

打開.xcarchive包文件會看到其目錄結構,dSYMs中的dSYM包文件就是我們接下來要剖析的文件了。dSYM同樣個包文件,打開之后,我們會找到一個二進制文件,如下圖,例子中是一個叫Demo的二進制文件。

從目錄名中,可以看出iOS使用的是DWARF文件結構,DWARF(可能的解釋是:Debugging With Attributed RecordFormats)是一種調試文件結構標準,結構相當的復雜。關于DWARF的前世今生,從何而來,為何而來,如何發展,請參考DWARF官網或網上搜索。
dSYM文件的一個重要作用就在于,當我們的程序發生崩潰,通過crash log或其他方式,會看到調用棧信息。通過log信息,我們并不知道具體是在那個文件的哪個位置出了問題,這個時候這個二進制文件就非常的有用了,通過它我們可以通過工具去符號化,比如Xcode自帶的atos,這樣可以直接定位到某個文件的具體位置。
目前有很多的工具可以解析DWARF文件,比如,Mac OS中就有dwarfdump、otool等,但現有工具并不能滿足我們所有的需求,現在我們了解下其內部結構,在日后開發中有需要,可以用來參考。
下面我們打開這個二進制文件。注意:以下“段”單位為4個字節。
打開文件后,先來看下文件頭,結構定義參考<fat.h>。

段為magic,這里需要注意字節序,讀出來之后需要看下是0xCAFEBABE還是0xBEBAFECA,需要根據這個來轉后續讀取的字節的字節序。
第二段為arch count,也就是該App或dSYM中包含哪些CPU架構,比如armv7、arm64等,這個例子中為2,表示包含了兩種cpu架構。
后續段中包含cputype(0x0000000C)、cpusubtype(0x00000009)、offset(0x00000040)、size(0x000F6825)等數據,根據fat中的結構定義,依次讀取,這里需要說明的是,如果只包含一種CPU架構的話,是沒有這段fat頭定義的,可以跳過這部分,直接讀取Arch數據。
根據fat頭中讀取的offset數據,我們可以跳到文件對應的arch數據的位置,當然如果只有一種架構的話就不需要計算偏移量了。例子中32-bit arch的offset為0x00000040,64-bit arch的offset為0x000F6880。以下數據結構參考<mach.h>。

32-bit

64-bit
通過magic我們可以區分出是32-bit還是64-bit,64-bit多了4個字節的保留字段,這里同樣需要注意字節序的問題,也就是判斷magic,來確定是否需要轉換字節序。
以下部分解析,以32-bit為例。

UUID是16個字節(128bit)的一段數據,是文件的標識,前面提到的符號化時,這個UUID必須要和App二進制文件中的UUID一致,才能被正確的符號化。dwarfdump查看的UUID就是這段數據。讀取這部分數據時通過Command結構讀取的,也就是段(0x0000001B)表示接下來的數據類型,第二段(0x00000018)數據的大小(包含Command數據)。

符號表數據塊結構,前二段依然是Command數據。后邊4段分別為符號在文件中的偏移量(0x00001000)、符號個數(0x00000015)、字符串在文件中的偏移量(0x000010FC)、字符串大小(0x00000297)。
接下來就是讀取Segment和Section數據塊了,和上面讀取數據塊結構一樣是根據Command結構讀取,下圖展示的Segment數據和Section數據是分開的,實際在二進制文件中它們是連續的,也就是每一條Segment數據后面會緊跟著多條對應的Section數據,Section的數據總數是通過Segment結構中的nsects決定的。

從Segment數據中我們可以看到, __TEXT的vmaddr是0x00004000,也就是程序的加載地址,當然這個是指32-bit的程序,64-bit是不同的。__DWARF中表明了DWARF數據塊的信息,表示dSYM是DWARF格式的數據結構。

以上為Section數據的一部分,從Section數據中,我們可以找到__debug_info、__debug_pubnames, __debug_line等調試信息,通過這些調試信息我們可以找到程序中符號的起始地址、變量類型等信息。如果我們要符號化的話,就可以通過解析這些數據得到我們想要的信息。
關于Segment和Section中類型的定義,請參考DWARF官網。
關于如何解析數據得到符號在文件中的位置,下篇再做分享。
到這里我們已經讀取了符號文件頭中的大部分數據,在文件頭里還有一部分數據也是很重要的,就是符號塊數據,他是我們程序里所有的方法信息。

通過SymTab中的數據可以得到Symbol在文件中的位置和個數,Symbol塊數據中包含了符號的起始地址、字符串的偏移量等數據,這部分數據結構可以參考<nlist.h> 和 <stabl.h>。在這部分數據全部讀取后,就可以讀取所有的符號數據了,也就是接下來的數據。

通過SymTab和Symbo中的數據可以得到每個符號字符串在文件中的偏移量和大小,每個符號數據是以0結尾的字符串。
我們通過以上兩部分數據的組合就可以得到每個symbo在程序中的加載地址了。這些數據對于以后做符號工作都非常的有幫助。64-bit的數據解析與以上方法相同,不過要注意64-bit中的有些數據是有點差別的,解析時需要注意。
到此,關于dSYM文件中頭部數據讀取就完成了。頭部數據都有相應的數據結構定義,讀取時相對會比較容易些,解析數據時要注意字節序的問題,32-bit和64-bit數據結構的差異、字節長度的差異,DWARF版本的差異,每個數據塊之間都是緊密聯系的,一個字節的讀取偏差就會造成后續數據的讀取錯誤,正所謂差之毫厘,失之千里。
本站文章版權歸原作者及原出處所有 。內容為作者個人觀點, 并不代表本站贊同其觀點和對其真實性負責,本站只提供參考并不構成任何投資及應用建議。本站是一個個人學習交流的平臺,網站上部分文章為轉載,并不用于任何商業目的,我們已經盡可能的對作者和來源進行了通告,但是能力有限或疏忽,造成漏登,請及時聯系我們,我們將根據著作權人的要求,立即更正或者刪除有關內容。本站擁有對此聲明的最終解釋權。