---
title: "ELF文件-DWARF"
author: "Perrin Yong"
author_profile: https://www.pystone.net/profile/
published_by: "Perrin Yong"
canonical: https://www.pystone.net/notes/elf-dwarf-format/
type: note
content_role: unspecified
visibility: public
id_stability: rename-stable
source_path: "10-计算机、信息技术与工程/02-编程语言与运行时/ELF文件-DWARF.md"
content_hash: bb18448284e80f786b620a42fa8a8325b711af9a0270d18bb2666e7bf289a5d8
knowledge_version: 224c990773de.5fa8af6e39fa
site_commit: 224c990773de166d23a886306577dd90379529ce
notes_commit: 5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a
---
# ELF文件-DWARF
DWARF 是一种用于调试的标准化格式，全称为 "**Debugging With Attributed Record Formats**"。帮助调试器从编译后的二进制文件中恢复源代码级别的信息，比如变量名称、类型、函数调用栈等。

libunwind 在实现栈回溯时依赖 DWARF 来解析栈帧信息和寄存器状态。

**DWARF** 是存储在 **ELF 文件**中的一个（或多个）**section**，它与其他诸如可执行代码段（`.text` 段）、符号表（`.symtab` 段）等并列存在。

DWARF 文件的内容包括：
- **源代码中的函数和变量信息**：每个函数或变量在源代码中的名称、数据类型、作用域等。
- **程序的控制流和栈信息**：包括函数调用的栈帧结构、寄存器信息，方便回溯调用栈。
- **数据布局信息**：帮助调试器理解如何解析复杂的数据结构。

## DWARF的内容
- **Compilation Unit (CU)**：
    - 是 DWARF 文件的基本单元，每个编译单元描述了从某个源文件生成的调试信息。它包含了该源文件中的所有符号和类型定义。
- **Debug Information Entry (DIE)**：
    - DIE 是 DWARF 中的基本数据结构，每个 DIE 描述一个调试信息项，如一个变量、一个类型定义或一个函数。DIE 是分层结构的，父 DIE 可以包含多个子 DIE。
- **Call Frame Information (CFI)**：
    - CFI 是 DWARF 中的栈帧信息，用来描述函数调用过程中寄存器和栈帧如何变化。CFI 用于栈回溯，它记录了关于程序中每个函数如何设置栈帧、保存寄存器以及在函数返回时如何恢复这些寄存器的信息。
- **Location Expressions**：
    - DWARF 使用位置表达式（Location Expressions）来描述变量在内存或寄存器中的位置。通过解析这些表达式，调试器可以找到程序执行期间变量存储的位置。
- **Line Number Information**：
    - DWARF 还包含源代码行号信息，帮助调试器在二进制文件和源代码之间进行映射。每个函数和变量都可以映射到源代码的具体行号，便于调试时快速定位。

- 每个 **编译单元（Compilation Unit，CU）** 代表一个源文件。
- 每个 CU 包含多个 **调试信息条目（Debug Information Entry, DIE）** ，这些条目描述了该文件中的变量、类型、函数等。

DWARF就是elf文件中的一系列段的类型，对elf的理解可以参见：
[ELF文件](https://www.pystone.net/notes/elf-file-format/)

常见的 DWARF 段包括：
- **`.debug_info`**：存储程序的调试信息，如变量、类型、函数等。
- **`.debug_line`**：用于将二进制代码的地址映射回源代码行号，方便调试时进行源代码级别的栈回溯。
- **`.debug_frame`** 或 **`.eh_frame`**：包含函数调用时的**栈帧信息（CFI）**，**用于栈回溯**，帮助恢复各个函数的寄存器状态和调用关系。

- **`.debug_abbrev`**：存储调试信息的缩写，减少冗余数据，提高 DWARF 信息的存储效率。
- **`.debug_str`**：包含 DWARF 调试信息中使用的字符串，如变量名、类型名等。
- **`.debug_pubnames`** 和 **`.debug_pubtypes`**：用于记录公共符号名称和类型信息。


## Call Frame Information (CFI)

**CFI（Call Frame Information，调用帧信息）** 是用来描述函数调用过程中寄存器和栈帧变化的。CFI中有以下两种内容：

- **CIE (Common Information Entry)**：描述函数调用栈的通用信息，例如栈帧的基本结构和栈指针如何变化。
- **FDE (Frame Description Entry)**：描述一个具体函数的栈帧布局，包括哪些寄存器被保存、如何恢复这些寄存器的值等。


> **`.eh_frame` 段**：这是异常处理使用的栈帧信息表，记录了函数的栈帧布局和如何恢复寄存器的规则。即使去除了调试信息，`.eh_frame` 段通常仍然会被保留，因为它对于栈回溯和异常处理（如 C++ 的异常机制）是必需的。

> 如果在编译时明确使用了选项去除 `.eh_frame` 段（例如通过 `-fno-asynchronous-unwind-tables`），则 `libunwind` 将无法使用 CFI 信息进行栈回溯。这种情况下，`libunwind` 只能依赖帧指针或栈指针进行回溯，这会导致栈回溯的能力大大受限，特别是在使用了大量优化（如内联、帧指针消除）时，回溯可能不准确甚至失败。

在编译时，以下编译选项会影响 `libunwind` 进行栈回溯的能力：
- **`-g`**：启用调试信息，生成 `.debug_*` 段，但这对于 `libunwind` 来说并非必需。
- **`-fno-omit-frame-pointer`**：保留帧指针，确保 `libunwind` 可以通过帧指针回溯栈帧。
- **`-fno-asynchronous-unwind-tables`**：去除 `.eh_frame` 段，禁止生成 CFI 信息，可能会导致栈回溯失败。


- **CFA**  (Canonical Frame Address)是栈帧的规范化基址,通过 DWARF 提供的 `DW_CFA_def_cfa` 指令定义。CFA 通常用于定位函数的局部变量、函数参数和返回地址等栈帧信息。

当调试信息中的属性（例如 `DW_AT_frame_base`）标记为 `DW_OP_call_frame_cfa`，它的含义是**当前函数的栈帧基址可以通过 CFA 来获得**, "使用当前的 CFA 作为栈帧的基址"。

### CIE（Common Information Entry）
CIE 提供了多个函数共享的通用栈帧信息，定义了如何计算栈帧基址（CFA）及如何处理返回地址。
- **Version**: DWARF 版本号。
- **Augmentation**: 增强字段，描述了附加信息，例如返回地址寄存器等。
- **Code alignment factor**: 程序计数器的对齐因子，通常与指令大小相关。
- **Data alignment factor**: 数据对齐因子，影响栈指针如何变化。
- **Return address column**: 指明返回地址所在的寄存器位置。
- **DW_CFA_def_cfa**: 定义栈帧的基准地址（CFA），用于定位局部变量、保存的寄存器等。它通常由栈指针寄存器（如 `rsp`）加上某个偏移量计算得出。


### **FDE（Frame Description Entry）**


FDE 描述了特定函数的栈帧布局，主要通过指令来更新 CFA 和寄存器的状态。
- **pc range**: 函数的指令范围（程序计数器）。
- **DW_CFA_def_cfa_offset**: 指定 CFA 偏移量，通常表示栈上分配的空间大小。
- **DW_CFA_offset**: 保存某个寄存器的值到栈上，偏移量相对于 CFA。
- **DW_CFA_advance_loc**: 程序计数器前进，用于表示指令执行的推进。
- **DW_CFA_def_cfa_register**: 更新 CFA 的寄存器指针，通常是 `rsp` 或 `rbp`。

> 虽然 CIE 中定义了基本的 CFA 计算规则，但每个函数的 **FDE** 可能会根据其特定的栈帧布局来调整 CFA 的计算。例如：
> - 函数 `funcA` 可能有更多的局部变量或保存了更多的寄存器，因此它可能需要调整更大的栈空间。
> - 函数 `funcB` 的栈帧可能相对简单，CFA 计算的偏移量可能更小。

## 从CFI中可以获取的主要信息

### 栈帧布局

可以利用CFI的信息, 确定每个栈帧的布局，包括栈指针、帧指针和返回地址的存储位置。

### 寄存器状态
**寄存器保存和恢复信息**：通过 `DW_CFA_offset` 和 `DW_CFA_restore` 等指令，DWARF 描述了寄存器的保存和恢复过程。根据这些指令，读取栈中的寄存器值，以恢复函数返回时的寄存器状态。


## 程序与调试信息示例

## 程序源码
示例程序：
```c
#include <stdio.h>

void funcA(int a) {
    int x = a * 2;  // 局部变量
    printf("funcA: x = %d\n", x);
    // 模拟崩溃点
    *((volatile int *)0) = 0;
}


void funcB() {
    int b = 10;  // 局部变量
    funcA(b);    // 调用 funcA
}

int main() {
    funcB();     // 调用 funcB
    return 0;
}
```

该程序可以使用gdb执行和调试。运行时，会出现崩溃信号：
```text
(gdb) run
Starting program: /home/[redacted-user]/example
funcA: x = 20

Program received signal SIGSEGV, Segmentation fault.
0x0000555555554678 in funcA (a=10) at example.c:7
7           *((volatile int *)0) = 0;

```

gdb可以输出崩溃的调用栈回溯结果:
```text
(gdb) backtrace
#0  0x0000555555554678 in funcA (a=10) at example.c:7
#1  0x000055555555469a in funcB () at example.c:12
#2  0x00005555555546ab in main () at example.c:16
```

## 汇编代码

```assembly
 objdump -d ./example

./example：     文件格式 elf64-x86-64


Disassembly of section .init:

00000000000004f0 <_init>:
 4f0:   48 83 ec 08             sub    $0x8,%rsp
 4f4:   48 8b 05 ed 0a 20 00    mov    0x200aed(%rip),%rax        # 200fe8 <__gmon_start__>
 4fb:   48 85 c0                test   %rax,%rax
 4fe:   74 02                   je     502 <_init+0x12>
 500:   ff d0                   callq  *%rax
 502:   48 83 c4 08             add    $0x8,%rsp
 506:   c3                      retq

Disassembly of section .plt:

0000000000000510 <.plt>:
 510:   ff 35 aa 0a 20 00       pushq  0x200aaa(%rip)        # 200fc0 <_GLOBAL_OFFSET_TABLE_+0x8>
 516:   ff 25 ac 0a 20 00       jmpq   *0x200aac(%rip)        # 200fc8 <_GLOBAL_OFFSET_TABLE_+0x10>
 51c:   0f 1f 40 00             nopl   0x0(%rax)

0000000000000520 :
 520:   ff 25 aa 0a 20 00       jmpq   *0x200aaa(%rip)        # 200fd0
 526:   68 00 00 00 00          pushq  $0x0
 52b:   e9 e0 ff ff ff          jmpq   510 <.plt>

Disassembly of section .plt.got:

0000000000000530 <__cxa_finalize@plt>:
 530:   ff 25 c2 0a 20 00       jmpq   *0x200ac2(%rip)        # 200ff8 <__cxa_finalize@GLIBC_2.2.5>
 536:   66 90                   xchg   %ax,%ax

Disassembly of section .text:

0000000000000540 <_start>:
 540:   31 ed                   xor    %ebp,%ebp
 542:   49 89 d1                mov    %rdx,%r9
 545:   5e                      pop    %rsi
 546:   48 89 e2                mov    %rsp,%rdx
 549:   48 83 e4 f0             and    $0xfffffffffffffff0,%rsp
 54d:   50                      push   %rax
 54e:   54                      push   %rsp
 54f:   4c 8d 05 da 01 00 00    lea    0x1da(%rip),%r8        # 730 <__libc_csu_fini>
 556:   48 8d 0d 63 01 00 00    lea    0x163(%rip),%rcx        # 6c0 <__libc_csu_init>
 55d:   48 8d 3d 39 01 00 00    lea    0x139(%rip),%rdi        # 69d <main>
 564:   ff 15 76 0a 20 00       callq  *0x200a76(%rip)        # 200fe0 <__libc_start_main@GLIBC_2.2.5>
 56a:   f4                      hlt
 56b:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

0000000000000570 <deregister_tm_clones>:
 570:   48 8d 3d 99 0a 20 00    lea    0x200a99(%rip),%rdi        # 201010 <__TMC_END__>
 577:   55                      push   %rbp
 578:   48 8d 05 91 0a 20 00    lea    0x200a91(%rip),%rax        # 201010 <__TMC_END__>
 57f:   48 39 f8                cmp    %rdi,%rax
 582:   48 89 e5                mov    %rsp,%rbp
 585:   74 19                   je     5a0 <deregister_tm_clones+0x30>
 587:   48 8b 05 4a 0a 20 00    mov    0x200a4a(%rip),%rax        # 200fd8 <_ITM_deregisterTMCloneTable>
 58e:   48 85 c0                test   %rax,%rax
 591:   74 0d                   je     5a0 <deregister_tm_clones+0x30>
 593:   5d                      pop    %rbp
 594:   ff e0                   jmpq   *%rax
 596:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 59d:   00 00 00
 5a0:   5d                      pop    %rbp
 5a1:   c3                      retq
 5a2:   0f 1f 40 00             nopl   0x0(%rax)
 5a6:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 5ad:   00 00 00

00000000000005b0 <register_tm_clones>:
 5b0:   48 8d 3d 59 0a 20 00    lea    0x200a59(%rip),%rdi        # 201010 <__TMC_END__>
 5b7:   48 8d 35 52 0a 20 00    lea    0x200a52(%rip),%rsi        # 201010 <__TMC_END__>
 5be:   55                      push   %rbp
 5bf:   48 29 fe                sub    %rdi,%rsi
 5c2:   48 89 e5                mov    %rsp,%rbp
 5c5:   48 c1 fe 03             sar    $0x3,%rsi
 5c9:   48 89 f0                mov    %rsi,%rax
 5cc:   48 c1 e8 3f             shr    $0x3f,%rax
 5d0:   48 01 c6                add    %rax,%rsi
 5d3:   48 d1 fe                sar    %rsi
 5d6:   74 18                   je     5f0 <register_tm_clones+0x40>
 5d8:   48 8b 05 11 0a 20 00    mov    0x200a11(%rip),%rax        # 200ff0 <_ITM_registerTMCloneTable>
 5df:   48 85 c0                test   %rax,%rax
 5e2:   74 0c                   je     5f0 <register_tm_clones+0x40>
 5e4:   5d                      pop    %rbp
 5e5:   ff e0                   jmpq   *%rax
 5e7:   66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
 5ee:   00 00
 5f0:   5d                      pop    %rbp
 5f1:   c3                      retq
 5f2:   0f 1f 40 00             nopl   0x0(%rax)
 5f6:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 5fd:   00 00 00

0000000000000600 <__do_global_dtors_aux>:
 600:   80 3d 09 0a 20 00 00    cmpb   $0x0,0x200a09(%rip)        # 201010 <__TMC_END__>
 607:   75 2f                   jne    638 <__do_global_dtors_aux+0x38>
 609:   48 83 3d e7 09 20 00    cmpq   $0x0,0x2009e7(%rip)        # 200ff8 <__cxa_finalize@GLIBC_2.2.5>
 610:   00
 611:   55                      push   %rbp
 612:   48 89 e5                mov    %rsp,%rbp
 615:   74 0c                   je     623 <__do_global_dtors_aux+0x23>
 617:   48 8b 3d ea 09 20 00    mov    0x2009ea(%rip),%rdi        # 201008 <__dso_handle>
 61e:   e8 0d ff ff ff          callq  530 <__cxa_finalize@plt>
 623:   e8 48 ff ff ff          callq  570 <deregister_tm_clones>
 628:   c6 05 e1 09 20 00 01    movb   $0x1,0x2009e1(%rip)        # 201010 <__TMC_END__>
 62f:   5d                      pop    %rbp
 630:   c3                      retq
 631:   0f 1f 80 00 00 00 00    nopl   0x0(%rax)
 638:   f3 c3                   repz retq
 63a:   66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

0000000000000640 <frame_dummy>:
 640:   55                      push   %rbp
 641:   48 89 e5                mov    %rsp,%rbp
 644:   5d                      pop    %rbp
 645:   e9 66 ff ff ff          jmpq   5b0 <register_tm_clones>

000000000000064a <funcA>:
 64a:   55                      push   %rbp
 64b:   48 89 e5                mov    %rsp,%rbp
 64e:   48 83 ec 20             sub    $0x20,%rsp
 652:   89 7d ec                mov    %edi,-0x14(%rbp)
 655:   8b 45 ec                mov    -0x14(%rbp),%eax
 658:   01 c0                   add    %eax,%eax
 65a:   89 45 fc                mov    %eax,-0x4(%rbp)
 65d:   8b 45 fc                mov    -0x4(%rbp),%eax
 660:   89 c6                   mov    %eax,%esi
 662:   48 8d 3d db 00 00 00    lea    0xdb(%rip),%rdi        # 744 <_IO_stdin_used+0x4>
 669:   b8 00 00 00 00          mov    $0x0,%eax
 66e:   e8 ad fe ff ff          callq  520
 673:   b8 00 00 00 00          mov    $0x0,%eax
 678:   c7 00 00 00 00 00       movl   $0x0,(%rax)
 67e:   90                      nop
 67f:   c9                      leaveq
 680:   c3                      retq

0000000000000681 <funcB>:
 681:   55                      push   %rbp
 682:   48 89 e5                mov    %rsp,%rbp
 685:   48 83 ec 10             sub    $0x10,%rsp
 689:   c7 45 fc 0a 00 00 00    movl   $0xa,-0x4(%rbp)
 690:   8b 45 fc                mov    -0x4(%rbp),%eax
 693:   89 c7                   mov    %eax,%edi
 695:   e8 b0 ff ff ff          callq  64a <funcA>
 69a:   90                      nop
 69b:   c9                      leaveq
 69c:   c3                      retq

000000000000069d <main>:
 69d:   55                      push   %rbp
 69e:   48 89 e5                mov    %rsp,%rbp
 6a1:   b8 00 00 00 00          mov    $0x0,%eax
 6a6:   e8 d6 ff ff ff          callq  681 <funcB>
 6ab:   b8 00 00 00 00          mov    $0x0,%eax
 6b0:   5d                      pop    %rbp
 6b1:   c3                      retq
 6b2:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 6b9:   00 00 00
 6bc:   0f 1f 40 00             nopl   0x0(%rax)

00000000000006c0 <__libc_csu_init>:
 6c0:   41 57                   push   %r15
 6c2:   41 56                   push   %r14
 6c4:   49 89 d7                mov    %rdx,%r15
 6c7:   41 55                   push   %r13
 6c9:   41 54                   push   %r12
 6cb:   4c 8d 25 e6 06 20 00    lea    0x2006e6(%rip),%r12        # 200db8 <__frame_dummy_init_array_entry>
 6d2:   55                      push   %rbp
 6d3:   48 8d 2d e6 06 20 00    lea    0x2006e6(%rip),%rbp        # 200dc0 <__init_array_end>
 6da:   53                      push   %rbx
 6db:   41 89 fd                mov    %edi,%r13d
 6de:   49 89 f6                mov    %rsi,%r14
 6e1:   4c 29 e5                sub    %r12,%rbp
 6e4:   48 83 ec 08             sub    $0x8,%rsp
 6e8:   48 c1 fd 03             sar    $0x3,%rbp
 6ec:   e8 ff fd ff ff          callq  4f0 <_init>
 6f1:   48 85 ed                test   %rbp,%rbp
 6f4:   74 20                   je     716 <__libc_csu_init+0x56>
 6f6:   31 db                   xor    %ebx,%ebx
 6f8:   0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
 6ff:   00
 700:   4c 89 fa                mov    %r15,%rdx
 703:   4c 89 f6                mov    %r14,%rsi
 706:   44 89 ef                mov    %r13d,%edi
 709:   41 ff 14 dc             callq  *(%r12,%rbx,8)
 70d:   48 83 c3 01             add    $0x1,%rbx
 711:   48 39 dd                cmp    %rbx,%rbp
 714:   75 ea                   jne    700 <__libc_csu_init+0x40>
 716:   48 83 c4 08             add    $0x8,%rsp
 71a:   5b                      pop    %rbx
 71b:   5d                      pop    %rbp
 71c:   41 5c                   pop    %r12
 71e:   41 5d                   pop    %r13
 720:   41 5e                   pop    %r14
 722:   41 5f                   pop    %r15
 724:   c3                      retq
 725:   90                      nop
 726:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 72d:   00 00 00

0000000000000730 <__libc_csu_fini>:
 730:   f3 c3                   repz retq

Disassembly of section .fini:

0000000000000734 <_fini>:
 734:   48 83 ec 08             sub    $0x8,%rsp
 738:   48 83 c4 08             add    $0x8,%rsp
 73c:   c3                      retq
```


## debug_frame
该程序的debug_frame或eh_frame内容如下.

CIE 可以是全局公共的, 被共享. FDE相当于继承或者共享CIE的内容

```text
uwa@wzp-B365-HD3:~$ readelf --debug-dump=frames ./example

.eh_frame 节的内容:


00000000 0000000000000014 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_undefined: r16 (rip)

00000018 0000000000000014 0000001c FDE cie=00000000 pc=0000000000000540..000000000000056b
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop

00000030 0000000000000014 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

00000048 0000000000000024 0000001c FDE cie=00000030 pc=0000000000000510..0000000000000530
  DW_CFA_def_cfa_offset: 16
  DW_CFA_advance_loc: 6 to 0000000000000516
  DW_CFA_def_cfa_offset: 24
  DW_CFA_advance_loc: 10 to 0000000000000520
  DW_CFA_def_cfa_expression (DW_OP_breg7 (rsp): 8; DW_OP_breg16 (rip): 0; DW_OP_lit15; DW_OP_and; DW_OP_lit11; DW_OP_ge; DW_OP_lit3; DW_OP_shl; DW_OP_plus)
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop

00000070 0000000000000014 00000044 FDE cie=00000030 pc=0000000000000530..0000000000000538
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop

00000088 000000000000001c 0000005c FDE cie=00000030 pc=000000000000064a..0000000000000681
  DW_CFA_advance_loc: 1 to 000000000000064b
  DW_CFA_def_cfa_offset: 16
  DW_CFA_offset: r6 (rbp) at cfa-16
  DW_CFA_advance_loc: 3 to 000000000000064e
  DW_CFA_def_cfa_register: r6 (rbp)
  DW_CFA_advance_loc: 50 to 0000000000000680
  DW_CFA_def_cfa: r7 (rsp) ofs 8
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop

000000a8 000000000000001c 0000007c FDE cie=00000030 pc=0000000000000681..000000000000069d
  DW_CFA_advance_loc: 1 to 0000000000000682
  DW_CFA_def_cfa_offset: 16
  DW_CFA_offset: r6 (rbp) at cfa-16
  DW_CFA_advance_loc: 3 to 0000000000000685
  DW_CFA_def_cfa_register: r6 (rbp)
  DW_CFA_advance_loc: 23 to 000000000000069c
  DW_CFA_def_cfa: r7 (rsp) ofs 8
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop

000000c8 000000000000001c 0000009c FDE cie=00000030 pc=000000000000069d..00000000000006b2
  DW_CFA_advance_loc: 1 to 000000000000069e
  DW_CFA_def_cfa_offset: 16
  DW_CFA_offset: r6 (rbp) at cfa-16
  DW_CFA_advance_loc: 3 to 00000000000006a1
  DW_CFA_def_cfa_register: r6 (rbp)
  DW_CFA_advance_loc: 16 to 00000000000006b1
  DW_CFA_def_cfa: r7 (rsp) ofs 8
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop

000000e8 0000000000000044 000000bc FDE cie=00000030 pc=00000000000006c0..0000000000000725
  DW_CFA_advance_loc: 2 to 00000000000006c2
  DW_CFA_def_cfa_offset: 16
  DW_CFA_offset: r15 (r15) at cfa-16
  DW_CFA_advance_loc: 2 to 00000000000006c4
  DW_CFA_def_cfa_offset: 24
  DW_CFA_offset: r14 (r14) at cfa-24
  DW_CFA_advance_loc: 5 to 00000000000006c9
  DW_CFA_def_cfa_offset: 32
  DW_CFA_offset: r13 (r13) at cfa-32
  DW_CFA_advance_loc: 2 to 00000000000006cb
  DW_CFA_def_cfa_offset: 40
  DW_CFA_offset: r12 (r12) at cfa-40
  DW_CFA_advance_loc: 8 to 00000000000006d3
  DW_CFA_def_cfa_offset: 48
  DW_CFA_offset: r6 (rbp) at cfa-48
  DW_CFA_advance_loc: 8 to 00000000000006db
  DW_CFA_def_cfa_offset: 56
  DW_CFA_offset: r3 (rbx) at cfa-56
  DW_CFA_advance_loc: 13 to 00000000000006e8
  DW_CFA_def_cfa_offset: 64
  DW_CFA_advance_loc: 50 to 000000000000071a
  DW_CFA_def_cfa_offset: 56
  DW_CFA_advance_loc: 1 to 000000000000071b
  DW_CFA_def_cfa_offset: 48
  DW_CFA_advance_loc: 1 to 000000000000071c
  DW_CFA_def_cfa_offset: 40
  DW_CFA_advance_loc: 2 to 000000000000071e
  DW_CFA_def_cfa_offset: 32
  DW_CFA_advance_loc: 2 to 0000000000000720
  DW_CFA_def_cfa_offset: 24
  DW_CFA_advance_loc: 2 to 0000000000000722
  DW_CFA_def_cfa_offset: 16
  DW_CFA_advance_loc: 2 to 0000000000000724
  DW_CFA_def_cfa_offset: 8
  DW_CFA_nop

00000130 0000000000000010 00000104 FDE cie=00000030 pc=0000000000000730..0000000000000732
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop

00000144 ZERO terminator

```

- `DW_CFA_def_cfa: rsp, 16`：表示 CFA 是栈指针 `rsp` 加上 16 字节的偏移。
- `DW_CFA_offset: rip at cfa-8`：表示返回地址（`rip`）保存于栈顶 CFA - 8 位置。

FDE 2 (函数 funcB)
```yaml
000000a8 000000000000001c 0000007c FDE cie=00000030 pc=0000000000000681..000000000000069d
  DW_CFA_advance_loc: 1 to 0000000000000682
  DW_CFA_def_cfa_offset: 16
  DW_CFA_offset: r6 (rbp) at cfa-16
  DW_CFA_advance_loc: 3 to 0000000000000685
  DW_CFA_def_cfa_register: r6 (rbp)
  DW_CFA_advance_loc: 23 to 000000000000069c
  DW_CFA_def_cfa: r7 (rsp) ofs 8
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop
```

- **`DW_CFA_advance_loc`** 表示 PC（Program Counter，程序计数器）向前推进 1 个字节，到地址 `0x682`。这通常意味着程序的执行已经进入了新的指令，可能是一个新的函数调用或栈操作。
- **`DW_CFA_def_cfa_offset`** 表示当前 **CFA** 的偏移量为 **16** 字节。

如果当前 `SP` 的值为 **`0x1000`**，那么 **CFA** 的计算如下：
```ini
CFA = SP + 16 = 0x1000 + 0x10 = 0x1010
```

**DW_CFA_offset: r6 (rbp) at cfa-16** 描述了 **`rbp`（帧指针寄存器）** 的保存位置。
假设当前 CFA = 0x1008，那么 `rbp` 保存的位置为：
```text
rbp 保存位置 = 0x1008 - 16 = 0x0FF8
```


## 借助DWARF信息进行栈回溯的基本流程

由以上信息可知, DWARF通过 `.eh_frame` 和 `.debug_frame` 段来提供调用帧信息(CFI)来描述栈帧布局。 调试工具或者回溯工具可以利用DWARF提供的CFI, 对调用栈进行回溯, 以获得造成崩溃的指令的调用链, 并且可以获取每一帧的寄存器信息.

- **获取当前 PC 和栈指针（SP）**：首先从当前的执行上下文中获取程序计数器（PC）和栈指针（SP）。
- **读取 CFI 信息**：使用 DWARF 的 CFI 信息找到当前栈帧中保存的返回地址和寄存器位置。
- **恢复上一个栈帧**：根据 CFI 信息恢复上一个栈帧的寄存器状态和返回地址。
- **重复过程**：继续回溯，直到到达栈的顶端。

1. **初始化上下文**：在崩溃时或调试时，首先获取当前的上下文状态，包括当前的栈指针（`rsp`）、程序计数器（`rip`）等寄存器状态。
2. **获取 CFA**：根据 DWARF 中的 **CFI 信息**，利用指令如 `DW_CFA_def_cfa` 来计算当前栈帧的 **CFA**。
3. **恢复寄存器状态**：
    - 从栈中恢复保存的寄存器信息（如 `rbp`、`rip`），以确定上一个栈帧的地址。
    - 通过 `DW_CFA_offset` 指令找到每个寄存器的位置，并从栈中恢复它们的值。
4. **回溯到上一个栈帧**：
    - 利用恢复的返回地址寄存器（如 `rip`），确定上一个函数的程序计数器（PC）。
    - 根据新的 PC 继续从 DWARF 信息中获取该栈帧的 CFI，更新 CFA 和寄存器状态。
5. **逐帧回溯**：重复上述过程，直到到达主函数或回溯到栈的最底部。
