DWARF 堆栈展开详细实现原理分析
概述
本文档详细解释 Android libunwindstack 中 DWARF CFI (Call Frame Information) 堆栈展开的实现原理,重点分析 DwarfSection::Step()、GetCfaLocationInfo() 和 Eval() 方法的实现机制。
DWARF CFI 基础知识
什么是 DWARF CFI?
DWARF CFI (Call Frame Information) 是一种描述函数调用帧布局的标准格式,用于在运行时恢复上一帧的寄存器状态。它包含在 ELF 文件的 .eh_frame 或 .debug_frame 段中。
核心概念
-
CFA (Canonical Frame Address) - 规范帧地址
- 指上一帧的栈指针(SP)值
- 是恢复其他寄存器的基准地址
-
FDE (Frame Description Entry) - 帧描述条目
- 描述一个函数范围内的堆栈布局
- 包含该函数的起始地址、结束地址和展开规则
-
CIE (Common Information Entry) - 公共信息条目
- 多个 FDE 共享的公共信息
- 包含架构相关的默认规则
-
Location Rules - 位置规则
- 描述如何计算每个寄存器的值
- 可以是:寄存器值、内存地址、表达式等
完整调用链
Unwinder::Unwind()
└─ elf->Step(rel_pc, regs, process_memory, finished, is_signal_frame)
└─ interface_->Step(pc, regs, process_memory, finished, is_signal_frame)
└─ eh_frame->Step(pc, regs, process_memory, finished, is_signal_frame)
├─ GetFdeFromPc(pc) // 查找 FDE
├─ GetCfaLocationInfo(pc, fde, ...) // 获取 CFA 位置信息
└─ Eval(cie, process_memory, loc_regs, regs, finished) // 执行寄存器恢复
第一步:DwarfSection::Step() 方法详解
方法签名
bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory,
bool* finished, bool* is_signal_frame)
实现流程
bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory,
bool* finished, bool* is_signal_frame) {
// 1. 查找缓存中的位置信息
auto it = loc_regs_.upper_bound(pc);
// 2. 如果缓存未命中,需要重新解析
if (it == loc_regs_.end() || pc < it->second.pc_start) {
last_error_.code = DWARF_ERROR_NONE;
// 3. 根据 PC 查找对应的 FDE
const DwarfFde* fde = GetFdeFromPc(pc);
if (fde == nullptr || fde->cie == nullptr) {
last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
return false;
}
// 4. 获取该 PC 位置的所有寄存器位置信息
DwarfLocations loc_regs;
if (!GetCfaLocationInfo(pc, fde, &loc_regs, regs->Arch())) {
return false;
}
loc_regs.cie = fde->cie;
// 5. 将解析结果存入缓存(以 pc_end 为键)
it = loc_regs_.emplace(loc_regs.pc_end, std::move(loc_regs)).first;
}
// 6. 检查是否为信号帧
*is_signal_frame = it->second.cie->is_signal_frame;
// 7. 执行寄存器恢复
return Eval(it->second.cie, process_memory, it->second, regs, finished);
}
关键步骤解析
步骤 1-2:缓存查找
auto it = loc_regs_.upper_bound(pc);
if (it == loc_regs_.end() || pc < it->second.pc_start) {
// 缓存未命中,需要重新解析
}
作用:
-
loc_regs_是一个std::map<uint64_t, DwarfLocations>,键是pc_end(该位置信息有效的结束地址) -
upper_bound(pc)查找第一个键大于pc的条目 - 如果找到的条目的
pc_start <= pc < pc_end,说明缓存命中 - 否则需要重新解析
为什么使用 pc_end 作为键?
- 因为同一个函数内的不同 PC 可能有不同的展开规则
- 使用
pc_end可以快速定位到包含当前 PC 的规则范围
步骤 3:查找 FDE
const DwarfFde* fde = GetFdeFromPc(pc);
GetFdeFromPc() 的实现原理:
const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) {
// 1. 在 FDE 列表中二分查找
// FDE 列表按函数起始地址排序
for (const auto& fde : fdes_) {
if (pc >= fde->pc_start && pc < fde->pc_end) {
return fde.get();
}
}
return nullptr;
}
FDE 结构:
struct DwarfFde {
uint64_t pc_start; // 函数起始地址
uint64_t pc_end; // 函数结束地址
DwarfCie* cie; // 关联的 CIE
std::vector<uint8_t> instructions; // CFI 指令序列
};
作用:找到包含当前 PC 的函数对应的 FDE。
步骤 4:获取 CFA 位置信息
DwarfLocations loc_regs;
if (!GetCfaLocationInfo(pc, fde, &loc_regs, regs->Arch())) {
return false;
}
这是最复杂的步骤,下面详细解释。
第二步:GetCfaLocationInfo() 方法详解
方法签名
bool DwarfSection::GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde,
DwarfLocations* loc_regs, ArchEnum arch)
核心原理
GetCfaLocationInfo() 的作用是解析 DWARF CFI 指令,计算出在当前 PC 位置时,如何恢复上一帧的所有寄存器。
DWARF CFI 指令系统
DWARF CFI 使用基于栈的状态机来编码展开规则:
- 初始状态:从 CIE 中获取初始规则
- 执行指令:按顺序执行 FDE 中的指令,直到覆盖当前 PC
- 最终状态:得到当前 PC 位置的所有寄存器恢复规则
主要 CFI 指令
| 指令 | 作用 | 示例 |
|---|---|---|
DW_CFA_def_cfa |
定义 CFA 的位置 | DW_CFA_def_cfa reg, offset |
DW_CFA_def_cfa_register |
修改 CFA 的基寄存器 | DW_CFA_def_cfa_register reg |
DW_CFA_def_cfa_offset |
修改 CFA 的偏移量 | DW_CFA_def_cfa_offset offset |
DW_CFA_offset |
定义寄存器保存在 CFA+offset | DW_CFA_offset reg, offset |
DW_CFA_register |
定义寄存器等于另一个寄存器 | DW_CFA_register reg1, reg2 |
DW_CFA_expression |
使用表达式计算寄存器位置 | DW_CFA_expression reg, expr |
DW_CFA_advance_loc |
前进到下一个地址范围 | DW_CFA_advance_loc delta |
DW_CFA_restore |
恢复寄存器的默认规则 | DW_CFA_restore reg |
GetCfaLocationInfo() 实现流程(简化)
bool DwarfSection::GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde,
DwarfLocations* loc_regs, ArchEnum arch) {
// 1. 初始化:从 CIE 复制初始规则
DwarfLocations cie_loc_regs;
if (!GetCieLocationInfo(fde->cie, &cie_loc_regs, arch)) {
return false;
}
// 2. 设置初始状态
*loc_regs = cie_loc_regs;
loc_regs->pc_start = fde->pc_start;
uint64_t cur_pc = fde->pc_start;
// 3. 解析 FDE 中的 CFI 指令
Memory* memory = process_memory_;
const uint8_t* cur_instructions = fde->instructions.data();
const uint8_t* end_instructions = cur_instructions + fde->instructions.size();
while (cur_instructions < end_instructions) {
uint8_t cfi_value = *cur_instructions++;
// 解析指令操作码
uint8_t opcode = cfi_value & 0x3f;
uint8_t operand = (cfi_value >> 6) & 0x3;
switch (opcode) {
case DW_CFA_advance_loc: {
// 前进到新的地址范围
uint64_t delta = ReadULEB128(&cur_instructions);
cur_pc += delta;
// 如果已经超过目标 PC,停止解析
if (cur_pc > pc) {
loc_regs->pc_end = cur_pc;
return true;
}
break;
}
case DW_CFA_def_cfa: {
// 定义 CFA: CFA = reg + offset
uint64_t reg = ReadULEB128(&cur_instructions);
uint64_t offset = ReadULEB128(&cur_instructions);
DwarfLocation loc;
loc.type = DWARF_LOCATION_REGISTER;
loc.values[0] = reg; // 基寄存器
loc.values[1] = offset; // 偏移量
(*loc_regs)[CFA_REG] = loc;
break;
}
case DW_CFA_def_cfa_register: {
// 修改 CFA 的基寄存器
uint64_t reg = ReadULEB128(&cur_instructions);
auto it = loc_regs->find(CFA_REG);
if (it != loc_regs->end()) {
it->second.values[0] = reg; // 只修改寄存器,保持偏移不变
}
break;
}
case DW_CFA_def_cfa_offset: {
// 修改 CFA 的偏移量
uint64_t offset = ReadULEB128(&cur_instructions);
auto it = loc_regs->find(CFA_REG);
if (it != loc_regs->end()) {
it->second.values[1] = offset; // 只修改偏移,保持寄存器不变
}
break;
}
case DW_CFA_offset: {
// 定义寄存器保存在 CFA + offset
uint64_t reg = ReadULEB128(&cur_instructions);
uint64_t offset = ReadULEB128(&cur_instructions);
DwarfLocation loc;
loc.type = DWARF_LOCATION_OFFSET;
loc.values[0] = offset;
(*loc_regs)[reg] = loc;
break;
}
case DW_CFA_register: {
// 定义寄存器等于另一个寄存器
uint64_t reg1 = ReadULEB128(&cur_instructions);
uint64_t reg2 = ReadULEB128(&cur_instructions);
DwarfLocation loc;
loc.type = DWARF_LOCATION_REGISTER;
loc.values[0] = reg2;
(*loc_regs)[reg1] = loc;
break;
}
case DW_CFA_expression: {
// 使用表达式计算寄存器位置
uint64_t reg = ReadULEB128(&cur_instructions);
uint64_t expr_length = ReadULEB128(&cur_instructions);
DwarfLocation loc;
loc.type = DWARF_LOCATION_VAL_EXPRESSION;
loc.values[0] = expr_length;
// 保存表达式字节码
loc.expression = std::vector<uint8_t>(cur_instructions,
cur_instructions + expr_length);
(*loc_regs)[reg] = loc;
cur_instructions += expr_length;
break;
}
case DW_CFA_restore: {
// 恢复寄存器的默认规则(从 CIE 中)
uint64_t reg = ReadULEB128(&cur_instructions);
auto cie_it = cie_loc_regs.find(reg);
if (cie_it != cie_loc_regs.end()) {
(*loc_regs)[reg] = cie_it->second;
} else {
loc_regs->erase(reg);
}
break;
}
// ... 其他指令 ...
}
}
// 4. 设置有效范围
loc_regs->pc_end = fde->pc_end;
return true;
}
DwarfLocations 数据结构
struct DwarfLocation {
uint8_t type; // 位置类型
std::vector<uint64_t> values; // 参数值
std::vector<uint8_t> expression; // 表达式字节码(如果类型是 EXPRESSION)
};
typedef std::map<uint32_t, DwarfLocation> DwarfLocations;
// 键:寄存器编号
// 值:如何恢复该寄存器的规则
位置类型(DwarfLocation::type)
| 类型 | 含义 | 如何计算 |
|---|---|---|
DWARF_LOCATION_REGISTER |
寄存器值 | reg[values[0]] + values[1] |
DWARF_LOCATION_OFFSET |
内存偏移 | CFA + values[0] |
DWARF_LOCATION_VAL_OFFSET |
值偏移 |
CFA + values[0](作为值,不是地址) |
DWARF_LOCATION_VAL_EXPRESSION |
表达式值 | 执行表达式字节码 |
DWARF_LOCATION_EXPRESSION |
表达式地址 | 执行表达式字节码得到地址,然后读取 |
DWARF_LOCATION_UNDEFINED |
未定义 | 寄存器值未知 |
DWARF_LOCATION_SAME_VALUE |
相同值 | 寄存器值不变(等于当前帧的值) |
示例:解析过程
假设有以下函数:
void function_a() {
// PC = 0x1000
int local_var;
function_b(); // PC = 0x1010
// PC = 0x1014
}
对应的 DWARF CFI 指令可能是:
CIE:
DW_CFA_def_cfa: SP, 0 // CFA = SP + 0
DW_CFA_offset: LR, -8 // LR 保存在 CFA - 8
FDE for function_a (0x1000-0x1020):
DW_CFA_advance_loc: 16 // 前进到 0x1010
DW_CFA_def_cfa_offset: 16 // CFA = SP + 16(分配了栈空间)
DW_CFA_offset: R0, -16 // R0 保存在 CFA - 16
DW_CFA_advance_loc: 4 // 前进到 0x1014
DW_CFA_def_cfa_offset: 0 // CFA = SP + 0(恢复栈)
当 pc = 0x1010(调用 function_b 时)时,解析过程:
-
初始状态(从 CIE):
CFA = SP + 0LR = [CFA - 8]
-
执行
DW_CFA_advance_loc: 16:-
cur_pc = 0x1000 + 16 = 0x1010,匹配目标 PC
-
-
执行
DW_CFA_def_cfa_offset: 16:CFA = SP + 16
-
执行
DW_CFA_offset: R0, -16:R0 = [CFA - 16]
-
最终结果:
CFA = SP + 16LR = [CFA - 8] = [SP + 16 - 8] = [SP + 8]R0 = [CFA - 16] = [SP + 16 - 16] = [SP]
第三步:Eval() 方法详解
方法签名
template <typename AddressType>
bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie,
Memory* regular_memory,
const DwarfLocations& loc_regs,
Regs* regs,
bool* finished)
核心原理
Eval() 方法的作用是根据解析出的位置规则,实际执行寄存器恢复,将当前帧的寄存器更新为上一帧的寄存器值。
实现流程详解
template <typename AddressType>
bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie,
Memory* regular_memory,
const DwarfLocations& loc_regs,
Regs* regs,
bool* finished) {
// 1. 类型转换
RegsImpl<AddressType>* cur_regs =
reinterpret_cast<RegsImpl<AddressType>*>(regs);
// 2. 验证返回地址寄存器
if (cie->return_address_register >= cur_regs->total_regs()) {
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
return false;
}
// 3. 查找 CFA 的位置规则
auto cfa_entry = loc_regs.find(CFA_REG);
if (cfa_entry == loc_regs.end()) {
last_error_.code = DWARF_ERROR_CFA_NOT_DEFINED;
return false;
}
// 4. 清除 DEX PC(Java 层相关)
cur_regs->set_dex_pc(0);
// 5. 重置伪寄存器
regs->ResetPseudoRegisters();
// 6. 准备评估信息
EvalInfo<AddressType> eval_info{
.loc_regs = &loc_regs,
.cie = cie,
.regular_memory = regular_memory,
.regs_info = RegsInfo<AddressType>(cur_regs)
};
// 7. 计算 CFA 值
const DwarfLocation* cfa_loc = &cfa_entry->second;
AddressType cfa_value;
switch (cfa_loc->type) {
case DWARF_LOCATION_REGISTER: {
// CFA = reg[values[0]] + values[1]
if (cfa_loc->values[0] >= cur_regs->total_regs()) {
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
return false;
}
cfa_value = (*cur_regs)[cfa_loc->values[0]]; // 读取基寄存器
cfa_value += cfa_loc->values[1]; // 加上偏移
break;
}
case DWARF_LOCATION_VAL_EXPRESSION: {
// CFA = 执行表达式的结果
if (!EvalExpression(*cfa_loc, regular_memory, &cfa_value,
&eval_info.regs_info, nullptr)) {
return false;
}
break;
}
default:
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
return false;
}
eval_info.cfa = cfa_value;
// 8. 恢复所有寄存器
for (const auto& entry : loc_regs) {
uint32_t reg = entry.first;
// 跳过 CFA 寄存器(已经处理)
if (reg == CFA_REG) continue;
AddressType* reg_ptr;
if (reg >= cur_regs->total_regs()) {
// 伪寄存器处理
if (entry.second.type != DWARF_LOCATION_PSEUDO_REGISTER) {
continue; // 跳过未知寄存器
}
if (!eval_info.regs_info.regs->SetPseudoRegister(reg,
entry.second.values[0])) {
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
return false;
}
} else {
// 普通寄存器:获取保存位置并恢复
reg_ptr = eval_info.regs_info.Save(reg);
if (!EvalRegister(&entry.second, reg, reg_ptr, &eval_info)) {
return false;
}
}
}
// 9. 恢复返回地址(PC)
if (eval_info.return_address_undefined) {
cur_regs->set_pc(0); // 返回地址未定义,到达栈底
} else {
// 从返回地址寄存器读取 PC
cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
}
// 10. 检查是否到达栈底
*finished = (cur_regs->pc() == 0 && !cie->is_signal_frame) ? true : false;
// 11. 更新栈指针为 CFA
cur_regs->set_sp(eval_info.cfa);
return true;
}
关键步骤详解
步骤 7:计算 CFA 值
CFA 是恢复其他寄存器的基准,必须先计算出来。
情况 1:CFA = 寄存器 + 偏移
case DWARF_LOCATION_REGISTER:
cfa_value = (*cur_regs)[cfa_loc->values[0]]; // 读取基寄存器(通常是 SP)
cfa_value += cfa_loc->values[1]; // 加上偏移
break;
示例:
- 如果规则是
CFA = SP + 16 cfa_loc->values[0] = SP 的编号cfa_loc->values[1] = 16- 结果:
cfa_value = 当前 SP 值 + 16(上一帧的 SP)
情况 2:CFA = 表达式结果
case DWARF_LOCATION_VAL_EXPRESSION:
EvalExpression(*cfa_loc, regular_memory, &cfa_value,
&eval_info.regs_info, nullptr);
break;
表达式是 DWARF 字节码,需要解释执行。
步骤 8:恢复所有寄存器
对每个寄存器,根据其位置规则恢复值:
for (const auto& entry : loc_regs) {
uint32_t reg = entry.first;
const DwarfLocation& loc = entry.second;
AddressType* reg_ptr = eval_info.regs_info.Save(reg);
EvalRegister(&loc, reg, reg_ptr, &eval_info);
}
EvalRegister() 的实现:
template <typename AddressType>
bool DwarfSectionImpl<AddressType>::EvalRegister(
const DwarfLocation* loc, uint32_t reg, AddressType* reg_ptr,
EvalInfo<AddressType>* eval_info) {
switch (loc->type) {
case DWARF_LOCATION_OFFSET: {
// 寄存器保存在 CFA + offset
AddressType addr = eval_info->cfa + loc->values[0];
if (!eval_info->regular_memory->Read(addr, reg_ptr, sizeof(AddressType))) {
return false;
}
break;
}
case DWARF_LOCATION_REGISTER: {
// 寄存器等于另一个寄存器
if (loc->values[0] >= eval_info->regs_info.regs->total_regs()) {
return false;
}
*reg_ptr = (*eval_info->regs_info.regs)[loc->values[0]];
if (loc->values.size() > 1) {
*reg_ptr += loc->values[1]; // 加上偏移
}
break;
}
case DWARF_LOCATION_VAL_OFFSET: {
// 寄存器值 = CFA + offset(直接作为值,不是地址)
*reg_ptr = eval_info->cfa + loc->values[0];
break;
}
case DWARF_LOCATION_EXPRESSION: {
// 寄存器保存在表达式计算的地址
AddressType addr;
if (!EvalExpression(*loc, eval_info->regular_memory, &addr,
&eval_info->regs_info, nullptr)) {
return false;
}
if (!eval_info->regular_memory->Read(addr, reg_ptr, sizeof(AddressType))) {
return false;
}
break;
}
case DWARF_LOCATION_VAL_EXPRESSION: {
// 寄存器值 = 表达式的结果
if (!EvalExpression(*loc, eval_info->regular_memory, reg_ptr,
&eval_info->regs_info, nullptr)) {
return false;
}
break;
}
case DWARF_LOCATION_SAME_VALUE: {
// 寄存器值不变(保持当前帧的值)
// 不需要修改 reg_ptr
break;
}
case DWARF_LOCATION_UNDEFINED: {
// 寄存器值未定义,设置为 0 或保持原值
*reg_ptr = 0;
break;
}
default:
return false;
}
return true;
}
寄存器恢复示例
假设当前帧的状态:
SP = 0x7fff0000-
LR = 0x40001000(返回地址)
CFI 规则:
CFA = SP + 16LR = [CFA - 8]R0 = [CFA - 16]
执行过程:
-
计算 CFA:
cfa = SP + 16 = 0x7fff0000 + 16 = 0x7fff0010 -
恢复 LR:
lr_addr = CFA - 8 = 0x7fff0010 - 8 = 0x7fff0008 LR = memory[lr_addr] // 从内存读取 -
恢复 R0:
r0_addr = CFA - 16 = 0x7fff0010 - 16 = 0x7fff0000 R0 = memory[r0_addr] // 从内存读取 -
更新寄存器:
new_SP = CFA = 0x7fff0010 // 上一帧的 SP new_PC = LR = memory[0x7fff0008] // 返回地址
步骤 9:恢复返回地址(PC)
if (eval_info.return_address_undefined) {
cur_regs->set_pc(0); // 到达栈底
} else {
// 从返回地址寄存器读取 PC
cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
}
返回地址寄存器:
-
ARM64:
LR(X30) -
ARM:
LR(R14) -
x86_64: 通常保存在栈上,通过
DW_CFA_offset规则恢复 - x86: 同上
示例(ARM64):
-
cie->return_address_register = 30(LR 的编号) - 在步骤 8 中已经恢复了 LR 的值
-
cur_regs->set_pc((*cur_regs)[30])将 PC 设置为恢复后的 LR 值
步骤 10-11:更新 SP 和检查结束
*finished = (cur_regs->pc() == 0 && !cie->is_signal_frame) ? true : false;
cur_regs->set_sp(eval_info.cfa);
- 如果 PC 为 0 且不是信号帧,说明到达栈底
- 将 SP 更新为 CFA(上一帧的栈指针)
完整示例:一次完整的展开过程
场景
假设有以下调用链:
main() -> function_a() -> function_b() [当前在这里]
当前状态(function_b 中):
-
PC = 0x40002000(function_b 内部) SP = 0x7fff0000-
LR = 0x40001014(返回到 function_a 的地址)
步骤 1:查找 FDE
fde = GetFdeFromPc(0x40002000);
// 找到 function_b 的 FDE (0x40001ff0 - 0x40002050)
步骤 2:解析 CFI 规则
GetCfaLocationInfo(0x40002000, fde, &loc_regs, ARCH_ARM64);
解析结果:
CFA = SP + 16
LR = [CFA - 8]
R19 = [CFA - 16]
R20 = [CFA - 24]
步骤 3:执行寄存器恢复
Eval(cie, process_memory, loc_regs, regs, &finished);
执行过程:
-
计算 CFA:
cfa = SP + 16 = 0x7fff0000 + 16 = 0x7fff0010 -
恢复 LR:
lr_addr = 0x7fff0010 - 8 = 0x7fff0008 LR = memory[0x7fff0008] // 假设读取到 0x40001014 -
恢复 R19, R20:
R19 = memory[0x7fff0010 - 16] = memory[0x7fff0000] R20 = memory[0x7fff0010 - 24] = memory[0x7fffffe8] -
更新寄存器:
new_PC = LR = 0x40001014 // 返回到 function_a new_SP = CFA = 0x7fff0010 // 上一帧的 SP
结果
展开后的状态(function_a 中):
-
PC = 0x40001014(function_a 内部) SP = 0x7fff0010-
LR = memory[0x7fff0008](从栈上恢复) -
R19, R20已恢复
关键数据结构
DwarfLocation
struct DwarfLocation {
uint8_t type; // 位置类型
std::vector<uint64_t> values; // 参数值
std::vector<uint8_t> expression; // 表达式字节码(可选)
};
DwarfLocations
typedef std::map<uint32_t, DwarfLocation> DwarfLocations;
// 键:寄存器编号(CFA_REG 是特殊值)
// 值:如何恢复该寄存器
DwarfFde
struct DwarfFde {
uint64_t pc_start; // 函数起始地址
uint64_t pc_end; // 函数结束地址
DwarfCie* cie; // 关联的 CIE
std::vector<uint8_t> instructions; // CFI 指令序列
};
DwarfCie
struct DwarfCie {
uint8_t version; // CIE 版本
std::string augmentation; // 增强字符串
uint64_t code_alignment_factor; // 代码对齐因子
int64_t data_alignment_factor; // 数据对齐因子
uint64_t return_address_register; // 返回地址寄存器编号
std::vector<uint8_t> instructions; // 初始 CFI 指令
bool is_signal_frame; // 是否为信号帧
};
总结
DWARF 堆栈展开的核心原理:
- 查找 FDE:根据 PC 找到对应的函数描述
- 解析 CFI 指令:执行指令序列,得到当前 PC 位置的展开规则
- 计算 CFA:确定上一帧的栈指针位置
- 恢复寄存器:根据规则从内存或寄存器中恢复所有寄存器
- 更新 PC/SP:将 PC 设置为返回地址,SP 设置为 CFA
整个过程依赖于:
- DWARF CFI 指令系统:编码展开规则
- 进程内存访问:读取栈上的保存值
- 寄存器状态:当前帧的寄存器值
这使得 Unwinder 能够在不修改目标进程的情况下,完整地展开其调用栈。











网友评论