当前位置: 首页 > 工具软件 > Sym > 使用案例 >

利用.sym跨平台解析iOS符号

佟和安
2023-12-01

一、背景

在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解析工具。

 类似资料: