You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ARM64平台下如何提取C语言当前函数的调用方参数?

ARM64平台下栈回溯函数的参数提取实现方案

核心思路

基于DWARF调试信息与ARM64调用帧规则,通过解析函数参数的位置描述,结合当前寄存器与栈帧上下文,读取参数值。该方案完全适配-g编译的不同优化级别(O0/O2)。

具体实现步骤

1. 关联栈帧与DWARF函数调试信息

每个栈帧对应一个函数的执行上下文,首先通过栈帧的PC值定位到对应的DWARF编译单元(CU)和函数调试信息条目(DIE):

  • 遍历.debug_info段中的所有CU,找到包含当前PC地址范围的CU;
  • 在该CU中查找标记为DW_TAG_subprogram的DIE,匹配PC对应的函数。

2. 提取函数参数的DWARF元信息

从函数DIE的子条目里筛选出DW_TAG_formal_parameter类型的DIE,每个条目对应一个函数参数:

  • 读取DW_AT_name属性,获取参数名称;
  • 读取DW_AT_location属性,获取参数的存储位置描述(可能是直接的DW_OP指令序列,或者指向.debug_loc段的偏移);
  • 读取DW_AT_type属性,获取参数的类型DIE,用于后续值的解析(比如区分int、double、指针等)。

3. 计算调用帧基址(CFA)

参数的栈位置通常基于调用帧地址(CFA)计算,需要从.eh_frame或.debug_frame段的调用帧信息(CFI)中获取计算规则:

  • 根据当前PC找到对应的CFI条目;
  • 解析CFI中的DW_CFA_def_cfa指令,ARM64下常见规则为CFA = x29 + offset(x29为帧指针FP);
  • 结合当前栈帧的x29、SP寄存器值,计算出CFA的实际地址。

4. 解析参数位置并读取值

根据DW_AT_location的内容,分情况处理:

  • 寄存器存储:如果位置是DW_OP_regx指令(比如DW_OP_reg0对应x0寄存器),直接读取对应寄存器的值;
  • 栈存储:如果是DW_OP_fbreg指令,提取偏移值,用CFA加上偏移得到参数的内存地址,读取对应长度的数据;
  • 位置列表:如果DW_AT_location指向.debug_loc段,需根据当前PC找到列表中对应的位置条目,再按上述方式读取值。

5. 适配不同编译优化级别

  • O0级别:参数通常会同时存在寄存器和栈帧的参数备份区,DWARF会记录栈上的位置,直接读取即可;
  • O2级别:参数可能保留在寄存器中,或被优化复用,但-g编译会保留足够的DWARF信息追踪参数的实际存储位置,严格按DWARF的位置描述读取即可。

关键代码示例

// 简化的参数信息结构体
typedef struct {
    const char* name;
    // DWARF位置指令序列的起始地址
    const uint8_t* loc_ops;
    // 参数类型对应的DIE
    Dwarf_Die type_die;
} ParamInfo;

// 从函数DIE提取参数列表
void collect_function_params(Dwarf_Die func_die, ParamInfo* params, size_t* count) {
    *count = 0;
    Dwarf_Die child_die;
    if (dwarf_child(func_die, &child_die) != 0) return;

    do {
        if (dwarf_tag(&child_die) == DW_TAG_formal_parameter) {
            params[*count].name = dwarf_attr_string(&child_die, DW_AT_name);
            params[*count].loc_ops = dwarf_attr_location(&child_die, DW_AT_location);
            params[*count].type_die = *dwarf_attr_die(&child_die, DW_AT_type);
            (*count)++;
        }
    } while (dwarf_siblingof(&child_die, &child_die) == 0);
}

// 解析位置并读取参数值(以64位整数/指针为例)
uint64_t read_param_value(const uint8_t* loc_ops, uint64_t cfa, uint64_t regs[31]) {
    uint8_t op = loc_ops[0];
    switch (op) {
        case DW_OP_reg0 ... DW_OP_reg30: {
            uint32_t reg_idx = op - DW_OP_reg0;
            return regs[reg_idx];
        }
        case DW_OP_fbreg: {
            int32_t offset = *(int32_t*)(loc_ops + 1);
            return *(uint64_t*)(cfa + offset);
        }
        // 扩展处理其他DW_OP指令(如DW_OP_plus_uconst等)
        default:
            return 0;
    }
}

可行性说明

该方案完全可行,GDB等调试工具的info args功能正是基于DWARF调试信息与CFI调用帧规则实现的。只要确保正确解析.debug_info、.debug_loc、.eh_frame等段的内容,结合ARM64的寄存器与栈帧上下文,就能在不同编译优化级别下准确提取函数参数值。

内容的提问来源于stack exchange,提问作者Jordan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 06:24:49