一、背景
在iOS開發中,我們經常會遇到各種各樣的線上問題,爲了能夠定位線上問題發生位置,我們搭建了各種各樣收集信息的框架,當線上發生問題時,會收集到發生問題的方法調用棧信息,這些信息可以幫助我們來解決相關問題。
無論是我們自己搭建的框架還是直接使用各種第三方收集框架,我們拿到的方法棧調用信息通常都是一些符號,爲了能夠從這些符號中獲取正確的方法名,我們需要在線上包對應的dSYM文件幫助下進行符號化,進而得到明確的方法名。
對於如何使用dSYM文件來解析符號,網上已經有了很多質量很好的教程,推薦其中一個教程。
對於目前已有的符號化方法,無論是 symbolicatecrash
還是 atos
,均需要配置 Xcode
環境,對於沒有 Xcode
或 MacOS
環境的機器來說,符號化就變得毫無希望。
今天就來介紹一種無需特殊 Xcode
及 MacOS
環境即可解析iOS符號的方法。
二、前期準備
1. dSYM文件
爲了解析符號,我們還是需要準備好對應的 dSYM
文件,畢竟 dSYM
文件中存儲着程序中的符號信息。
2. dump_syms工具
dump_syms
工具是谷歌 Breakpad
項目中提供的一個工具,該工具可以將讀取 dSYM
文件中的符號信息,並將這些信息導出爲文本文件。
dump_syms
工具獲取流程如下:
- 前往 https://github.com/google/breakpad 下載源碼
- 進入 src/tools目錄
- 根據平臺進入對應目錄
- 進入 dump_syms 目錄,根據對應方法進行源碼編譯,生成 dump_syms 工具
3. 導出.sym文件
使用 dump_syms
工具導出 dSYM
文件中符號信息,命令如下: dump_syms [-a ARCHITECTURE] [dSYM path] > [output path]
,例如 dump_syms -a arm64 /Users/mademao/Desktop/SymbolDemo.app.dSYM/ > /Users/mademao/Desktop/arm64.sym
。
三、解析符號
對於如何解析符號,這裏只介紹解析原理,具體可實現一個跨平臺腳本,使用腳本來進行跨平臺iOS符號解析。
1. .sym文件結構介紹
在上述步驟中導出的 .sym
文件結構如下:
MODULE mac arm64 F14BC1FA245E3F84801872FB087F8FFA0 SymbolDemo
FILE 0 /Users/mademao/Desktop/SymbolDemo/SymbolDemo/AppDelegate.m
FILE 1 /Users/mademao/Desktop/SymbolDemo/SymbolDemo/SceneDelegate.h
FILE 2 /Users/mademao/Desktop/SymbolDemo/SymbolDemo/SceneDelegate.m
FILE 3 /Users/mademao/Desktop/SymbolDemo/SymbolDemo/ViewController.m
FILE 4 /Users/mademao/Desktop/SymbolDemo/SymbolDemo/main.m
FUNC 5f90 4c 0 -[ViewController viewDidLoad]
5f90 24 17 3
5fb4 1c 18 3
5fd0 c 20 3
FUNC 5fdc 88 0 -[AppDelegate application:didFinishLaunchingWithOptions:]
5fdc 4c 18 0
6028 3c 21 0
FUNC 6064 108 0 -[AppDelegate application:configurationForConnectingSceneSession:options:]
6064 74 27 0
60d8 c 30 0
60e4 4 30 0
60e8 1c 30 0
6104 2c 30 0
6130 10 30 0
6140 2c 31 0
FUNC 616c 80 0 -[AppDelegate application:didDiscardSceneSessions:]
616c 4c 34 0
61b8 34 38 0
FUNC 61ec a0 0 main
61ec 18 12 4
6204 8 13 4
620c c 14 4
6218 10 16 4
6228 4 16 4
622c 1c 16 4
6248 4 17 4
624c 4 18 4
6250 4 18 4
6254 4 18 4
6258 c 18 4
6264 4 18 4
6268 24 19 4
FUNC 628c b4 0 -[SceneDelegate scene:willConnectToSession:options:]
628c 6c 18 2
62f8 48 22 2
FUNC 6340 4c 0 -[SceneDelegate sceneDidDisconnect:]
6340 2c 25 2
636c 20 30 2
FUNC 638c 4c 0 -[SceneDelegate sceneDidBecomeActive:]
638c 2c 33 2
63b8 20 36 2
FUNC 63d8 4c 0 -[SceneDelegate sceneWillResignActive:]
63d8 2c 39 2
6404 20 42 2
FUNC 6424 4c 0 -[SceneDelegate sceneWillEnterForeground:]
6424 2c 45 2
6450 20 48 2
FUNC 6470 4c 0 -[SceneDelegate sceneDidEnterBackground:]
6470 2c 51 2
649c 20 55 2
FUNC 64bc 2c 0 -[SceneDelegate window]
64bc 14 13 1
64d0 18 13 1
FUNC 64e8 48 0 -[SceneDelegate setWindow:]
64e8 20 13 1
6508 1c 0 1
6524 c 13 1
FUNC 6530 44 0 -[SceneDelegate .cxx_destruct]
6530 1c 15 2
654c 28 15 2
PUBLIC 0 0 _mh_execute_header
PUBLIC 8028 0 OBJC_LABEL_CLASS_$
PUBLIC 8030 0 OBJC_LABEL_CLASS_$
PUBLIC 8038 0 OBJC_LABEL_CLASS_$
PUBLIC 8040 0 _OBJC_LABEL_PROTOCOL_$_NSObject
PUBLIC 8048 0 _OBJC_LABEL_PROTOCOL_$_UIApplicationDelegate
PUBLIC 8050 0 _OBJC_LABEL_PROTOCOL_$_UISceneDelegate
PUBLIC 8058 0 _OBJC_LABEL_PROTOCOL_$_UIWindowSceneDelegate
PUBLIC c060 0 _OBJC_METACLASS_RO_$_ViewController
PUBLIC c0a8 0 _OBJC_$_INSTANCE_METHODS_ViewController
PUBLIC c0c8 0 _OBJC_CLASS_RO_$_ViewController
PUBLIC c110 0 _OBJC_$_PROTOCOL_INSTANCE_METHODS_NSObject
PUBLIC c2e0 0 _OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_NSObject
PUBLIC c300 0 _OBJC_$_PROP_LIST_NSObject
PUBLIC c348 0 _OBJC_$_PROTOCOL_METHOD_TYPES_NSObject
PUBLIC c3e8 0 _OBJC_$_PROTOCOL_REFS_UIApplicationDelegate
PUBLIC c400 0 _OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_UIApplicationDelegate
PUBLIC c900 0 _OBJC_$_PROP_LIST_UIApplicationDelegate
PUBLIC c918 0 _OBJC_$_PROTOCOL_METHOD_TYPES_UIApplicationDelegate
PUBLIC cac0 0 _OBJC_CLASS_PROTOCOLS_$_AppDelegate
PUBLIC cad8 0 _OBJC_METACLASS_RO_$_AppDelegate
PUBLIC cb20 0 _OBJC_$_INSTANCE_METHODS_AppDelegate
PUBLIC cb70 0 _OBJC_$_PROP_LIST_AppDelegate
PUBLIC cbc8 0 _OBJC_CLASS_RO_$_AppDelegate
PUBLIC cc10 0 _OBJC_$_PROTOCOL_INSTANCE_METHODS_NSObject
PUBLIC cde0 0 _OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_NSObject
PUBLIC ce00 0 _OBJC_$_PROP_LIST_NSObject
PUBLIC ce48 0 _OBJC_$_PROTOCOL_METHOD_TYPES_NSObject
PUBLIC cee8 0 _OBJC_$_PROTOCOL_REFS_UISceneDelegate
PUBLIC cf00 0 _OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_UISceneDelegate
PUBLIC d028 0 _OBJC_$_PROTOCOL_METHOD_TYPES_UISceneDelegate
PUBLIC d088 0 _OBJC_$_PROTOCOL_REFS_UIWindowSceneDelegate
PUBLIC d0a0 0 _OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_UIWindowSceneDelegate
PUBLIC d120 0 _OBJC_$_PROP_LIST_UIWindowSceneDelegate
PUBLIC d138 0 _OBJC_$_PROTOCOL_METHOD_TYPES_UIWindowSceneDelegate
PUBLIC d160 0 _OBJC_CLASS_PROTOCOLS_$_SceneDelegate
PUBLIC d178 0 _OBJC_METACLASS_RO_$_SceneDelegate
PUBLIC d1c0 0 _OBJC_$_INSTANCE_METHODS_SceneDelegate
PUBLIC d2a0 0 _OBJC_$_INSTANCE_VARIABLES_SceneDelegate
PUBLIC d2c8 0 _OBJC_$_PROP_LIST_SceneDelegate
PUBLIC d320 0 _OBJC_CLASS_RO_$_SceneDelegate
PUBLIC d390 0 OBJC_CLASSLIST_SUP_REFS_$_
PUBLIC d398 0 OBJC_IVAR_$_SceneDelegate._window
PUBLIC d3a0 0 OBJC_CLASS_$_ViewController
PUBLIC d3c8 0 OBJC_METACLASS_$_ViewController
PUBLIC d3f0 0 OBJC_METACLASS_$_AppDelegate
PUBLIC d418 0 OBJC_CLASS_$_AppDelegate
PUBLIC d440 0 OBJC_METACLASS_$_SceneDelegate
PUBLIC d468 0 OBJC_CLASS_$_SceneDelegate
PUBLIC d490 0 _dyld_private
PUBLIC d498 0 _OBJC_PROTOCOL_$_NSObject
PUBLIC d4f8 0 _OBJC_PROTOCOL_$_UIApplicationDelegate
PUBLIC d558 0 _OBJC_PROTOCOL_$_UISceneDelegate
PUBLIC d5b8 0 _OBJC_PROTOCOL_$_UIWindowSceneDelegate
該文件以行爲單位,每一行記錄一條信息,其中記錄格式分爲以下5種:
(1) MODULE標識行
MODULE標識行在整個.sym文件中只有一行,且位於第一行。
該行記錄提供有關符號文件描述的模塊的元信息。它具有以下形式:
MODULE 操作系統 架構 ID 可執行文件或庫的基礎名稱
(2) FILE標識行
FILE標識行在整個.sym文件中包含多行,且位於MODULE標識行之後。
每一行FILE標識行記錄了一個源文件路徑。它具有以下形式:
FILE 編號 源文件路徑
其中編號從0開始遞增。
(3) FUNC標識行
FUNC標識行在整個.sym文件中包含多行。
該行用來描述源語言方法。它具有以下形式:
FUNC 地址 大小 參數尺寸 名稱
- 地址表示該方法的起始偏移位置的十六進制;
- 大小表示該方法對應機器指令佔用的長度的十六進制;
- 參數尺寸表示方法需要在堆棧上參數佔用大小的十六進制,iOS方法該值均爲0;
- 名稱表示該方法名稱;
(4) 無標識行
該類型行在某個FUNC標識行下存在,同時每個FUNC標識行下可能有多個該類型標識行。
每一行表示在機器指令下所屬FUNC指定方法所屬的文件下標及行號。它具有以下形式:
地址 大小 行號 文件下標
- 地址表示該機器代碼起始偏移位置的十六進制;
- 大小表示該機器代碼佔用的長度的十六進制;
- 行號表示該機器代碼在文件中的行號;
- 文件下標表示該機器代碼所在文件的下標,該下標對應FILE標識行中編號;
(5) PUBLIC標識行
PUBLIC標識行在.sym文件中包含多行,且位於所有FUNC及無標識行之後。
每一行PUBLIC標識行記錄了一個公共可見的鏈接符號,可包括類名信息、公共變量名等,其中有偏移地址但行號數據不確定的函數也會指定爲PUBLIC標識行。它具有以下形式:
PUBLIC 地址 參數尺寸 名稱
此處我們僅對指定爲PUBLIC的函數做介紹:
- 地址表示該機器代碼其實偏移位置的十六進制;
- 名稱表示該函數名稱;
以上就是整個.sym文件所包含的信息,其中不難看到對於程序方法來說,他的偏移位置等及所處文件均有信息記錄。
2. 解析iOS符號思路
對於獲取到的iOS符號,格式通常爲如下所示:
5 SymbolDemo 0x00000001000d5fb4 0x1000d0000 + 24501
對於以上信息,SymbolDemo
表示指令所在鏡像的bundle,0x00000001000d5fb4
表示程序運行時指令位置偏移量,0x1000d0000
表示指令所在鏡像通過 ASLR
隨機出來的偏移量,245001
表示指令在鏡像中的真正偏移量。
以上符號的解析過程如下:
- 通過bunlde爲
SymbolDemo
得到對應的.sym文件 - 通過偏移量
245001
找到.sym中對應的FUNC或PUBLIC,即FUNC 5f90 4c 0 -[ViewController viewDidLoad]
行,原因爲 5f90 < 245001 < 5f90 + 4c,從而確定該指令所屬方法爲-[ViewController viewDidLoad]
- 通過
245000
得到具體的無標識行,即5fb4 1c 18 3
,原因爲 5fb4 < 245001 < 5fb4 + 1c,從而確定該指令在文件中行號爲18,文件下標爲3 - 通過文件下標3得到所屬文件爲
ViewController.m
- 最終得到符號化後的信息爲
-[ViewController viewDidLoad] (ViewController.m:18)
以上即是通過.sym文件解析iOS符號的原理及流程,對應的可實現一個可跨平臺的腳本,即可完成不受Xcode
和MacOS
限制的iOS解析工具。