eh_frame
所有的dwarf都在eh_frame的节当中,具体格式如下:

一般一个cie对应多个fde,一个fde对应相应函数的寄存器恢复信息,不仅仅是栈信息。
当我们拿到eh_frame后,就需要解析cie和fde.
(http://dwarfstd.org/doc/DWARF4.pdf) 官方文档有很详细的东西,不适合入门感觉。根据结尾的例子来分别讲解cie和fde.
common information entry encoding
R0 =0;
R1 保存返回地址
R2-R3 临时寄存器
R4-R6 保存在栈上
R7 栈顶指针 (rsp)
table D.4中
1.R8 是返回地址
2.s 表示一样的值
3.u标识undefined rule
4.rN 寄存器规则
5.cN 偏移规则
6 a 架构规则
cie : common information entry encoding

cie length 如果是0xffffffff,则会有extend length 字段,标识为64位dwarf,但我看到的一般是32位dwarf,这里的位数和可执行文件的位数不对应。长度后面是结束cie的全部36长度的内容。
augmentation 用来表示cie
这里有详细解释。
https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
readelf -wF
Contents of the .eh_frame section:
00000000 00000014 00000000 CIE "zR" cf=1 df=-8 ra=16
LOC CFA ra
0000000000000000 rsp+8 c-8
00000018 0000001c 0000001c FDE cie=00000000 pc=35fb01ea60..35fb01ea88
LOC CFA rbx ra
00000035fb01ea60 rsp+8 u c-8
00000035fb01ea68 rsp+16 c-16 c-8
00000035fb01ea87 rsp+8 c-16 c-8
readelf -wf
Contents of the .eh_frame section:
00000000 00000014 00000000 CIE
Version: 1
Augmentation: "zR"
Code alignment factor: 1
Data alignment factor: -8
Return address column: 16
Augmentation data: 1b
DW_CFA_def_cfa: r7 (rsp) ofs 8
DW_CFA_offset: r16 (rip) at cfa-8
DW_CFA_nop
DW_CFA_nop
00000018 0000001c 0000001c FDE cie=00000000 pc=35fb01ea60..35fb01ea88
DW_CFA_advance_loc: 8 to 35fb01ea68
DW_CFA_def_cfa_offset: 16
DW_CFA_offset: r3 (rbx) at cfa-16
DW_CFA_advance_loc: 31 to 35fb01ea87
DW_CFA_def_cfa_offset: 8
DW_CFA_nop
DW_CFA_nop
DW_CFA_nop
DW_CFA_nop
DW_CFA_nop
DW_CFA_nop
DW_CFA_nop
通过对比得知, cfa = rsp + DW_CFA_def_cfa_offset; rbx = cfa - DW_CFA_offset;
frame description entry encoding

argument_data_length 该字段仅在CIE扩展字符串以“ z”开头的情况下出现。对应一个无符号LEB128,这是FDE扩展数据的总大小。这可用于跳过与无法识别的扩展字符关联的数据。
argumnet_data 如果CIE未将LSDA编码设置为DW_EH_PE_omit,则Augmentation Data中包含指向LSDA的指针,该指针由CIE指定编码(LSDA encoding)。
这里显示的没有扩展字符z的fde。


之后就是对instruction进行解码,然后状态恢复。
| instruction | Description |
|---|---|
| DW_CFA_advance_loc | Location += (delta * code_alignment_factor) |
| DW_CFA_advance_loc1 | Location += (delta * code_alignment_factor) DW_CFA_advance_loc1指令采用一个表示常量增量的单个ubyte操作数。除了增量操作数的编码和大小外,该指令与DW_CFA_advance_loc相同。 |
| DW_CFA_def_cfa | register = new register num; offset = new offset; CFA = register + offset |
| DW_CFA_def_cfa_offset | offset = new offset ; CFA = old register + offset ;DW_CFA_def_cfa_offset指令采用单个无符号LEB128操作数表示一个(未分解的)偏移量。 所需的操作是定义当前的CFA规则以使用提供的偏移量(但保留旧寄存器)。仅当当前CFA规则定义为使用寄存器和偏移量时,此操作才有效。 |
| DW_CFA_offset offset(N) | offset = (factored offset * data_alignment_factor); reg num = *(CFA + offset) ;DW_CFA_offset指令采用两个操作数:一个寄存器号(用操作码编码)和一个无符号的LEB128常量,表示因数偏移量。 所需的操作是将由寄存器编号指示的寄存器的规则更改为offset(N)规则,其中N的值是fac→redoffset⋅dataalignmentfac→r |
00000000 00000014 00000000 CIE "zR" cf=1 df=-8 ra=16
LOC CFA ra
0000000000000000 rsp+8 c-8
LOC CFA rbx ra
00000035fb01ea60 rsp+8 u c-8
00000035fb01ea68 rsp+16 c-16 c-8
00000035fb01ea87 rsp+8 c-16 c-8
DW_CFA_def_cfa: r7 (rsp) ofs 8
DW_CFA_offset: r16 (rip) at cfa-8
DW_CFA_advance_loc: 8 to 35fb01ea68
DW_CFA_def_cfa_offset: 16
DW_CFA_offset: r3 (rbx) at cfa-16
DW_CFA_advance_loc: 31 to 35fb01ea87
DW_CFA_def_cfa_offset: 8
R8 是返回地址
R7 栈顶指针 (rsp)
代码实现
对比了gcc gdb 和readelf,其中gcc的代码较友好。
gcc 4.8.5
static _Unwind_Reason_Code
uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs)
{
const struct dwarf_fde *fde;
const struct dwarf_cie *cie;
const unsigned char *aug, *insn, *end;
memset (fs, 0, sizeof (*fs));
context->args_size = 0;
context->lsda = 0;
if (context->ra == 0)
return _URC_END_OF_STACK;
fde = _Unwind_Find_FDE (context->ra + _Unwind_IsSignalFrame (context) - 1,
&context->bases);
if (fde == NULL)
{
#ifdef MD_FALLBACK_FRAME_STATE_FOR
/* 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. */
return MD_FALLBACK_FRAME_STATE_FOR (context, fs);
#else
return _URC_END_OF_STACK;
#endif
}
fs->pc = context->bases.func;
cie = get_cie (fde);
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 = (const unsigned char *) next_fde ((const struct dwarf_fde *) cie)

本文深入解析了EH_FRAME节中的CIE和FDE结构,详细阐述了DWARF格式下CFA(Common Frame Address)的计算方法,以及如何通过CFA恢复函数调用前的寄存器状态,对于理解程序栈帧和异常处理机制有重要价值。
1万+

被折叠的 条评论
为什么被折叠?



