c++ 异常处理(2)(二)

2014-11-24 11:40:51 · 作者: · 浏览: 11
ta: 00 00 00 00 00 00 00 00
#以下为字节码
DW_CFA_advance_loc: 1 to 00400ac9
DW_CFA_def_cfa_offset: 16
DW_CFA_offset: r6 at cfa-16
DW_CFA_advance_loc: 3 to 00400acc
DW_CFA_def_cfa_reg: r6
DW_CFA_nop
DW_CFA_nop
DW_CFA_nop
复制代码
对于由GCC编译出来的程序来说,CIE, FDE是其在unwind过程中恢复现场时所依赖的全部东西,而且是完备的,这里所说的恢复现场指的是恢复调用当前函数的函数的现场,比如,func1调用func2,然后我们可以在func2里通过查询CIE,FDE恢复func1的现场。
CIE,FDE存在于每一个需要处理异常的ELF文件中,当异常发生时,根据当前PC值调用dl_iterate_phdr()函数就可以把当前程序所加载的所有模块轮询一遍,从而找到该PC所在模块的eh_frame。
复制代码
for (n = info->dlpi_phnum; --n >= 0; phdr++)
{
if (phdr->p_type == PT_LOAD)
{
_Unwind_Ptr vaddr = phdr->p_vaddr + load_base;
if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz)
match = 1;
}
else if (phdr->p_type == PT_GNU_EH_FRAME)
p_eh_frame_hdr = phdr;
else if (phdr->p_type == PT_DYNAMIC)
p_dynamic = phdr;
}
复制代码
找到eh_frame也就找到CIE,找到了CIE也就可以去搜索相应的FDE。
找到FDE及CIE后,就可以从这两数据表中提取相关的信息,并执行DWARF 字节码,从而得到当前函数的调用函数的现场,参看如下用于重建函数帧的函数:
复制代码
static _Unwind_Reason_Code
uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs)
{
struct dwarf_fde *fde;
struct dwarf_cie *cie;
const unsigned char *aug, *insn, *end;
memset (fs, 0, sizeof (*fs));
context->args_size = 0;
context->lsda = 0;
// 根据context查找FDE。
fde = _Unwind_Find_FDE (context->ra - 1, &context->bases);
if (fde == NULL)
{
/* Couldn't find frame unwind info for this function. Try a
target-specific fallback mechanism. This will necessarily
not provide a personality routine or LSDA. */
#ifdef MD_FALLBACK_FRAME_STATE_FOR
MD_FALLBACK_FRAME_STATE_FOR (context, fs, success);
return _URC_END_OF_STACK;
success:
return _URC_NO_REASON;
#else
return _URC_END_OF_STACK;
#endif
}
fs->pc = context->bases.func;
// 获取对应的CIE.
cie = get_cie (fde);
// 提取出CIE中的信息,如personality routine的地址。
insn = extract_cie_info (cie, context, fs);
if (insn == NULL)
/* CIE contained unknown augmentation. */
return _URC_FATAL_PHASE1_ERROR;
/* First decode all the insns in the CIE. */
end = (unsigned char *) next_fde ((struct dwarf_fde *) cie);
// 执行dwarf字节码,从而恢复相应的寄存器的值。
execute_cfa_program (insn, end, context, fs);
// 定位到fde的相关数据
/* Locate augmentation for the fde. */
aug = (unsigned char *) fde + sizeof (*fde);
aug += 2 * size_of_encoded_value (fs->fde_encoding);
insn = NULL;
if (fs->saw_z)
{
_Unwind_Word i;
aug = read_uleb128 (aug, &i);
insn = aug + i;
}
// 读取language specific data的指针
if (fs->lsda_encoding != DW_EH_PE_omit)
aug = read_encoded_value (context, fs->lsda_encoding, aug,
(_Unwind_Ptr *) &context->lsda);
/* Then the insns in the FDE up to our target PC. */
if (insn == NULL)
insn = aug;
end = (unsigned char *) next_fde (fde);
// 执行FDE中的字节码。
execute_cfa_program (insn, end, context, fs);
return _URC_NO_REASON;
}
复制代码
通过如上的操作,unwinder就已经把调用函数的现场给重建起来了,这些现场信息包括:
复制代码
struct _Unwind_Context
{
void *reg[DWARF_FRAME_REGISTERS+1]; //必要的寄存器。
void *cfa; // canoniacl frame address, 前面提到过,基地址。
void *ra;/