利用.sym跨平臺解析iOS符號

一、背景

在iOS開發中,我們經常會遇到各種各樣的線上問題,爲了能夠定位線上問題發生位置,我們搭建了各種各樣收集信息的框架,當線上發生問題時,會收集到發生問題的方法調用棧信息,這些信息可以幫助我們來解決相關問題。

無論是我們自己搭建的框架還是直接使用各種第三方收集框架,我們拿到的方法棧調用信息通常都是一些符號,爲了能夠從這些符號中獲取正確的方法名,我們需要在線上包對應的dSYM文件幫助下進行符號化,進而得到明確的方法名。

對於如何使用dSYM文件來解析符號,網上已經有了很多質量很好的教程,推薦其中一個教程

對於目前已有的符號化方法,無論是 symbolicatecrash還是 atos,均需要配置 Xcode環境,對於沒有 XcodeMacOS環境的機器來說,符號化就變得毫無希望。

今天就來介紹一種無需特殊 XcodeMacOS環境即可解析iOS符號的方法。

二、前期準備

1. dSYM文件

爲了解析符號,我們還是需要準備好對應的 dSYM文件,畢竟 dSYM文件中存儲着程序中的符號信息。

2. dump_syms工具

dump_syms工具是谷歌 Breakpad項目中提供的一個工具,該工具可以將讀取 dSYM文件中的符號信息,並將這些信息導出爲文本文件。

dump_syms工具獲取流程如下:

  1. 前往 https://github.com/google/breakpad 下載源碼
  2. 進入 src/tools目錄
  3. 根據平臺進入對應目錄
  4. 進入 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符號的原理及流程,對應的可實現一個可跨平臺的腳本,即可完成不受XcodeMacOS限制的iOS解析工具。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章