C++ 二进制接口(ABI)扫描仪:利用 DWARF 调试信息自动检测 C++ 共享库中的破坏性变更
各位编程专家,技术爱好者们,大家好!
今天,我们将深入探讨一个在 C++ 软件开发,尤其是在构建和维护大型共享库生态系统时至关重要但又极具挑战性的话题:二进制接口(ABI)的稳定性。我们将一起设计并理解一个强大的工具——C++ ABI 扫描仪,它能够利用 DWARF 调试信息,自动化地检测 C++ 共享库中的破坏性变更。
引言:ABI 稳定性为何如此重要?
在 C++ 的世界里,当我们谈论兼容性,通常会想到源代码兼容性。但是,对于共享库(如 .so 文件在 Linux 上,.dll 文件在 Windows 上),源代码兼容性只是冰山一角。更深层次、更隐蔽,也更具破坏性的是二进制接口(Application Binary Interface, ABI)。
ABI 定义了在操作系统层面,不同模块(例如应用程序和共享库,或两个共享库之间)如何相互交互的底层细节。这包括:
- 函数调用约定:参数如何传递,返回值如何处理,栈帧如何管理。
- 数据结构布局:类、结构体、联合体的内存布局、成员偏移、对齐方式。
- 名称修饰(Name Mangling):C++ 编译器如何将函数和变量的符号名称编码成唯一的字符串,以便链接器能够识别。
- 虚函数表(VTable)布局:虚函数和虚继承的实现细节。
- 异常处理机制:异常如何在二进制层面传播和捕获。
- 运行时类型信息(RTTI):
dynamic_cast和typeid的实现。
当一个共享库发布了新版本,如果其 ABI 发生了不兼容的变更,那么依赖于旧版本 ABI 的客户端应用程序或其它共享库将无法正常工作,即使它们的源代码没有变化,也可能在运行时崩溃、表现异常,甚至无法加载。这种问题往往难以调试,因为它们通常表现为内存访问错误、符号未定义或不匹配的链接错误,而且往往发生在部署阶段,而非开发阶段。
想象一下,你发布了一个广泛使用的 C++ 库。你的用户升级了你的库,然后他们的应用程序开始随机崩溃。这就是 ABI 破坏的噩梦。因此,拥有一种机制来自动检测这些破坏性变更,对于维护健康的软件生态系统至关重要。
C++ ABI 的复杂性与挑战
C++ 语言的强大和复杂性也使其 ABI 稳定性成为一个巨大的挑战。
- 名称修饰(Name Mangling):为了支持函数重载、命名空间和类成员函数,C++ 编译器会将函数和变量名修饰成编译器特定的格式。不同编译器(GCC, Clang, MSVC)之间的名称修饰规则可能不同,即使是同一编译器,在不同版本或不同编译选项下,也可能存在细微差异。
- 类布局与虚函数表(VTable):
- 成员变量顺序:添加、删除或重新排序非静态成员变量可能改变类的整体大小和成员偏移。
- 虚函数:添加、删除或重新排序虚函数,或改变虚函数的签名,会直接影响虚函数表(VTable)的布局,从而导致客户端调用错误的函数。
- 虚继承:虚继承的实现细节对类布局的影响更为复杂。
- 模板实例化:模板在编译时实例化,其生成的代码和数据结构也成为 ABI 的一部分。
- 内联函数:内联函数的具体实现通常不会导出到共享库的 ABI 中,但如果内联函数调用了非内联函数,或者其行为依赖于特定的类型布局,仍然可能间接影响 ABI。
- 编译器优化:不同的优化级别可能导致不同的代码生成,这在理论上不应影响 ABI,但在实践中,极端优化可能导致某些调试信息丢失或被转换,从而影响 ABI 扫描的准确性。
- 平台差异:不同的操作系统和 CPU 架构可能有不同的 ABI 约定(例如,数据类型的大小、对齐方式、寄存器使用等)。
什么构成一个破坏性的 ABI 变更?
以下是一些常见的 C++ ABI 破坏性变更示例:
| 变更类型 | 描述 | DWARF 影响示例 |
|---|---|---|
| 类布局变更 | ||
| 增加/删除非静态成员 | 改变了类的总大小和后续成员的偏移量。 | DW_AT_byte_size 变更,DW_AT_data_member_location (偏移量) 变更。 |
| 重新排序成员 | 改变了成员的偏移量。 | DW_AT_data_member_location (偏移量) 变更。 |
| 改变成员类型 | 改变了成员的大小或对齐方式,可能影响后续成员的偏移。 | DW_AT_type 引用类型变更,可能导致 DW_AT_data_member_location 和 DW_AT_byte_size 变更。 |
| 虚函数变更 | ||
| 增加虚函数 | 改变了虚函数表的布局,以及 vptr 后续成员的偏移量。 |
DW_TAG_subprogram (标记为虚函数) 的 DW_AT_virtuality 属性可能出现,或者其在 DW_TAG_class_type 中的相对顺序和偏移量 (DW_AT_vtable_elem_location) 变更。类的 DW_AT_byte_size 可能因 vptr 的添加而变更。 |
| 删除虚函数 | 同上,改变虚函数表的布局。 | 虚函数对应的 DW_TAG_subprogram 消失,或其在 VTable 中的索引变更。 |
| 改变虚函数签名 | 即使名称相同,参数或返回值类型不同也会导致 VTable 中的条目不匹配。 | DW_TAG_subprogram 的 DW_AT_type (返回类型) 或其参数 (DW_TAG_formal_parameter 的 DW_AT_type) 变更。 |
| 函数签名变更 | ||
| 改变参数类型/数量 | 影响函数调用约定和名称修饰。 | DW_TAG_subprogram 的参数列表 (DW_TAG_formal_parameter 的 DW_AT_type 或数量) 变更。名称修饰 (DW_AT_MIPS_linkage_name 或通过工具 demangle 后) 变更。 |
| 改变返回类型 | 影响函数调用约定。 | DW_TAG_subprogram 的 DW_AT_type (返回类型) 变更。 |
改变 const/noexcept |
可能影响名称修饰和编译器生成代码。 | DW_TAG_subprogram 可能增加/删除 DW_AT_const_value 或 DW_AT_noexcept 等属性,或影响名称修饰。 |
| 枚举类型变更 | ||
| 改变底层类型 | 改变了枚举的大小。 | DW_TAG_enumeration_type 的 DW_AT_type 引用底层类型变更,或 DW_AT_byte_size 变更。 |
| 改变枚举值 | 如果枚举值被直接用于 switch 语句或存储,改变可能导致逻辑错误。 | DW_TAG_enumerator 的 DW_AT_const_value 变更。 |
| 全局变量变更 | ||
| 改变类型 | 改变了全局变量的大小或布局。 | DW_TAG_variable 的 DW_AT_type 引用类型变更,或 DW_AT_byte_size 变更。 |
| 删除全局变量 | 客户端无法链接到该变量。 | DW_TAG_variable 消失。 |
为了应对这些挑战,我们需要一种能够深入到二进制层面,理解 C++ 结构和行为的自动化工具。DWARF 调试信息正是我们所需要的。
DWARF 调试信息:ABI 审查的基石
什么是 DWARF?
DWARF(Debugging With Arbitrary Record Formats)是一种广泛使用的调试文件格式,主要用于存储程序的调试信息。它是一个开放标准,独立于编程语言和处理器架构,被 GCC, Clang 等主流编译器广泛支持。当你在编译时使用 -g 选项(例如 g++ -g),编译器就会在可执行文件或共享库中嵌入 DWARF 信息。
DWARF 信息提供了一个抽象的、高级的程序表示,包括:
- 源代码文件和行号信息:用于将机器码映射回源代码。
- 变量信息:变量的名称、类型、作用域、内存位置。
- 函数信息:函数的名称、参数、返回类型、入口点、代码范围。
- 类型信息:复杂数据类型(如结构体、类、枚举、数组)的完整定义,包括成员、继承关系、大小和偏移量。
- 编译单元(Compilation Unit, CU):每个源文件通常对应一个 CU。
为什么 DWARF 对 ABI 扫描至关重要?
DWARF 提供了对 C++ 语言结构最接近源代码层面的二进制表示。它包含了构建 ABI 所需的几乎所有关键信息:
- 完整的类型定义:DWARF 可以详细描述一个类或结构体的所有成员、它们的类型、访问修饰符、偏移量,以及继承关系。这直接反映了数据结构的内存布局。
- 函数签名:DWARF 包含了函数的返回类型、参数类型、参数名称(可选),以及是否是虚函数、静态函数等属性。这有助于重建完整的函数签名。
- 名称修饰:虽然 DWARF 不直接存储名称修饰后的符号,但它提供了原始的 C++ 名称,我们可以通过工具(如
abi::__cxa_demangle)进行反向名称修饰,然后比较原始名称或在需要时重新修饰以匹配符号表。更重要的是,DWARF 通常包含DW_AT_MIPS_linkage_name或类似的属性,可以直接提供 mangled name。 - 内存位置和大小:DWARF 可以提供变量和类型在内存中的大小(
DW_AT_byte_size)和成员相对于基址的偏移量(DW_AT_data_member_location),这对于检测布局变化至关重要。 - 虚函数表信息:某些 DWARF 实现会包含虚函数表布局的相关信息,或者通过分析虚函数定义和继承关系来推断。
DWARF 的基本结构
DWARF 数据以一系列调试信息条目(Debugging Information Entries, DIEs)组织。每个 DIE 代表程序中的一个实体,例如一个编译单元、一个函数、一个变量、一个类型等。每个 DIE 都有一个标签(Tag)来标识其类型(例如 DW_TAG_class_type 表示一个类,DW_TAG_subprogram 表示一个函数),以及一系列属性(Attributes)来描述该实体的特征(例如 DW_AT_name 表示名称,DW_AT_type 引用其类型,DW_AT_byte_size 表示大小)。
使用 objdump -g 探索 DWARF
在深入编程之前,我们可以使用 objdump -g 命令来初步了解 DWARF 信息:
# 假设你有一个简单的 C++ 文件 example.cpp:
// class MyClass {
// public:
// int a;
// virtual void foo() {}
// double b;
// };
// void func(MyClass& obj) {}
//
// 编译: g++ -g -shared -o libexample.so example.cpp
objdump -g libexample.so | less
在输出中,你会看到类似这样的结构:
<1><b>: Abbrev Number: 1 (DW_TAG_compile_unit)
<c> DW_AT_producer : (GCC) 11.4.0
<12> DW_AT_language : DW_LANG_C_plus_plus
<13> DW_AT_name : example.cpp
<1e> DW_AT_stmt_list : 0x0
<22> DW_AT_comp_dir : /home/user/abi_scanner_project
<2f> DW_AT_low_pc : 0x1140
<37> DW_AT_high_pc : 0x1175
<3b> DW_AT_ranges : 0x0
<3f> DW_AT_GNU_ODR_signature: 0x17c09f3e4b786449
<2><43>: Abbrev Number: 2 (DW_TAG_base_type)
<44> DW_AT_byte_size : 8
<45> DW_AT_encoding : DW_ATE_signed_char
<46> DW_AT_name : long int
<2><4f>: Abbrev Number: 3 (DW_TAG_base_type)
<50> DW_AT_byte_size : 4
<51> DW_AT_encoding : DW_ATE_signed_char
<52> DW_AT_name : int
...
<2><130>: Abbrev Number: 10 (DW_TAG_class_type)
<131> DW_AT_name : MyClass
<139> DW_AT_byte_size : 24 // MyClass的大小,可能包含vptr
<13c> DW_AT_decl_file : 1
<13d> DW_AT_decl_line : 1
<140> DW_AT_MIPS_linkage_name: _ZTS7MyClass // mangled name
<141> DW_AT_GNU_ODR_signature: 0x2287f39423c8e317
<145> DW_TAG_member
<146> DW_AT_name : a
<147> DW_AT_type : <0x53> // 引用int类型
<14b> DW_AT_decl_file : 1
<14c> DW_AT_decl_line : 3
<14d> DW_AT_data_member_location: 8 // 偏移量,因为vptr是8字节
<14e> DW_TAG_subprogram // 虚函数foo
<14f> DW_AT_name : foo
<150> DW_AT_linkage_name: _ZN7MyClass3fooEv // mangled name
<154> DW_AT_decl_file : 1
<155> DW_AT_decl_line : 4
<156> DW_AT_low_pc : 0x1140
<15e> DW_AT_high_pc : 0x1145
<162> DW_AT_frame_base : 16 bit block: 0x9c
<164> DW_AT_virtuality : DW_VIRTUALITY_virtual
<165> DW_AT_external : 1
<166> DW_AT_calling_convention: DW_CC_C_plus_plus
<167> DW_AT_declaration : 0
<167> DW_AT_type : <0x4f> // 引用void类型
<168> DW_TAG_member
<169> DW_AT_name : b
<16a> DW_AT_type : <0x62> // 引用double类型
<16e> DW_AT_decl_file : 1
<16f> DW_AT_decl_line : 5
<170> DW_AT_data_member_location: 16 // 偏移量,int a (4) + padding + double b (8)
从上面的输出中,我们可以清晰地看到 MyClass 的定义,包括其大小、成员 a 和 b 的类型和偏移量,以及虚函数 foo 的存在和其虚属性。这些正是我们构建 ABI 扫描仪的关键数据。
ABI 扫描仪的架构与核心流程
一个高效的 C++ ABI 扫描仪通常包含以下四个核心阶段:
- DWARF 信息解析与抽取:从旧版本和新版本的共享库中加载 ELF/Mach-O 文件,并解析其中的 DWARF 调试信息。
- 构建规范化的 ABI 表示模型:将解析出的原始 DWARF 信息转换为一种语言无关、编译器无关的中间表示(IR),我们称之为 ABI 表示模型。这个模型应该能够捕捉所有与 ABI 相关的 C++ 结构特征。
- ABI 差异比较算法:对比旧版本和新版本的 ABI 表示模型,识别出所有差异,并根据预定义的规则判断这些差异是否构成 ABI 破坏。
- 生成详细的 ABI 变更报告:将检测到的 ABI 变更以人类可读的格式输出,指出具体的变更类型、位置及其潜在影响。
+---------------------+ +---------------------+
| 旧版本共享库 (.so/.dll) | | 新版本共享库 (.so/.dll) |
| (Old Binary) | | (New Binary) |
+----------+----------+ +----------+----------+
| |
v v
+---------------------+ +---------------------+
| DWARF 信息解析器 | | DWARF 信息解析器 |
| (DWARF Parser) | | (DWARF Parser) |
| - libdwarf / elfutils | | - libdwarf / elfutils |
| - 遍历DIE, 提取属性 | | - 遍历DIE, 提取属性 |
+----------+----------+ +----------+----------+
| |
v v
+---------------------+ +---------------------+
| 旧版本 ABI 表示模型 | | 新版本 ABI 表示模型 |
| (Old ABI Representation) | | (New ABI Representation) |
| - C++ 类结构 | | - C++ 类结构 |
| - 规范化 (去修饰, 统一类型) | | - 规范化 (去修饰, 统一类型) |
+----------+----------+ +----------+----------+
| |
+---------------------------------+
|
v
+---------------------+
| ABI 差异比较算法 |
| (ABI Diff Algorithm)|
| - 结构化递归比较 |
| - 识别 ADDED/REMOVED/MODIFIED |
| - 判断破坏性变更 |
+----------+----------+
|
v
+---------------------+
| ABI 变更报告生成器 |
| (Report Generator) |
| - 人类可读格式 |
| - 变更类型与影响 |
+---------------------+
第一阶段:DWARF 信息的解析与抽取
这一阶段是整个扫描仪的基础。我们需要一个能够读取 ELF/Mach-O 文件并解析 DWARF 信息的库。在 Linux/Unix 环境下,常用的选择有:
libdwarf:一个跨平台的 DWARF 访问库,提供了相对完整的 DWARF API。elfutils:GNU 项目的一部分,提供了用于处理 ELF 文件和 DWARF 信息的工具和库。llvm::DWARF库:如果你的项目已经依赖 LLVM,这是一个不错的选择。
这里我们以 libdwarf 为例,因为它是一个独立的 DWARF 库,并且提供了相对清晰的 API。
核心步骤:
- 打开 ELF 文件并初始化 DWARF 访问:使用
dwarf_init函数。 - 遍历编译单元(CU):每个源文件通常对应一个 CU。使用
dwarf_next_cu_header。 - 获取 CU 的根 DIE:每个 CU 都有一个根 DIE,所有属于该 CU 的调试信息都以树状结构挂载在其下。使用
dwarf_cu_die_root。 - 递归遍历 DIE 树:从根 DIE 开始,使用
dwarf_child和dwarf_sibling遍历其所有子节点和兄弟节点。 - 提取 DIE 的标签和属性:对于每个 DIE,获取其
DW_TAG(例如DW_TAG_class_type)和所有DW_AT属性(例如DW_AT_name,DW_AT_type,DW_AT_byte_size)。
代码示例(概念性):
#include <libdwarf.h>
#include <libelf.h>
#include <fcntl.h>
#include <unistd.h>
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <memory>
// 前向声明 ABI 表示模型中的类
class AbiType;
class AbiClass;
class AbiFunction;
class AbiMember;
// 辅助函数:获取属性值
std::string get_string_attribute(Dwarf_Die die, Dwarf_Half attr_code) {
Dwarf_Attribute attr;
Dwarf_Error error;
if (dwarf_attr(die, attr_code, &attr, &error) == DW_DLV_OK) {
char *str;
if (dwarf_formstring(attr, &str, &error) == DW_DLV_OK) {
std::string result(str);
dwarf_dealloc(nullptr, str, DW_DLA_STRING); // libdwarf 1.0.0+ 推荐
return result;
}
}
return "";
}
Dwarf_Unsigned get_unsigned_attribute(Dwarf_Die die, Dwarf_Half attr_code) {
Dwarf_Attribute attr;
Dwarf_Error error;
Dwarf_Unsigned val;
if (dwarf_attr(die, attr_code, &attr, &error) == DW_DLV_OK) {
if (dwarf_formudata(attr, &val, &error) == DW_DLV_OK) {
return val;
}
}
return 0;
}
// 递归处理 DIE
void process_die(Dwarf_Die die, int indent = 0) {
Dwarf_Error error;
Dwarf_Half tag;
if (dwarf_tag(die, &tag, &error) != DW_DLV_OK) {
return;
}
std::string name = get_string_attribute(die, DW_AT_name);
if (name.empty()) {
name = "(anonymous)";
}
// 示例:打印类和函数信息
if (tag == DW_TAG_class_type || tag == DW_TAG_structure_type) {
Dwarf_Unsigned byte_size = get_unsigned_attribute(die, DW_AT_byte_size);
std::cout << std::string(indent * 2, ' ') << "Class/Struct: " << name << " (Size: " << byte_size << " bytes)" << std::endl;
} else if (tag == DW_TAG_subprogram) {
std::string mangled_name = get_string_attribute(die, DW_AT_MIPS_linkage_name); // GCC
if (mangled_name.empty()) {
mangled_name = get_string_attribute(die, DW_AT_linkage_name); // Clang, older GCC
}
std::cout << std::string(indent * 2, ' ') << "Function: " << name << " (Mangled: " << mangled_name << ")" << std::endl;
} else if (tag == DW_TAG_member) {
Dwarf_Off type_offset = 0;
Dwarf_Attribute attr;
if (dwarf_attr(die, DW_AT_type, &attr, &error) == DW_DLV_OK) {
Dwarf_Die type_die;
if (dwarf_formref_die(attr, &type_die, &error) == DW_DLV_OK) {
// Here, you would resolve type_die to get the member's type name
// For simplicity, we just print the offset
Dwarf_Unsigned member_offset = get_unsigned_attribute(die, DW_AT_data_member_location);
std::cout << std::string(indent * 2, ' ') << "Member: " << name << " (Offset: " << member_offset << ")" << std::endl;
}
}
}
// 遍历子 DIE
Dwarf_Die child_die;
if (dwarf_child(die, &child_die, &error) == DW_DLV_OK) {
process_die(child_die, indent + 1);
Dwarf_Die sibling_die;
while (dwarf_siblingof(child_die, &sibling_die, &error) == DW_DLV_OK) {
process_die(sibling_die, indent);
child_die = sibling_die;
}
}
}
// 主解析函数
bool parse_dwarf_info(const std::string& filename) {
int fd = open(filename.c_str(), O_RDONLY);
if (fd < 0) {
std::cerr << "Error opening file: " << filename << std::endl;
return false;
}
Dwarf_Debug dbg = nullptr;
Dwarf_Error error;
if (dwarf_init(fd, DW_DLC_READ, nullptr, nullptr, &dbg, &error) != DW_DLV_OK) {
std::cerr << "Error initializing DWARF: " << dwarf_errmsg(error) << std::endl;
close(fd);
return false;
}
Dwarf_Unsigned cu_header_length, abbrev_offset, next_cu_header;
Dwarf_Half version_stamp, address_size;
Dwarf_Die cu_die;
while (dwarf_next_cu_header(dbg, &cu_header_length, &version_stamp,
&abbrev_offset, &address_size, &next_cu_header,
&error) == DW_DLV_OK) {
if (dwarf_cu_die_root(dbg, &cu_die, &error) == DW_DLV_OK) {
std::cout << "Processing Compilation Unit..." << std::endl;
process_die(cu_die);
dwarf_dealloc(dbg, cu_die, DW_DLA_DIE);
}
}
dwarf_finish(dbg, &error);
close(fd);
return true;
}
// int main(int argc, char* argv[]) {
// if (argc < 2) {
// std::cerr << "Usage: " << argv[0] << " <shared_library>" << std::endl;
// return 1;
// }
// parse_dwarf_info(argv[1]);
// return 0;
// }
关键 DWARF Tag 和 Attribute 列表:
| DWARF Tag | 描述 | 对应 C++ 结构 |
|---|---|---|
DW_TAG_compile_unit |
编译单元 | .cpp 文件 |
DW_TAG_class_type |
类类型 | class |
DW_TAG_structure_type |
结构体类型 | struct |
DW_TAG_union_type |
联合体类型 | union |
DW_TAG_enumeration_type |
枚举类型 | enum |
DW_TAG_base_type |
基本类型 | int, char, double 等 |
DW_TAG_pointer_type |
指针类型 | T* |
DW_TAG_reference_type |
引用类型 | T& |
DW_TAG_array_type |
数组类型 | T[] |
DW_TAG_subprogram |
函数/方法 | 函数、类成员函数 |
DW_TAG_member |
类/结构体成员 | 类或结构体中的成员变量 |
DW_TAG_formal_parameter |
函数形参 | 函数的参数 |
DW_TAG_variable |
全局/静态/局部变量 | 全局变量、静态变量 |
DW_TAG_typedef |
类型别名 | typedef 或 using |
DW_TAG_inheritance |
继承关系 | 类的继承(基类) |
| DWARF Attribute | 描述 | 对应 C++ 结构信息 |
|---|---|---|
DW_AT_name |
实体名称 | 类名、函数名、变量名、成员名等 |
DW_AT_type |
引用另一个 DIE,表示其类型 | 变量的类型、函数返回类型、参数类型等 |
DW_AT_byte_size |
实体在内存中的大小(字节) | 类型的大小、变量的大小 |
DW_AT_encoding |
基本类型的编码方式 | 有符号/无符号整数、浮点数等 |
DW_AT_data_member_location |
成员变量相对于其类实例基址的偏移量 | 类的成员变量在内存中的具体位置 |
DW_AT_virtuality |
函数的虚属性(虚函数、纯虚函数) | virtual, pure virtual |
DW_AT_external |
实体是否为外部可见(全局符号) | extern 变量,非静态函数 |
DW_AT_linkage_name |
链接器使用的名称(通常是修饰后的名称) | C++ mangled name |
DW_AT_MIPS_linkage_name |
另一个常见的链接器名称属性(GCC) | C++ mangled name (GCC specific) |
DW_AT_decl_file |
声明所在的文件索引 | 源文件路径 |
DW_AT_decl_line |
声明所在的行号 | 源文件行号 |
DW_AT_const_value |
常量的值(用于枚举器、静态常量) | enum 值,constexpr 变量 |
DW_AT_accessibility |
成员的访问权限 | public, protected, private |
第二阶段:构建规范化的 ABI 表示模型
原始的 DWARF 信息虽然详细,但它包含了大量的调试细节,并且其结构是树状的,不便于直接进行对比。我们需要将其转换为一个高度抽象、规范化的 C++ ABI 表示模型。这个模型应该:
- 语言和编译器无关:尽可能去除 DWARF 特有或编译器特有的细节。
- 扁平化或简化结构:将复杂的 DWARF 树结构转换为更容易管理的 C++ 对象图。
- 具备唯一标识符:每个可比较的 ABI 实体(类、函数、类型)都应该有一个规范化的、稳定的唯一标识符。
- 包含所有 ABI 关键信息:包括类型大小、成员偏移、函数签名、虚函数属性等。
我们可以定义一系列 C++ 类来表示这些 ABI 实体:
#include <string>
#include <vector>
#include <map>
#include <set>
#include <memory>
#include <algorithm>
#include <numeric> // For std::accumulate
// 前向声明
class AbiType;
class AbiClass;
class AbiFunction;
class AbiMember;
class AbiEnum;
class AbiEnumerator;
class AbiGlobalVariable;
// 规范化名称:处理命名空间、模板参数等
std::string normalize_name(const std::string& name) {
// 实际实现会更复杂,可能涉及demangle,移除不必要的修饰符等
return name; // 简化处理
}
// ---------------------- 基础类型 ----------------------
enum class TypeKind {
Unknown,
Base,
Pointer,
Reference,
Array,
Class,
Struct,
Union,
Enum,
FunctionPtr,
Typedef
// ... 更多类型
};
class AbiType {
public:
std::string name; // 规范化后的类型名称
TypeKind kind = TypeKind::Unknown;
size_t byte_size = 0;
size_t alignment = 0; // 实际需要从DWARF中推断或获取
// DWARF offset 用于内部引用解析,不作为ABI模型的一部分
Dwarf_Off dwarf_offset = 0;
AbiType(std::string n, TypeKind k, size_t bs = 0, Dwarf_Off offset = 0)
: name(normalize_name(std::move(n))), kind(k), byte_size(bs), dwarf_offset(offset) {}
// 规范化 ID 用于比较
virtual std::string get_canonical_id() const {
return std::to_string(static_cast<int>(kind)) + ":" + name + ":" + std::to_string(byte_size);
}
virtual bool operator==(const AbiType& other) const {
return get_canonical_id() == other.get_canonical_id();
}
virtual bool operator!=(const AbiType& other) const { return !(*this == other); }
virtual bool is_compatible_with(const AbiType& other) const { return *this == other; } // 简化,实际可能更复杂
};
class AbiBaseType : public AbiType {
public:
// DW_ATE_signed_char, DW_ATE_float etc.
Dwarf_Half encoding = 0;
AbiBaseType(std::string n, size_t bs, Dwarf_Half enc, Dwarf_Off offset)
: AbiType(std::move(n), TypeKind::Base, bs, offset), encoding(enc) {}
std::string get_canonical_id() const override {
return AbiType::get_canonical_id() + ":" + std::to_string(encoding);
}
};
class AbiPointerType : public AbiType {
public:
std::shared_ptr<AbiType> pointed_to_type;
AbiPointerType(std::shared_ptr<AbiType> pt_type, Dwarf_Off offset)
: AbiType(pt_type->name + "*", TypeKind::Pointer, sizeof(void*), offset), pointed_to_type(std::move(pt_type)) {}
std::string get_canonical_id() const override {
return AbiType::get_canonical_id() + ":" + pointed_to_type->get_canonical_id();
}
};
class AbiReferenceType : public AbiType {
public:
std::shared_ptr<AbiType> referenced_type;
AbiReferenceType(std::shared_ptr<AbiType> ref_type, Dwarf_Off offset)
: AbiType(ref_type->name + "&", TypeKind::Reference, sizeof(void*), offset), referenced_type(std::move(ref_type)) {}
std::string get_canonical_id() const override {
return AbiType::get_canonical_id() + ":" + referenced_type->get_canonical_id();
}
};
// ---------------------- 类/结构体/联合体 ----------------------
class AbiMember {
public:
std::string name;
std::shared_ptr<AbiType> type;
Dwarf_Unsigned offset = 0; // 成员相对于类实例基址的偏移量
Dwarf_Half accessibility = DW_ACCESS_public; // public, protected, private
AbiMember(std::string n, std::shared_ptr<AbiType> t, Dwarf_Unsigned off, Dwarf_Half acc)
: name(normalize_name(std::move(n))), type(std::move(t)), offset(off), accessibility(acc) {}
std::string get_canonical_id() const {
return name + ":" + std::to_string(offset) + ":" + type->get_canonical_id();
}
bool operator==(const AbiMember& other) const {
return name == other.name && offset == other.offset && accessibility == other.accessibility && *type == *other.type;
}
};
class AbiClass : public AbiType {
public:
std::vector<AbiMember> members;
std::vector<std::shared_ptr<AbiFunction>> methods;
std::vector<std::shared_ptr<AbiType>> base_classes; // 继承的基类
bool has_vtable = false; // 是否包含虚函数表
AbiClass(std::string n, TypeKind k, size_t bs, Dwarf_Off offset)
: AbiType(std::move(n), k, bs, offset) {
if (k != TypeKind::Class && k != TypeKind::Struct && k != TypeKind::Union) {
throw std::runtime_error("Invalid type kind for AbiClass");
}
}
// 类布局的规范化 ID,考虑成员顺序、类型、偏移
std::string get_canonical_id() const override {
std::string id = AbiType::get_canonical_id();
for (const auto& base : base_classes) {
id += ":B" + base->get_canonical_id();
}
for (const auto& member : members) {
id += ":M" + member.get_canonical_id();
}
for (const auto& method : methods) {
// 只考虑 ABI 相关的虚函数和公开方法
if (method->is_virtual || method->accessibility == DW_ACCESS_public) {
id += ":F" + method->get_canonical_id();
}
}
return id;
}
};
// ---------------------- 函数 ----------------------
class AbiParameter {
public:
std::string name; // 参数名可能不同,但类型必须一致
std::shared_ptr<AbiType> type;
bool is_const = false;
AbiParameter(std::string n, std::shared_ptr<AbiType> t, bool is_c)
: name(normalize_name(std::move(n))), type(std::move(t)), is_const(is_c) {}
std::string get_canonical_id() const {
return type->get_canonical_id() + (is_const ? "_const" : "");
}
bool operator==(const AbiParameter& other) const {
return is_const == other.is_const && *type == *other.type; // 参数名不影响ABI
}
};
class AbiFunction : public AbiType {
public:
std::string mangled_name; // 链接器名称
std::shared_ptr<AbiType> return_type;
std::vector<AbiParameter> parameters;
Dwarf_Half accessibility = DW_ACCESS_public;
bool is_virtual = false;
bool is_pure_virtual = false;
bool is_static = false;
bool is_const_method = false; // 成员函数是否为const
bool is_noexcept = false; // 是否noexcept (DWARF通常不直接提供)
AbiFunction(std::string n, std::string mn, std::shared_ptr<AbiType> rt, Dwarf_Off offset)
: AbiType(std::move(n), TypeKind::FunctionPtr, 0, offset), // FunctionPtr kind for simplified type system
mangled_name(std::move(mn)), return_type(std::move(rt)) {}
// 函数签名的规范化 ID
std::string get_canonical_id() const override {
std::string id = name + ":" + return_type->get_canonical_id();
for (const auto& param : parameters) {
id += ":" + param.get_canonical_id();
}
id += ":" + std::to_string(static_cast<int>(accessibility));
if (is_virtual) id += ":virtual";
if (is_pure_virtual) id += ":pure_virtual";
if (is_static) id += ":static";
if (is_const_method) id += ":const_method";
if (is_noexcept) id += ":noexcept"; // 仅当DWARF能提供时
return id;
}
};
// ---------------------- 枚举 ----------------------
class AbiEnumerator {
public:
std::string name;
Dwarf_Unsigned value;
AbiEnumerator(std::string n, Dwarf_Unsigned v) : name(std::move(n)), value(v) {}
std::string get_canonical_id() const {
return name + "=" + std::to_string(value);
}
bool operator==(const AbiEnumerator& other) const {
return name == other.name && value == other.value;
}
};
class AbiEnum : public AbiType {
public:
std::vector<AbiEnumerator> enumerators;
std::shared_ptr<AbiType> underlying_type; // 底层类型,如 int, long
AbiEnum(std::string n, size_t bs, std::shared_ptr<AbiType> ut, Dwarf_Off offset)
: AbiType(std::move(n), TypeKind::Enum, bs, offset), underlying_type(std::move(ut)) {}
std::string get_canonical_id() const override {
std::string id = AbiType::get_canonical_id() + ":" + underlying_type->get_canonical_id();
for (const auto& en : enumerators) {
id += ":" + en.get_canonical_id();
}
return id;
}
};
// ---------------------- 全局变量 ----------------------
class AbiGlobalVariable {
public:
std::string name;
std::string mangled_name;
std::shared_ptr<AbiType> type;
Dwarf_Off dwarf_offset = 0; // DWARF offset for internal reference
AbiGlobalVariable(std::string n, std::string mn, std::shared_ptr<AbiType> t, Dwarf_Off offset)
: name(normalize_name(std::move(n))), mangled_name(std::move(mn)), type(std::move(t)), dwarf_offset(offset) {}
std::string get_canonical_id() const {
return name + ":" + type->get_canonical_id();
}
bool operator==(const AbiGlobalVariable& other) const {
return name == other.name && *type == *other.type;
}
};
// ---------------------- 整个 ABI 库 ----------------------
class AbiLibrary {
public:
std::string name;
std::map<std::string, std::shared_ptr<AbiClass>> classes; // Key: canonical ID
std::map<std::string, std::shared_ptr<AbiFunction>> functions; // Key: canonical ID
std::map<std::string, std::shared_ptr<AbiEnum>> enums; // Key: canonical ID
std::map<std::string, std::shared_ptr<AbiGlobalVariable>> global_variables; // Key: canonical ID
// 存储所有类型,通过 DWARF_OFF 索引,便于解析时的引用查找
std::map<Dwarf_Off, std::shared_ptr<AbiType>> all_types_by_dwarf_offset;
AbiLibrary(std::string n) : name(std::move(n)) {}
void add_type(Dwarf_Off offset, std::shared_ptr<AbiType> type) {
all_types_by_dwarf_offset[offset] = type;
// 根据类型种类添加到相应的map中
if (type->kind == TypeKind::Class || type->kind == TypeKind::Struct || type->kind == TypeKind::Union) {
classes[type->get_canonical_id()] = std::static_pointer_cast<AbiClass>(type);
} else if (type->kind == TypeKind::Enum) {
enums[type->get_canonical_id()] = std::static_pointer_cast<AbiEnum>(type);
}
// 对于函数和全局变量,需要在解析时直接构建并添加到相应的map中,因为它们的ID可能不是纯粹基于TypeKind
}
void add_function(std::shared_ptr<AbiFunction> func) {
functions[func->get_canonical_id()] = std::move(func);
}
void add_global_variable(std::shared_ptr<AbiGlobalVariable> var) {
global_variables[var->get_canonical_id()] = std::move(var);
}
};
// 辅助函数:将 DWARF 修饰名反修饰为 C++ 可读名
std::string demangle_cpp_name(const std::string& mangled_name) {
int status;
char* demangled_name = abi::__cxa_demangle(mangled_name.c_str(), nullptr, nullptr, &status);
if (status == 0) {
std::string result(demangled_name);
free(demangled_name);
return result;
}
return mangled_name; // Demangling failed, return original
}
// 在 DWARF 解析阶段,当遇到 DW_AT_type 属性时,需要解析其引用的 DIE 来获取具体的类型信息。
// 这是一个复杂的过程,需要一个全局的类型映射表。
// 例如,一个简化版的类型解析器:
std::shared_ptr<AbiType> resolve_dwarf_type(Dwarf_Die type_die, AbiLibrary& abi_lib, std::map<Dwarf_Off, std::shared_ptr<AbiType>>& type_cache) {
Dwarf_Error error;
Dwarf_Off die_offset;
if (dwarf_dieoffset(type_die, &die_offset, &error) != DW_DLV_OK) {
return nullptr;
}
// 检查缓存
if (type_cache.count(die_offset)) {
return type_cache[die_offset];
}
Dwarf_Half tag;
if (dwarf_tag(type_die, &tag, &error) != DW_DLV_OK) {
return nullptr;
}
std::string name = get_string_attribute(type_die, DW_AT_name);
if (name.empty()) {
name = "(anonymous_type_at_0x" + std::to_string(die_offset) + ")";
}
Dwarf_Unsigned byte_size = get_unsigned_attribute(type_die, DW_AT_byte_size);
std::shared_ptr<AbiType> resolved_type = nullptr;
switch (tag) {
case DW_TAG_base_type: {
Dwarf_Half encoding = get_unsigned_attribute(type_die, DW_AT_encoding);
resolved_type = std::make_shared<AbiBaseType>(name, byte_size, encoding, die_offset);
break;
}
case DW_TAG_pointer_type: {
Dwarf_Attribute ptr_to_attr;
if (dwarf_attr(type_die, DW_AT_type, &ptr_to_attr, &error) == DW_DLV_OK) {
Dwarf_Die pointed_to_die;
if (dwarf_formref_die(ptr_to_attr, &pointed_to_die, &error) == DW_DLV_OK) {
std::shared_ptr<AbiType> pointed_to = resolve_dwarf_type(pointed_to_die, abi_lib, type_cache);
if (pointed_to) {
resolved_type = std::make_shared<AbiPointerType>(pointed_to, die_offset);
}
}
}
break;
}
case DW_TAG_reference_type: {
Dwarf_Attribute ref_to_attr;
if (dwarf_attr(type_die, DW_AT_type, &ref_to_attr, &error) == DW_DLV_OK) {
Dwarf_Die referenced_die;
if (dwarf_formref_die(ref_to_attr, &referenced_die, &error) == DW_DLV_OK) {
std::shared_ptr<AbiType> referenced_to = resolve_dwarf_type(referenced_die, abi_lib, type_cache);
if (referenced_to) {
resolved_type = std::make_shared<AbiReferenceType>(referenced_to, die_offset);
}
}
}
break;
}
case DW_TAG_class_type:
case DW_TAG_structure_type:
case DW_TAG_union_type: {
TypeKind k = TypeKind::Unknown;
if (tag == DW_TAG_class_type) k = TypeKind::Class;
else if (tag == DW_TAG_structure_type) k = TypeKind::Struct;
else k = TypeKind::Union;
auto cls = std::make_shared<AbiClass>(name, k, byte_size, die_offset);
type_cache[die_offset] = cls; // 提前放入缓存,处理循环引用
// 递归解析成员和方法
Dwarf_Die child_die;
if (dwarf_child(type_die, &child_die, &error) == DW_DLV_OK) {
do {
Dwarf_Half child_tag;
if (dwarf_tag(child_die, &child_tag, &error) != DW_DLV_OK) continue;
if (child_tag == DW_TAG_member) {
std::string member_name = get_string_attribute(child_die, DW_AT_name);
Dwarf_Unsigned member_offset = get_unsigned_attribute(child_die, DW_AT_data_member_location);
Dwarf_Half accessibility = get_unsigned_attribute(child_die, DW_AT_accessibility);
Dwarf_Attribute member_type_attr;
if (dwarf_attr(child_die, DW_AT_type, &member_type_attr, &error) == DW_DLV_OK) {
Dwarf_Die member_type_die;
if (dwarf_formref_die(member_type_attr, &member_type_die, &error) == DW_DLV_OK) {
std::shared_ptr<AbiType> member_type = resolve_dwarf_type(member_type_die, abi_lib, type_cache);
if (member_type) {
cls->members.emplace_back(member_name, member_type, member_offset, accessibility);
}
}
}
} else if (child_tag == DW_TAG_subprogram) {
// 这是一个方法
std::string method_name = get_string_attribute(child_die, DW_AT_name);
std::string method_mangled_name = get_string_attribute(child_die, DW_AT_MIPS_linkage_name);
if (method_mangled_name.empty()) {
method_mangled_name = get_string_attribute(child_die, DW_AT_linkage_name);
}
Dwarf_Attribute ret_type_attr;
std::shared_ptr<AbiType> ret_type = nullptr;
if (dwarf_attr(child_die, DW_AT_type, &ret_type_attr, &error) == DW_DLV_OK) {
Dwarf_Die ret_type_die;
if (dwarf_formref_die(ret_type_attr, &ret_type_die, &error) == DW_DLV_OK) {
ret_type = resolve_dwarf_type(ret_type_die, abi_lib, type_cache);
}
}
auto method = std::make_shared<AbiFunction>(method_name, method_mangled_name, ret_type, die_offset);
method->accessibility = get_unsigned_attribute(child_die, DW_AT_accessibility);
method->is_virtual = (get_unsigned_attribute(child_die, DW_AT_virtuality) == DW_VIRTUALITY_virtual);
// DWARF doesn't directly indicate pure virtual, often it's virtual with no low_pc.
// For pure virtual, typically DW_AT_declaration is 1 and no low_pc.
if (method->is_virtual && get_unsigned_attribute(child_die, DW_AT_declaration) == 1 && get_unsigned_attribute(child_die, DW_AT_low_pc) == 0) {
method->is_pure_virtual = true;
}
method->is_static = (get_unsigned_attribute(child_die, DW_AT_external) == 0 && method_mangled_name.find("ZN") == 0); // Simplified heuristic
// 解析参数
Dwarf_Die param_die;
if (dwarf_child(child_die, ¶m_die, &error) == DW_DLV_OK) {
do {
Dwarf_Half param_tag;
if (dwarf_tag(param_die, ¶m_tag, &error) != DW_DLV_OK) continue;
if (param_tag == DW_TAG_formal_parameter) {
std::string param_name = get_string_attribute(param_die, DW_AT_name);
Dwarf_Attribute param_type_attr;
if (dwarf_attr(param_die, DW_AT_type, ¶m_type_attr, &error) == DW_DLV_OK) {
Dwarf_Die param_type_die;
if (dwarf_formref_die(param_type_attr, ¶m_type_die, &error) == DW_DLV_OK) {
std::shared_ptr<AbiType> param_type = resolve_dwarf_type(param_type_die, abi_lib, type_cache);
if (param_type) {
method->parameters.emplace_back(param_name, param_type, false); // is_const not directly in DWARF for params
}
}
}
}
} while (dwarf_siblingof(param_die, ¶m_die, &error) == DW_DLV_OK);
}
cls->methods.push_back(method);
abi_lib.add_function(method); // 也作为顶层函数添加,方便查找
} else if (child_tag == DW_TAG_inheritance) {
Dwarf_Attribute base_type_attr;
if (dwarf_attr(child_die, DW_AT_type, &base_type_attr, &error) == DW_DLV_OK) {
Dwarf_Die base_type_die;
if (dwarf_formref_die(base_type_attr, &base_type_die, &error) == DW_DLV_OK) {
std::shared_ptr<AbiType> base_type = resolve_dwarf_type(base_type_die, abi_lib, type_cache);
if (base_type) {
cls->base_classes.push_back(base_type);
}
}
}
}
} while (dwarf_siblingof(child_die, &child_die, &error) == DW_DLV_OK);
}
// 对成员和方法进行排序,确保比较时顺序一致
std::sort(cls->members.begin(), cls->members.end(), [](const AbiMember& a, const AbiMember& b) {
return a.offset < b.offset; // 优先按偏移量排序
});
std::sort(cls->methods.begin(), cls->methods.end(), [](const std::shared_ptr<AbiFunction>& a, const std::shared_ptr<AbiFunction>& b) {
return a->mangled_name < b->mangled_name; // 按修饰名排序
});
resolved_type = cls;
break;
}
case DW_TAG_enumeration_type: {
auto enm = std::make_shared<AbiEnum>(name, byte_size, nullptr, die_offset);
type_cache[die_offset] = enm; // 提前放入缓存
Dwarf_Attribute underlying_type_attr;
if (dwarf_attr(type_die, DW_AT_type, &underlying_type_attr, &error) == DW_DLV_OK) {
Dwarf_Die underlying_type_die;
if (dwarf_formref_die(underlying_type_attr, &underlying_type_die, &error) == DW_DLV_OK) {
enm->underlying_type = resolve_dwarf_type(underlying_type_die, abi_lib, type_cache);
}
}
Dwarf_Die child_die;
if (dwarf_child(type_die, &child_die, &error) == DW_DLV_OK) {
do {
Dwarf_Half child_tag;
if (dwarf_tag(child_die, &child_tag, &error) != DW_DLV_OK) continue;
if (child_tag == DW_TAG_enumerator) {
std::string enum_name = get_string_attribute(child_die, DW_AT_name);
Dwarf_Unsigned enum_value = get_unsigned_attribute(child_die, DW_AT_const_value);
enm->enumerators.emplace_back(enum_name, enum_value);
}
} while (dwarf_siblingof(child_die, &child_die, &error) == DW_DLV_OK);
}
resolved_type = enm;
break;
}
case DW_TAG_typedef: {
Dwarf_Attribute typedef_type_attr;
if (dwarf_attr(type_die, DW_AT_type, &typedef_type_attr, &error) == DW_DLV_OK) {
Dwarf_Die actual_type_die;
if (dwarf_formref_die(typedef_type_attr, &actual_type_die, &error) == DW_DLV_OK) {
resolved_type = resolve_dwarf_type(actual_type_die, abi_lib, type_cache);
// Typedefs themselves don't change ABI if the underlying type is the same.
// We can just return the underlying type directly for comparison purposes
// or store the typedef name for reporting.
if (resolved_type) {
// For canonical ID, we might want to use the typedef's name but its underlying type's properties.
// Here, we'll simplify and just return the underlying type for comparison.
// A more robust solution might wrap the underlying type with the typedef's name.
return resolved_type;
}
}
}
break;
}
// ... 处理其他 DW_TAG
default:
resolved_type = std::make_shared<AbiType>(name, TypeKind::Unknown, byte_size, die_offset);
break;
}
if (resolved_type) {
type_cache[die_offset] = resolved_type; // 放入缓存
abi_lib.add_type(die_offset, resolved_type); // 添加到库中
}
return resolved_type;
}
// 改进后的 DWARF 解析函数
bool parse_dwarf_info_to_abi_lib(const std::string& filename, AbiLibrary& abi_lib) {
int fd = open(filename.c_str(), O_RDONLY);
if (fd < 0) {
std::cerr << "Error opening file: " << filename << std::endl;
return false;
}
Dwarf_Debug dbg = nullptr;
Dwarf_Error error;
if (dwarf_init(fd, DW_DLC_READ, nullptr, nullptr, &dbg, &error) != DW_DLV_OK) {
std::cerr << "Error initializing DWARF: " << dwarf_errmsg(error) << std::endl;
close(fd);
return false;
}
Dwarf_Unsigned cu_header_length, abbrev_offset, next_cu_header;
Dwarf_Half version_stamp, address_size;
Dwarf_Die cu_die;
// A temporary map to store all resolved types by their DWARF offset
// This is crucial for resolving DW_AT_type references which can point to any DIE
std::map<Dwarf_Off, std::shared_ptr<AbiType>> type_cache;
// First pass: Collect all type definitions (classes, enums, base types, etc.)
// This is necessary to resolve forward declarations and cyclic dependencies
std::vector<Dwarf_Die> all_dies_to_process;
while (dwarf_next_cu_header(dbg, &cu_header_length, &version_stamp,
&abbrev_offset, &address_size, &next_cu_header,
&error) == DW_DLV_OK) {
if (dwarf_cu_die_root(dbg, &cu_die, &error) == DW_DLV_OK) {
Dwarf_Die current_die = cu_die;
std::vector<Dwarf_Die> die_stack;
die_stack.push_back(current_die);
while (!die_stack.empty()) {
Dwarf_Die d = die_stack.back();
die_stack.pop_back();
all_dies_to_process.push_back(d);
Dwarf_Die child_die;
if (dwarf_child(d, &child_die, &error) == DW_DLV_OK) {
die_stack.push_back(child_die);
Dwarf_Die sibling_die = child_die;
while (dwarf_siblingof(sibling_die, &sibling_die, &error) == DW_DLV_OK) {
die_stack.push_back(sibling_die);
}
}
}
}
}
// Process all types first
for(Dwarf_Die d : all_dies_to_process) {
Dwarf_Half tag;
if (dwarf_tag(d, &tag, &error) != DW_DLV_OK) continue;
// Process type-defining DIEs
if (tag == DW_TAG_class_type || tag == DW_TAG_structure_type ||
tag == DW_TAG_union_type || tag == DW_TAG_enumeration_type ||
tag == DW_TAG_base_type || tag == DW_TAG_pointer_type ||
tag == DW_TAG_reference_type || tag == DW_TAG_typedef ||
tag == DW_TAG_array_type) { // Add array types etc.
Dwarf_Off die_offset;
if (dwarf_dieoffset(d, &die_offset, &error) == DW_DLV_OK && type_cache.find(die_offset) == type_cache.end()) {
resolve_dwarf_type(d, abi_lib, type_cache);
}
}
}
// Second pass: Process functions and global variables, using the resolved types
for (Dwarf_Die d : all_dies_to_process) {
Dwarf_Half tag;
if (dwarf_tag(d, &tag, &error) != DW_DLV_OK) continue;
if (tag == DW_TAG_subprogram) {
// Only process top-level functions, methods are handled within AbiClass
// We can check if its parent is a CU or namespace to determine if it's a top-level function
Dwarf_Die parent_die;
if (dwarf_die_parent(d, &parent_die, &error) == DW_DLV_OK) {
Dwarf_Half parent_tag;
if (dwarf_tag(parent_die, &parent_tag, &error) == DW_DLV_OK &&
(parent_tag == DW_TAG_compile_unit || parent_tag == DW_TAG_namespace)) {
std::string func_name = get_string_attribute(d, DW_AT_name);
std::string func_mangled_name = get_string_attribute(d, DW_AT_MIPS_linkage_name);
if (func_mangled_name.empty()) {
func_mangled_name = get_string_attribute(d, DW_AT_linkage_name);
}
Dwarf_Attribute ret_type_attr;
std::shared_ptr<AbiType> ret_type = nullptr;
if (dwarf_attr(d, DW_AT_type, &ret_type_attr, &error) == DW_DLV_OK) {
Dwarf_Die ret_type_die;
if (dwarf_formref_die(ret_type_attr, &ret_type_die, &error) == DW_DLV_OK) {
ret_type = resolve_dwarf_type(ret_type_die, abi_lib, type_cache);
}
}
auto func = std::make_shared<AbiFunction>(func_name, func_mangled_name, ret_type, 0);
// Populate parameters as done for methods
Dwarf_Die param_die;
if (dwarf_child(d, ¶m_die, &error) == DW_DLV_OK) {
do {
Dwarf_Half param_tag;
if (dwarf_tag(param_die, ¶m_tag, &error) != DW_DLV_OK) continue;
if (param_tag == DW_TAG_formal_parameter) {
std::string param_name = get_string_attribute(param_die, DW_AT_name);
Dwarf_Attribute param_type_attr;
if (dwarf_attr(param_die, DW_AT_type, ¶m_type_attr, &error) == DW_DLV_OK) {
Dwarf_Die param_type_die;
if (dwarf_formref_die(param_type_attr, ¶m_type_die, &error) == DW_DLV_OK) {
std::shared_ptr<AbiType> param_type = resolve_dwarf_type(param_type_die, abi_lib, type_cache);
if (param_type) {
func->parameters.emplace_back(param_name, param_type, false);
}
}
}
}
} while (dwarf_siblingof(param_die, ¶m_die, &error) == DW_DLV_OK);
}
abi_lib.add_function(func);
}
}
} else if (tag == DW_TAG_variable) {
// 处理顶层全局变量
Dwarf_Die parent_die;
if (dwarf_die_parent(d, &parent_die, &error) == DW_DLV_OK) {
Dwarf_Half parent_tag;
if (dwarf_tag(parent_die, &parent_tag, &error) == DW_DLV_OK &&
(parent_tag == DW_TAG_compile_unit || parent_tag == DW_TAG_namespace)) {
std::string var_name = get_string_attribute(d, DW_AT_name);
std::string var_mangled_name = get_string_attribute(d, DW_AT_MIPS_linkage_name);
if (var_mangled_name.empty()) {
var_mangled_name = get_string_attribute(d, DW_AT_linkage_name);
}
Dwarf_Attribute var_type_attr;
std::shared_ptr<AbiType> var_type = nullptr;
if (dwarf_attr(d, DW_AT_type, &var_type_attr, &error) == DW_DLV_OK) {
Dwarf_Die var_type_die;
if (dwarf_formref_die(var_type_attr, &var_type_die, &error) == DW_DLV_OK) {
var_type = resolve_dwarf_type(var_type_die, abi_lib, type_cache);
}
}
if (var_type) {
abi_lib.add_global_variable(std::make_shared<AbiGlobalVariable>(var_name, var_mangled_name, var_type, 0));
}
}
}
}
}
dwarf_finish(dbg, &error);
close(fd);
return true;
}
关键点:
std::shared_ptr<AbiType>:使用智能指针来管理类型对象的生命周期,并支持多态。dwarf_offset:在AbiType中保留 DWARFDwarf_Off是一个好习惯,它作为 DWARF 内部的唯一 ID,可以在解析时用于快速查找和处理循环引用。resolve_dwarf_type函数:这是类型解析的核心。它递归地解析类型引用(DW_AT_type),并使用type_cache来避免重复解析和处理循环类型依赖(例如struct A { B* b; }; struct B { A* a; };)。- 规范化 ID (
get_canonical_id):每个 ABI 实体都应该有一个根据其所有 ABI 相关特性生成的唯一 ID。这确保了即使名称或 DWARF offset 不同,如果 ABI 相同,也能得到相同的 ID。例如,类的 ID 应包含其大小、成员偏移、虚函数签名等。 - 两阶段解析:首先收集所有 DIE 并处理所有类型定义,然后处理函数和变量。这是为了确保在解析函数或变量的类型时,所有依赖的类型都已经被解析并放入
type_cache。
第三阶段:ABI 差异比较算法
有了旧版本和新版本的规范化 ABI 表示模型(AbiLibrary 实例),接下来就是进行比较。这个阶段需要一个结构化的、递归的比较算法。
比较策略:
- 基于规范化 ID 进行查找:对于旧版本中的每个实体(类、函数、枚举、全局变量),尝试在新版本中通过其规范化 ID 进行查找。
- 识别变更类型:
- ADDED:新版本中有,旧版本中没有。
- REMOVED:旧版本中有,新版本中没有。
- MODIFIED:新旧版本都存在(通过 ID 匹配),但其内部细节(例如类成员顺序、函数参数类型、大小等)不一致。
- 递归比较:对于复杂类型(如类),需要递归地比较其成员、方法和基类。
- 判断破坏性:根据 C++ ABI 规则,判断一个变更是否是破坏性的。例如:
- 类大小或成员偏移量变更:破坏性。
- 虚函数签名变更、增加/删除虚函数:破坏性。
- 公共函数签名变更:破坏性。
- 枚举底层类型变更、枚举值变更:破坏性。
- 全局变量类型变更:破坏性。
- 私有成员或非虚私有方法变更(不影响类大小):通常非破坏性。
- 添加非虚公共方法:通常非破坏性(但客户端如果通过
dlopen/dlsym查找符号,可能会发现新符号)。
代码示例(概念性):
#include <map>
#include <string>
#include <vector>
#include <iostream>
// 定义变更类型
enum class AbiChangeType {
Added,
Removed,
Modified
};
// 定义变更报告条目
struct AbiChangeReportEntry {
AbiChangeType type;
std::string entity_type; // e.g., "Class", "Function", "Member"
std::string entity_name; // Original C++ name
std::string old_canonical_id;
std::string new_canonical_id;
std::string description;
bool is_breaking = false; // 是否是破坏性变更
std::string to_string() const {
std::string type_str;
switch (type) {
case AbiChangeType::Added: type_str = "ADDED"; break;
case AbiChangeType::Removed: type_str = "RE