{
  "schemaVersion": "0.11.0",
  "canonical": "https://www.pystone.net/notes/elf-dwarf-format/",
  "atlas": "https://www.pystone.net/?node=elf-dwarf-format#knowledge-atlas",
  "markdown": "https://www.pystone.net/notes/elf-dwarf-format.md",
  "context": "https://www.pystone.net/notes/elf-dwarf-format.context.json",
  "knowledgeVersion": "224c990773de.5fa8af6e39fa",
  "build": {
    "siteCommit": "224c990773de166d23a886306577dd90379529ce",
    "notesCommit": "5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a",
    "builtAt": "1970-01-01T00:00:00.000Z",
    "version": "224c990773de.5fa8af6e39fa"
  },
  "id": "note:elf-dwarf-format",
  "slug": "elf-dwarf-format",
  "title": "ELF文件-DWARF",
  "type": "note",
  "visibility": "public",
  "idStability": "rename-stable",
  "author": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "publisher": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "aliases": [],
  "summary": "ELF文件 DWARF DWARF 是一种用于调试的标准化格式，全称为 \" Debugging With Attributed Record Formats \"。帮助调试器从编译后的二进制文件中恢复源代码级别的信息，比如变量名称、类型、函数调用栈等。",
  "contentRole": "unspecified",
  "isMoc": false,
  "mocRecognition": "none",
  "generated": false,
  "attribution": "unspecified",
  "domain": "10-计算机、信息技术与工程",
  "tags": [],
  "mocs": [],
  "contentHash": "bb18448284e80f786b620a42fa8a8325b711af9a0270d18bb2666e7bf289a5d8",
  "assets": [],
  "headings": [
    {
      "depth": 1,
      "text": "ELF文件-DWARF",
      "anchor": "elf文件-dwarf",
      "citation": "https://www.pystone.net/notes/elf-dwarf-format/#elf%E6%96%87%E4%BB%B6-dwarf"
    },
    {
      "depth": 2,
      "text": "DWARF的内容",
      "anchor": "dwarf的内容",
      "citation": "https://www.pystone.net/notes/elf-dwarf-format/#dwarf%E7%9A%84%E5%86%85%E5%AE%B9"
    },
    {
      "depth": 2,
      "text": "Call Frame Information (CFI)",
      "anchor": "call-frame-information-cfi",
      "citation": "https://www.pystone.net/notes/elf-dwarf-format/#call-frame-information-cfi"
    },
    {
      "depth": 3,
      "text": "CIE（Common Information Entry）",
      "anchor": "ciecommon-information-entry",
      "citation": "https://www.pystone.net/notes/elf-dwarf-format/#ciecommon-information-entry"
    },
    {
      "depth": 3,
      "text": "FDE（Frame Description Entry）",
      "anchor": "fdeframe-description-entry",
      "citation": "https://www.pystone.net/notes/elf-dwarf-format/#fdeframe-description-entry"
    },
    {
      "depth": 2,
      "text": "从CFI中可以获取的主要信息",
      "anchor": "从cfi中可以获取的主要信息",
      "citation": "https://www.pystone.net/notes/elf-dwarf-format/#%E4%BB%8Ecfi%E4%B8%AD%E5%8F%AF%E4%BB%A5%E8%8E%B7%E5%8F%96%E7%9A%84%E4%B8%BB%E8%A6%81%E4%BF%A1%E6%81%AF"
    },
    {
      "depth": 3,
      "text": "栈帧布局",
      "anchor": "栈帧布局",
      "citation": "https://www.pystone.net/notes/elf-dwarf-format/#%E6%A0%88%E5%B8%A7%E5%B8%83%E5%B1%80"
    },
    {
      "depth": 3,
      "text": "寄存器状态",
      "anchor": "寄存器状态",
      "citation": "https://www.pystone.net/notes/elf-dwarf-format/#%E5%AF%84%E5%AD%98%E5%99%A8%E7%8A%B6%E6%80%81"
    },
    {
      "depth": 2,
      "text": "程序与调试信息示例",
      "anchor": "程序与调试信息示例",
      "citation": "https://www.pystone.net/notes/elf-dwarf-format/#%E7%A8%8B%E5%BA%8F%E4%B8%8E%E8%B0%83%E8%AF%95%E4%BF%A1%E6%81%AF%E7%A4%BA%E4%BE%8B"
    },
    {
      "depth": 2,
      "text": "程序源码",
      "anchor": "程序源码",
      "citation": "https://www.pystone.net/notes/elf-dwarf-format/#%E7%A8%8B%E5%BA%8F%E6%BA%90%E7%A0%81"
    },
    {
      "depth": 2,
      "text": "汇编代码",
      "anchor": "汇编代码",
      "citation": "https://www.pystone.net/notes/elf-dwarf-format/#%E6%B1%87%E7%BC%96%E4%BB%A3%E7%A0%81"
    },
    {
      "depth": 2,
      "text": "debugframe",
      "anchor": "debugframe",
      "citation": "https://www.pystone.net/notes/elf-dwarf-format/#debugframe"
    },
    {
      "depth": 2,
      "text": "借助DWARF信息进行栈回溯的基本流程",
      "anchor": "借助dwarf信息进行栈回溯的基本流程",
      "citation": "https://www.pystone.net/notes/elf-dwarf-format/#%E5%80%9F%E5%8A%A9dwarf%E4%BF%A1%E6%81%AF%E8%BF%9B%E8%A1%8C%E6%A0%88%E5%9B%9E%E6%BA%AF%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%B5%81%E7%A8%8B"
    }
  ],
  "claims": [],
  "outgoing": [
    {
      "id": "note:elf-file-format",
      "title": "ELF文件",
      "url": "https://www.pystone.net/notes/elf-file-format/",
      "atlas": "https://www.pystone.net/?node=elf-file-format#knowledge-atlas",
      "label": "ELF文件",
      "origin": "explicit",
      "humanReviewed": true,
      "context": "DWARF的内容中的“ELF文件”导航项",
      "citation": "https://www.pystone.net/notes/elf-dwarf-format/#dwarf%E7%9A%84%E5%86%85%E5%AE%B9"
    }
  ],
  "incoming": [
    {
      "id": "note:programming-languages-and-runtimes",
      "title": "编程语言与运行时",
      "url": "https://www.pystone.net/notes/programming-languages-and-runtimes/",
      "atlas": "https://www.pystone.net/?node=programming-languages-and-runtimes#knowledge-atlas",
      "label": "编程语言与运行时",
      "origin": "explicit",
      "humanReviewed": true,
      "context": "本层中的“ELF文件-DWARF”导航项",
      "citation": "https://www.pystone.net/notes/programming-languages-and-runtimes/#%E6%9C%AC%E5%B1%82"
    }
  ],
  "contentMarkdown": "# ELF文件-DWARF\nDWARF 是一种用于调试的标准化格式，全称为 \"**Debugging With Attributed Record Formats**\"。帮助调试器从编译后的二进制文件中恢复源代码级别的信息，比如变量名称、类型、函数调用栈等。\n\nlibunwind 在实现栈回溯时依赖 DWARF 来解析栈帧信息和寄存器状态。\n\n**DWARF** 是存储在 **ELF 文件**中的一个（或多个）**section**，它与其他诸如可执行代码段（`.text` 段）、符号表（`.symtab` 段）等并列存在。\n\nDWARF 文件的内容包括：\n- **源代码中的函数和变量信息**：每个函数或变量在源代码中的名称、数据类型、作用域等。\n- **程序的控制流和栈信息**：包括函数调用的栈帧结构、寄存器信息，方便回溯调用栈。\n- **数据布局信息**：帮助调试器理解如何解析复杂的数据结构。\n\n## DWARF的内容\n- **Compilation Unit (CU)**：\n    - 是 DWARF 文件的基本单元，每个编译单元描述了从某个源文件生成的调试信息。它包含了该源文件中的所有符号和类型定义。\n- **Debug Information Entry (DIE)**：\n    - DIE 是 DWARF 中的基本数据结构，每个 DIE 描述一个调试信息项，如一个变量、一个类型定义或一个函数。DIE 是分层结构的，父 DIE 可以包含多个子 DIE。\n- **Call Frame Information (CFI)**：\n    - CFI 是 DWARF 中的栈帧信息，用来描述函数调用过程中寄存器和栈帧如何变化。CFI 用于栈回溯，它记录了关于程序中每个函数如何设置栈帧、保存寄存器以及在函数返回时如何恢复这些寄存器的信息。\n- **Location Expressions**：\n    - DWARF 使用位置表达式（Location Expressions）来描述变量在内存或寄存器中的位置。通过解析这些表达式，调试器可以找到程序执行期间变量存储的位置。\n- **Line Number Information**：\n    - DWARF 还包含源代码行号信息，帮助调试器在二进制文件和源代码之间进行映射。每个函数和变量都可以映射到源代码的具体行号，便于调试时快速定位。\n\n- 每个 **编译单元（Compilation Unit，CU）** 代表一个源文件。\n- 每个 CU 包含多个 **调试信息条目（Debug Information Entry, DIE）** ，这些条目描述了该文件中的变量、类型、函数等。\n\nDWARF就是elf文件中的一系列段的类型，对elf的理解可以参见：\n[ELF文件](5%20信息技术领域/编程语言及特性/ELF文件.md)\n\n常见的 DWARF 段包括：\n- **`.debug_info`**：存储程序的调试信息，如变量、类型、函数等。\n- **`.debug_line`**：用于将二进制代码的地址映射回源代码行号，方便调试时进行源代码级别的栈回溯。\n- **`.debug_frame`** 或 **`.eh_frame`**：包含函数调用时的**栈帧信息（CFI）**，**用于栈回溯**，帮助恢复各个函数的寄存器状态和调用关系。\n\n- **`.debug_abbrev`**：存储调试信息的缩写，减少冗余数据，提高 DWARF 信息的存储效率。\n- **`.debug_str`**：包含 DWARF 调试信息中使用的字符串，如变量名、类型名等。\n- **`.debug_pubnames`** 和 **`.debug_pubtypes`**：用于记录公共符号名称和类型信息。\n\n\n## Call Frame Information (CFI)\n\n**CFI（Call Frame Information，调用帧信息）** 是用来描述函数调用过程中寄存器和栈帧变化的。CFI中有以下两种内容：\n\n- **CIE (Common Information Entry)**：描述函数调用栈的通用信息，例如栈帧的基本结构和栈指针如何变化。\n- **FDE (Frame Description Entry)**：描述一个具体函数的栈帧布局，包括哪些寄存器被保存、如何恢复这些寄存器的值等。\n\n\n> **`.eh_frame` 段**：这是异常处理使用的栈帧信息表，记录了函数的栈帧布局和如何恢复寄存器的规则。即使去除了调试信息，`.eh_frame` 段通常仍然会被保留，因为它对于栈回溯和异常处理（如 C++ 的异常机制）是必需的。\n\n> 如果在编译时明确使用了选项去除 `.eh_frame` 段（例如通过 `-fno-asynchronous-unwind-tables`），则 `libunwind` 将无法使用 CFI 信息进行栈回溯。这种情况下，`libunwind` 只能依赖帧指针或栈指针进行回溯，这会导致栈回溯的能力大大受限，特别是在使用了大量优化（如内联、帧指针消除）时，回溯可能不准确甚至失败。\n\n在编译时，以下编译选项会影响 `libunwind` 进行栈回溯的能力：\n- **`-g`**：启用调试信息，生成 `.debug_*` 段，但这对于 `libunwind` 来说并非必需。\n- **`-fno-omit-frame-pointer`**：保留帧指针，确保 `libunwind` 可以通过帧指针回溯栈帧。\n- **`-fno-asynchronous-unwind-tables`**：去除 `.eh_frame` 段，禁止生成 CFI 信息，可能会导致栈回溯失败。\n\n\n- **CFA**  (Canonical Frame Address)是栈帧的规范化基址,通过 DWARF 提供的 `DW_CFA_def_cfa` 指令定义。CFA 通常用于定位函数的局部变量、函数参数和返回地址等栈帧信息。\n\n当调试信息中的属性（例如 `DW_AT_frame_base`）标记为 `DW_OP_call_frame_cfa`，它的含义是**当前函数的栈帧基址可以通过 CFA 来获得**, \"使用当前的 CFA 作为栈帧的基址\"。\n\n### CIE（Common Information Entry）\nCIE 提供了多个函数共享的通用栈帧信息，定义了如何计算栈帧基址（CFA）及如何处理返回地址。\n- **Version**: DWARF 版本号。\n- **Augmentation**: 增强字段，描述了附加信息，例如返回地址寄存器等。\n- **Code alignment factor**: 程序计数器的对齐因子，通常与指令大小相关。\n- **Data alignment factor**: 数据对齐因子，影响栈指针如何变化。\n- **Return address column**: 指明返回地址所在的寄存器位置。\n- **DW_CFA_def_cfa**: 定义栈帧的基准地址（CFA），用于定位局部变量、保存的寄存器等。它通常由栈指针寄存器（如 `rsp`）加上某个偏移量计算得出。\n\n\n### **FDE（Frame Description Entry）**\n\n\nFDE 描述了特定函数的栈帧布局，主要通过指令来更新 CFA 和寄存器的状态。\n- **pc range**: 函数的指令范围（程序计数器）。\n- **DW_CFA_def_cfa_offset**: 指定 CFA 偏移量，通常表示栈上分配的空间大小。\n- **DW_CFA_offset**: 保存某个寄存器的值到栈上，偏移量相对于 CFA。\n- **DW_CFA_advance_loc**: 程序计数器前进，用于表示指令执行的推进。\n- **DW_CFA_def_cfa_register**: 更新 CFA 的寄存器指针，通常是 `rsp` 或 `rbp`。\n\n> 虽然 CIE 中定义了基本的 CFA 计算规则，但每个函数的 **FDE** 可能会根据其特定的栈帧布局来调整 CFA 的计算。例如：\n> - 函数 `funcA` 可能有更多的局部变量或保存了更多的寄存器，因此它可能需要调整更大的栈空间。\n> - 函数 `funcB` 的栈帧可能相对简单，CFA 计算的偏移量可能更小。\n\n## 从CFI中可以获取的主要信息\n\n### 栈帧布局\n\n可以利用CFI的信息, 确定每个栈帧的布局，包括栈指针、帧指针和返回地址的存储位置。\n\n### 寄存器状态\n**寄存器保存和恢复信息**：通过 `DW_CFA_offset` 和 `DW_CFA_restore` 等指令，DWARF 描述了寄存器的保存和恢复过程。根据这些指令，读取栈中的寄存器值，以恢复函数返回时的寄存器状态。\n\n\n## 程序与调试信息示例\n\n## 程序源码\n示例程序：\n```c\n#include <stdio.h>\n\nvoid funcA(int a) {\n    int x = a * 2;  // 局部变量\n    printf(\"funcA: x = %d\\n\", x);\n    // 模拟崩溃点\n    *((volatile int *)0) = 0;\n}\n\n\nvoid funcB() {\n    int b = 10;  // 局部变量\n    funcA(b);    // 调用 funcA\n}\n\nint main() {\n    funcB();     // 调用 funcB\n    return 0;\n}\n```\n\n该程序可以使用gdb执行和调试。运行时，会出现崩溃信号：\n```text\n(gdb) run\nStarting program: /home/[redacted-user]/example\nfuncA: x = 20\n\nProgram received signal SIGSEGV, Segmentation fault.\n0x0000555555554678 in funcA (a=10) at example.c:7\n7           *((volatile int *)0) = 0;\n\n```\n\ngdb可以输出崩溃的调用栈回溯结果:\n```text\n(gdb) backtrace\n#0  0x0000555555554678 in funcA (a=10) at example.c:7\n#1  0x000055555555469a in funcB () at example.c:12\n#2  0x00005555555546ab in main () at example.c:16\n```\n\n## 汇编代码\n\n```assembly\n objdump -d ./example\n\n./example：     文件格式 elf64-x86-64\n\n\nDisassembly of section .init:\n\n00000000000004f0 <_init>:\n 4f0:   48 83 ec 08             sub    $0x8,%rsp\n 4f4:   48 8b 05 ed 0a 20 00    mov    0x200aed(%rip),%rax        # 200fe8 <__gmon_start__>\n 4fb:   48 85 c0                test   %rax,%rax\n 4fe:   74 02                   je     502 <_init+0x12>\n 500:   ff d0                   callq  *%rax\n 502:   48 83 c4 08             add    $0x8,%rsp\n 506:   c3                      retq\n\nDisassembly of section .plt:\n\n0000000000000510 <.plt>:\n 510:   ff 35 aa 0a 20 00       pushq  0x200aaa(%rip)        # 200fc0 <_GLOBAL_OFFSET_TABLE_+0x8>\n 516:   ff 25 ac 0a 20 00       jmpq   *0x200aac(%rip)        # 200fc8 <_GLOBAL_OFFSET_TABLE_+0x10>\n 51c:   0f 1f 40 00             nopl   0x0(%rax)\n\n0000000000000520 :\n 520:   ff 25 aa 0a 20 00       jmpq   *0x200aaa(%rip)        # 200fd0\n 526:   68 00 00 00 00          pushq  $0x0\n 52b:   e9 e0 ff ff ff          jmpq   510 <.plt>\n\nDisassembly of section .plt.got:\n\n0000000000000530 <__cxa_finalize@plt>:\n 530:   ff 25 c2 0a 20 00       jmpq   *0x200ac2(%rip)        # 200ff8 <__cxa_finalize@GLIBC_2.2.5>\n 536:   66 90                   xchg   %ax,%ax\n\nDisassembly of section .text:\n\n0000000000000540 <_start>:\n 540:   31 ed                   xor    %ebp,%ebp\n 542:   49 89 d1                mov    %rdx,%r9\n 545:   5e                      pop    %rsi\n 546:   48 89 e2                mov    %rsp,%rdx\n 549:   48 83 e4 f0             and    $0xfffffffffffffff0,%rsp\n 54d:   50                      push   %rax\n 54e:   54                      push   %rsp\n 54f:   4c 8d 05 da 01 00 00    lea    0x1da(%rip),%r8        # 730 <__libc_csu_fini>\n 556:   48 8d 0d 63 01 00 00    lea    0x163(%rip),%rcx        # 6c0 <__libc_csu_init>\n 55d:   48 8d 3d 39 01 00 00    lea    0x139(%rip),%rdi        # 69d <main>\n 564:   ff 15 76 0a 20 00       callq  *0x200a76(%rip)        # 200fe0 <__libc_start_main@GLIBC_2.2.5>\n 56a:   f4                      hlt\n 56b:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)\n\n0000000000000570 <deregister_tm_clones>:\n 570:   48 8d 3d 99 0a 20 00    lea    0x200a99(%rip),%rdi        # 201010 <__TMC_END__>\n 577:   55                      push   %rbp\n 578:   48 8d 05 91 0a 20 00    lea    0x200a91(%rip),%rax        # 201010 <__TMC_END__>\n 57f:   48 39 f8                cmp    %rdi,%rax\n 582:   48 89 e5                mov    %rsp,%rbp\n 585:   74 19                   je     5a0 <deregister_tm_clones+0x30>\n 587:   48 8b 05 4a 0a 20 00    mov    0x200a4a(%rip),%rax        # 200fd8 <_ITM_deregisterTMCloneTable>\n 58e:   48 85 c0                test   %rax,%rax\n 591:   74 0d                   je     5a0 <deregister_tm_clones+0x30>\n 593:   5d                      pop    %rbp\n 594:   ff e0                   jmpq   *%rax\n 596:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)\n 59d:   00 00 00\n 5a0:   5d                      pop    %rbp\n 5a1:   c3                      retq\n 5a2:   0f 1f 40 00             nopl   0x0(%rax)\n 5a6:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)\n 5ad:   00 00 00\n\n00000000000005b0 <register_tm_clones>:\n 5b0:   48 8d 3d 59 0a 20 00    lea    0x200a59(%rip),%rdi        # 201010 <__TMC_END__>\n 5b7:   48 8d 35 52 0a 20 00    lea    0x200a52(%rip),%rsi        # 201010 <__TMC_END__>\n 5be:   55                      push   %rbp\n 5bf:   48 29 fe                sub    %rdi,%rsi\n 5c2:   48 89 e5                mov    %rsp,%rbp\n 5c5:   48 c1 fe 03             sar    $0x3,%rsi\n 5c9:   48 89 f0                mov    %rsi,%rax\n 5cc:   48 c1 e8 3f             shr    $0x3f,%rax\n 5d0:   48 01 c6                add    %rax,%rsi\n 5d3:   48 d1 fe                sar    %rsi\n 5d6:   74 18                   je     5f0 <register_tm_clones+0x40>\n 5d8:   48 8b 05 11 0a 20 00    mov    0x200a11(%rip),%rax        # 200ff0 <_ITM_registerTMCloneTable>\n 5df:   48 85 c0                test   %rax,%rax\n 5e2:   74 0c                   je     5f0 <register_tm_clones+0x40>\n 5e4:   5d                      pop    %rbp\n 5e5:   ff e0                   jmpq   *%rax\n 5e7:   66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)\n 5ee:   00 00\n 5f0:   5d                      pop    %rbp\n 5f1:   c3                      retq\n 5f2:   0f 1f 40 00             nopl   0x0(%rax)\n 5f6:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)\n 5fd:   00 00 00\n\n0000000000000600 <__do_global_dtors_aux>:\n 600:   80 3d 09 0a 20 00 00    cmpb   $0x0,0x200a09(%rip)        # 201010 <__TMC_END__>\n 607:   75 2f                   jne    638 <__do_global_dtors_aux+0x38>\n 609:   48 83 3d e7 09 20 00    cmpq   $0x0,0x2009e7(%rip)        # 200ff8 <__cxa_finalize@GLIBC_2.2.5>\n 610:   00\n 611:   55                      push   %rbp\n 612:   48 89 e5                mov    %rsp,%rbp\n 615:   74 0c                   je     623 <__do_global_dtors_aux+0x23>\n 617:   48 8b 3d ea 09 20 00    mov    0x2009ea(%rip),%rdi        # 201008 <__dso_handle>\n 61e:   e8 0d ff ff ff          callq  530 <__cxa_finalize@plt>\n 623:   e8 48 ff ff ff          callq  570 <deregister_tm_clones>\n 628:   c6 05 e1 09 20 00 01    movb   $0x1,0x2009e1(%rip)        # 201010 <__TMC_END__>\n 62f:   5d                      pop    %rbp\n 630:   c3                      retq\n 631:   0f 1f 80 00 00 00 00    nopl   0x0(%rax)\n 638:   f3 c3                   repz retq\n 63a:   66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)\n\n0000000000000640 <frame_dummy>:\n 640:   55                      push   %rbp\n 641:   48 89 e5                mov    %rsp,%rbp\n 644:   5d                      pop    %rbp\n 645:   e9 66 ff ff ff          jmpq   5b0 <register_tm_clones>\n\n000000000000064a <funcA>:\n 64a:   55                      push   %rbp\n 64b:   48 89 e5                mov    %rsp,%rbp\n 64e:   48 83 ec 20             sub    $0x20,%rsp\n 652:   89 7d ec                mov    %edi,-0x14(%rbp)\n 655:   8b 45 ec                mov    -0x14(%rbp),%eax\n 658:   01 c0                   add    %eax,%eax\n 65a:   89 45 fc                mov    %eax,-0x4(%rbp)\n 65d:   8b 45 fc                mov    -0x4(%rbp),%eax\n 660:   89 c6                   mov    %eax,%esi\n 662:   48 8d 3d db 00 00 00    lea    0xdb(%rip),%rdi        # 744 <_IO_stdin_used+0x4>\n 669:   b8 00 00 00 00          mov    $0x0,%eax\n 66e:   e8 ad fe ff ff          callq  520\n 673:   b8 00 00 00 00          mov    $0x0,%eax\n 678:   c7 00 00 00 00 00       movl   $0x0,(%rax)\n 67e:   90                      nop\n 67f:   c9                      leaveq\n 680:   c3                      retq\n\n0000000000000681 <funcB>:\n 681:   55                      push   %rbp\n 682:   48 89 e5                mov    %rsp,%rbp\n 685:   48 83 ec 10             sub    $0x10,%rsp\n 689:   c7 45 fc 0a 00 00 00    movl   $0xa,-0x4(%rbp)\n 690:   8b 45 fc                mov    -0x4(%rbp),%eax\n 693:   89 c7                   mov    %eax,%edi\n 695:   e8 b0 ff ff ff          callq  64a <funcA>\n 69a:   90                      nop\n 69b:   c9                      leaveq\n 69c:   c3                      retq\n\n000000000000069d <main>:\n 69d:   55                      push   %rbp\n 69e:   48 89 e5                mov    %rsp,%rbp\n 6a1:   b8 00 00 00 00          mov    $0x0,%eax\n 6a6:   e8 d6 ff ff ff          callq  681 <funcB>\n 6ab:   b8 00 00 00 00          mov    $0x0,%eax\n 6b0:   5d                      pop    %rbp\n 6b1:   c3                      retq\n 6b2:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)\n 6b9:   00 00 00\n 6bc:   0f 1f 40 00             nopl   0x0(%rax)\n\n00000000000006c0 <__libc_csu_init>:\n 6c0:   41 57                   push   %r15\n 6c2:   41 56                   push   %r14\n 6c4:   49 89 d7                mov    %rdx,%r15\n 6c7:   41 55                   push   %r13\n 6c9:   41 54                   push   %r12\n 6cb:   4c 8d 25 e6 06 20 00    lea    0x2006e6(%rip),%r12        # 200db8 <__frame_dummy_init_array_entry>\n 6d2:   55                      push   %rbp\n 6d3:   48 8d 2d e6 06 20 00    lea    0x2006e6(%rip),%rbp        # 200dc0 <__init_array_end>\n 6da:   53                      push   %rbx\n 6db:   41 89 fd                mov    %edi,%r13d\n 6de:   49 89 f6                mov    %rsi,%r14\n 6e1:   4c 29 e5                sub    %r12,%rbp\n 6e4:   48 83 ec 08             sub    $0x8,%rsp\n 6e8:   48 c1 fd 03             sar    $0x3,%rbp\n 6ec:   e8 ff fd ff ff          callq  4f0 <_init>\n 6f1:   48 85 ed                test   %rbp,%rbp\n 6f4:   74 20                   je     716 <__libc_csu_init+0x56>\n 6f6:   31 db                   xor    %ebx,%ebx\n 6f8:   0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)\n 6ff:   00\n 700:   4c 89 fa                mov    %r15,%rdx\n 703:   4c 89 f6                mov    %r14,%rsi\n 706:   44 89 ef                mov    %r13d,%edi\n 709:   41 ff 14 dc             callq  *(%r12,%rbx,8)\n 70d:   48 83 c3 01             add    $0x1,%rbx\n 711:   48 39 dd                cmp    %rbx,%rbp\n 714:   75 ea                   jne    700 <__libc_csu_init+0x40>\n 716:   48 83 c4 08             add    $0x8,%rsp\n 71a:   5b                      pop    %rbx\n 71b:   5d                      pop    %rbp\n 71c:   41 5c                   pop    %r12\n 71e:   41 5d                   pop    %r13\n 720:   41 5e                   pop    %r14\n 722:   41 5f                   pop    %r15\n 724:   c3                      retq\n 725:   90                      nop\n 726:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)\n 72d:   00 00 00\n\n0000000000000730 <__libc_csu_fini>:\n 730:   f3 c3                   repz retq\n\nDisassembly of section .fini:\n\n0000000000000734 <_fini>:\n 734:   48 83 ec 08             sub    $0x8,%rsp\n 738:   48 83 c4 08             add    $0x8,%rsp\n 73c:   c3                      retq\n```\n\n\n## debug_frame\n该程序的debug_frame或eh_frame内容如下.\n\nCIE 可以是全局公共的, 被共享. FDE相当于继承或者共享CIE的内容\n\n```text\nuwa@wzp-B365-HD3:~$ readelf --debug-dump=frames ./example\n\n.eh_frame 节的内容:\n\n\n00000000 0000000000000014 00000000 CIE\n  Version:               1\n  Augmentation:          \"zR\"\n  Code alignment factor: 1\n  Data alignment factor: -8\n  Return address column: 16\n  Augmentation data:     1b\n  DW_CFA_def_cfa: r7 (rsp) ofs 8\n  DW_CFA_offset: r16 (rip) at cfa-8\n  DW_CFA_undefined: r16 (rip)\n\n00000018 0000000000000014 0000001c FDE cie=00000000 pc=0000000000000540..000000000000056b\n  DW_CFA_nop\n  DW_CFA_nop\n  DW_CFA_nop\n  DW_CFA_nop\n  DW_CFA_nop\n  DW_CFA_nop\n  DW_CFA_nop\n\n00000030 0000000000000014 00000000 CIE\n  Version:               1\n  Augmentation:          \"zR\"\n  Code alignment factor: 1\n  Data alignment factor: -8\n  Return address column: 16\n  Augmentation data:     1b\n  DW_CFA_def_cfa: r7 (rsp) ofs 8\n  DW_CFA_offset: r16 (rip) at cfa-8\n  DW_CFA_nop\n  DW_CFA_nop\n\n00000048 0000000000000024 0000001c FDE cie=00000030 pc=0000000000000510..0000000000000530\n  DW_CFA_def_cfa_offset: 16\n  DW_CFA_advance_loc: 6 to 0000000000000516\n  DW_CFA_def_cfa_offset: 24\n  DW_CFA_advance_loc: 10 to 0000000000000520\n  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)\n  DW_CFA_nop\n  DW_CFA_nop\n  DW_CFA_nop\n  DW_CFA_nop\n\n00000070 0000000000000014 00000044 FDE cie=00000030 pc=0000000000000530..0000000000000538\n  DW_CFA_nop\n  DW_CFA_nop\n  DW_CFA_nop\n  DW_CFA_nop\n  DW_CFA_nop\n  DW_CFA_nop\n  DW_CFA_nop\n\n00000088 000000000000001c 0000005c FDE cie=00000030 pc=000000000000064a..0000000000000681\n  DW_CFA_advance_loc: 1 to 000000000000064b\n  DW_CFA_def_cfa_offset: 16\n  DW_CFA_offset: r6 (rbp) at cfa-16\n  DW_CFA_advance_loc: 3 to 000000000000064e\n  DW_CFA_def_cfa_register: r6 (rbp)\n  DW_CFA_advance_loc: 50 to 0000000000000680\n  DW_CFA_def_cfa: r7 (rsp) ofs 8\n  DW_CFA_nop\n  DW_CFA_nop\n  DW_CFA_nop\n\n000000a8 000000000000001c 0000007c FDE cie=00000030 pc=0000000000000681..000000000000069d\n  DW_CFA_advance_loc: 1 to 0000000000000682\n  DW_CFA_def_cfa_offset: 16\n  DW_CFA_offset: r6 (rbp) at cfa-16\n  DW_CFA_advance_loc: 3 to 0000000000000685\n  DW_CFA_def_cfa_register: r6 (rbp)\n  DW_CFA_advance_loc: 23 to 000000000000069c\n  DW_CFA_def_cfa: r7 (rsp) ofs 8\n  DW_CFA_nop\n  DW_CFA_nop\n  DW_CFA_nop\n\n000000c8 000000000000001c 0000009c FDE cie=00000030 pc=000000000000069d..00000000000006b2\n  DW_CFA_advance_loc: 1 to 000000000000069e\n  DW_CFA_def_cfa_offset: 16\n  DW_CFA_offset: r6 (rbp) at cfa-16\n  DW_CFA_advance_loc: 3 to 00000000000006a1\n  DW_CFA_def_cfa_register: r6 (rbp)\n  DW_CFA_advance_loc: 16 to 00000000000006b1\n  DW_CFA_def_cfa: r7 (rsp) ofs 8\n  DW_CFA_nop\n  DW_CFA_nop\n  DW_CFA_nop\n\n000000e8 0000000000000044 000000bc FDE cie=00000030 pc=00000000000006c0..0000000000000725\n  DW_CFA_advance_loc: 2 to 00000000000006c2\n  DW_CFA_def_cfa_offset: 16\n  DW_CFA_offset: r15 (r15) at cfa-16\n  DW_CFA_advance_loc: 2 to 00000000000006c4\n  DW_CFA_def_cfa_offset: 24\n  DW_CFA_offset: r14 (r14) at cfa-24\n  DW_CFA_advance_loc: 5 to 00000000000006c9\n  DW_CFA_def_cfa_offset: 32\n  DW_CFA_offset: r13 (r13) at cfa-32\n  DW_CFA_advance_loc: 2 to 00000000000006cb\n  DW_CFA_def_cfa_offset: 40\n  DW_CFA_offset: r12 (r12) at cfa-40\n  DW_CFA_advance_loc: 8 to 00000000000006d3\n  DW_CFA_def_cfa_offset: 48\n  DW_CFA_offset: r6 (rbp) at cfa-48\n  DW_CFA_advance_loc: 8 to 00000000000006db\n  DW_CFA_def_cfa_offset: 56\n  DW_CFA_offset: r3 (rbx) at cfa-56\n  DW_CFA_advance_loc: 13 to 00000000000006e8\n  DW_CFA_def_cfa_offset: 64\n  DW_CFA_advance_loc: 50 to 000000000000071a\n  DW_CFA_def_cfa_offset: 56\n  DW_CFA_advance_loc: 1 to 000000000000071b\n  DW_CFA_def_cfa_offset: 48\n  DW_CFA_advance_loc: 1 to 000000000000071c\n  DW_CFA_def_cfa_offset: 40\n  DW_CFA_advance_loc: 2 to 000000000000071e\n  DW_CFA_def_cfa_offset: 32\n  DW_CFA_advance_loc: 2 to 0000000000000720\n  DW_CFA_def_cfa_offset: 24\n  DW_CFA_advance_loc: 2 to 0000000000000722\n  DW_CFA_def_cfa_offset: 16\n  DW_CFA_advance_loc: 2 to 0000000000000724\n  DW_CFA_def_cfa_offset: 8\n  DW_CFA_nop\n\n00000130 0000000000000010 00000104 FDE cie=00000030 pc=0000000000000730..0000000000000732\n  DW_CFA_nop\n  DW_CFA_nop\n  DW_CFA_nop\n\n00000144 ZERO terminator\n\n```\n\n- `DW_CFA_def_cfa: rsp, 16`：表示 CFA 是栈指针 `rsp` 加上 16 字节的偏移。\n- `DW_CFA_offset: rip at cfa-8`：表示返回地址（`rip`）保存于栈顶 CFA - 8 位置。\n\nFDE 2 (函数 funcB)\n```yaml\n000000a8 000000000000001c 0000007c FDE cie=00000030 pc=0000000000000681..000000000000069d\n  DW_CFA_advance_loc: 1 to 0000000000000682\n  DW_CFA_def_cfa_offset: 16\n  DW_CFA_offset: r6 (rbp) at cfa-16\n  DW_CFA_advance_loc: 3 to 0000000000000685\n  DW_CFA_def_cfa_register: r6 (rbp)\n  DW_CFA_advance_loc: 23 to 000000000000069c\n  DW_CFA_def_cfa: r7 (rsp) ofs 8\n  DW_CFA_nop\n  DW_CFA_nop\n  DW_CFA_nop\n```\n\n- **`DW_CFA_advance_loc`** 表示 PC（Program Counter，程序计数器）向前推进 1 个字节，到地址 `0x682`。这通常意味着程序的执行已经进入了新的指令，可能是一个新的函数调用或栈操作。\n- **`DW_CFA_def_cfa_offset`** 表示当前 **CFA** 的偏移量为 **16** 字节。\n\n如果当前 `SP` 的值为 **`0x1000`**，那么 **CFA** 的计算如下：\n```ini\nCFA = SP + 16 = 0x1000 + 0x10 = 0x1010\n```\n\n**DW_CFA_offset: r6 (rbp) at cfa-16** 描述了 **`rbp`（帧指针寄存器）** 的保存位置。\n假设当前 CFA = 0x1008，那么 `rbp` 保存的位置为：\n```text\nrbp 保存位置 = 0x1008 - 16 = 0x0FF8\n```\n\n\n## 借助DWARF信息进行栈回溯的基本流程\n\n由以上信息可知, DWARF通过 `.eh_frame` 和 `.debug_frame` 段来提供调用帧信息(CFI)来描述栈帧布局。 调试工具或者回溯工具可以利用DWARF提供的CFI, 对调用栈进行回溯, 以获得造成崩溃的指令的调用链, 并且可以获取每一帧的寄存器信息.\n\n- **获取当前 PC 和栈指针（SP）**：首先从当前的执行上下文中获取程序计数器（PC）和栈指针（SP）。\n- **读取 CFI 信息**：使用 DWARF 的 CFI 信息找到当前栈帧中保存的返回地址和寄存器位置。\n- **恢复上一个栈帧**：根据 CFI 信息恢复上一个栈帧的寄存器状态和返回地址。\n- **重复过程**：继续回溯，直到到达栈的顶端。\n\n1. **初始化上下文**：在崩溃时或调试时，首先获取当前的上下文状态，包括当前的栈指针（`rsp`）、程序计数器（`rip`）等寄存器状态。\n2. **获取 CFA**：根据 DWARF 中的 **CFI 信息**，利用指令如 `DW_CFA_def_cfa` 来计算当前栈帧的 **CFA**。\n3. **恢复寄存器状态**：\n    - 从栈中恢复保存的寄存器信息（如 `rbp`、`rip`），以确定上一个栈帧的地址。\n    - 通过 `DW_CFA_offset` 指令找到每个寄存器的位置，并从栈中恢复它们的值。\n4. **回溯到上一个栈帧**：\n    - 利用恢复的返回地址寄存器（如 `rip`），确定上一个函数的程序计数器（PC）。\n    - 根据新的 PC 继续从 DWARF 信息中获取该栈帧的 CFI，更新 CFA 和寄存器状态。\n5. **逐帧回溯**：重复上述过程，直到到达主函数或回溯到栈的最底部。\n"
}
