概述

最近更新时间:2025-10-17 18:56:51

我的收藏

符号表

定义

符号表在不同平台上的意义略有差异,但其目的都是将混淆后的堆栈进行还原,还原成开发可读的文件名、方法名及行号。
在 Android 平台,包含了两类符号表:SO 的符号表和 Java 的 mapping 文件。详细说明请参见 Android 符号表
Android Java 堆栈还原:

Android Native 堆栈还原:

在 iOS 平台,符号表指 dSYM 文件。详细说明请参见 iOS 符号表。iOS 堆栈还原示例如下:

在 Harmony 平台,包含了两类符号表:SO 的符号表和 nameCache & SourceMaps 文件。详细说明请参见 Harmony(鸿蒙)符号表


区分符号表

还原时,可以通过异常堆栈的场景来判断需要取哪个符号表进行还原:
平台
类型
说明
Android 平台
Android Java 堆栈
符号表文件为 mapping 文件。
通过 App 版本,构建号定位查找符号表文件。
相同 App 版本且相同构建,支持同时上传多个 mapping 文件,通过文件名区分。
符号还原时,会根据 App 版本和构建号,找出符合条件的 mapping 文件,将这些 mapping 文件组成一个大 mapping,再进行翻译。
Android Native 堆栈
符号表为包含符号信息的 SO。
通过 SO 的 UUID 来查找符号表,即同一次构建可以同时生成包含符号表的 SO,以及移除了符号表的 SO,他们的 UUID是一样的。
App 版本、构建号、CPU 构建,以及模块名只是增强可读性,SO 的 UUID 是唯一标识。
iOS 平台
iOS 堆栈
符号表文件为 dSYM 文件。
通过 UUID来查找符号表。
App 版本、构建号、CPU 构建,以及模块名只是增强可读性,UUID 是唯一标识。
Harmony 平台
JS 堆栈
符号表文件为 SourceMap 文件。
通过 App 版本,构建号定位查找符号表文件。
相同 App 版本且相同构建,支持同时上传多个 SourceMap 文件,通过文件名区分。
符号还原时,会根据 App 版本和构建号,找出符合条件的 SourceMap 文件,将所有的文件组成一个大 SourceMap,再进行翻译。

管理符号表

用户可以在 应用配置 > 符号表 查看当前产品的符号表信息。
1. 通过筛选条件查看指定的符号表是否已经上传。
2. 上传符号表,详细请参见 符号表上传
3. 单击查看详情查看产品符号表的整体情况。

用户可以在符号表列表 Tab 通过符号表类型、版本号、构建号、上传 ID、上传方式、符号表信息等筛选条件查询已上传的符号表信息 。


UUID

定义

UUID 是用于区别一个模块的唯一标识,Android 的 SO 符号表和 iOS 的 dSYM 文件都通过 UUID 来唯一标识一个符号表文件。SDK 在捕获到异常堆栈后,会获取对应模块的 UUID,符号表还原平台,根据这个 UUID 来检索对应的符号表文件。
示例1:Android Native 堆栈中,包含 UUID 信息,还原系统通过 UUID 来检索符号表。

示例2:iOS 的异常中,crash.log 文件的 Binary Images 部分,包含模块地址范围与 UUID、模块名的映射。



UUID 提取指引

Android SO UUID

Android NDK 生成 UUID 的时候有两种方式。
编译时生成
读取 .note.gnu.build-id 这个 Section,如果发现有值,则使用编译生成的 UUID。
readelf -x .note.gnu.build-id libBugly.SO

Hex dump of section '.note.gnu.build-id':
0x000001c8 04000000 14000000 03000000 474e5500 ............GNU.
0x000001d8 c21c5005 a226dedc 83a5a5b3 1611a3d6 ..P..&..........
0x000001e8 27713fd9 'q?.
其中:
0x000001c8:表示 .note.gnu.build-id 所在 elf 的偏移地址。
04000000:表示 name size,对应10进制是4byte。
14000000:表示 UUID 所占的大小,对应的10进制是20个 byte。
03000000:表示 type。
474e5500是4个 byte,对应名称 GNU。
c21c5005 a226dedc 83a5a5b3 1611a3d6 27713fd9合计20个 byte,对应编译过程生成的 UUID。编译生成的 UUID 是160bit,平台采用16个字节即128bit,如果超出128bit,平台会舍弃前32bit。因此此处实际上平台最终得到的 UUID 是 a226dedc83a5a5b31611a3d627713fd9,也就是最后16个字节。
注意:
平台使用的是16个字节的 UUID ,也就是编译生成的 UUID 中的最后16个字节。
运行时生成
当 elf 文件不存在 .note.gnu.build-id 节区时,平台计算.text 节区前4096字节哈希生成16字节 UUID 的方式会因仅部分字节差异导致冲突,即便将计算范围扩大到整个.text 节区,也无法解决两份仅差几行空格(.text 内容不变却编译出不同 UUID)的代码的 UUID 冲突问题,且运行时计算整个.text 节区耗时会影响程序效率,而开发者虽可能为编译效率未开启 .note.gnu.build-id 生成,但编译耗时远小于运行时耗时影响。故严格来说,若要保证 UUID 绝对不冲突,需在 SO 构建时配置生成 .note.gnu.build-id,您可以通过以下两种方式在编译过程开启生成 .note.gnu.build-id:
NDK 构建:在 Android.mk 配置 LOCAL_LDFLAGS += -Xlinker --build-id。
Clang 构建:参考此处

Mach-O UUID

Mach-O UUID 是在构建时生成,存储在 Mach-O header 中,查询一个 dSYM 的 UUID 方法有两种。
命令行读取
dwarfdump -u path/to/compile/executable
代码读取
#import <mach-o/ldsyms.h>
NSString *executableUUID()
{
const uint8_t *command = (const uint8_t *)(&_mh_execute_header + 1);
for (uint32_t idx = 0; idx < _mh_execute_header.ncmds; ++idx) {
if (((const struct load_command *)command)->cmd == LC_UUID) {
command += sizeof(struct load_command);
return [NSString stringWithFormat:@"%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
command[0], command[1], command[2], command[3],
command[4], command[5],
command[6], command[7],
command[8], command[9],
command[10], command[11], command[12], command[13], command[14], command[15]];
} else {
command += ((const struct load_command *)command)->cmdsize;
}
}
return nil;
}

UUID 什么时候会相同

Each binary file in an app—the main app executable, frameworks, and app extensions—has its own dSYM file. The compiled binary and its companion dSYM file are tied together by a build UUID, that’s recorded by both the built binary and dSYM file. If you build two binaries from the same source code but with different Xcode versions or build settings, the build UUIDs for the two binaries won’t match. A binary and a dSYM file are only compatible with each other when they have identical build UUIDs. Keep the dSYM files for the specific builds you distribute, and use them when diagnosing issues from crash reports.
上面的解释是引用文章 Building Your App to Include Debugging Information,这个规则对 Android Native / Linux 平台同样适用。
说明:
只有两个条件同时具备,模块 UUID 才会相等:
代码完全相同。
构建平台配置完全相同。