dwarf 栈解析格式小结

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

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.41.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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值