debugger(三):dwarf 文件

〇、前言

事实上,一个成熟的 debugger 是不会利用 break 0xADDR 类似的命令来打断点的,这个需要改进,使得它可以直接利用函数名、行数等来打断点。这就需要生成编译信息,只需要在编译的时候,在目标文件中加以下参数:

# 添加编译器标志
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -gdwarf-2")

这样,目标文件就携带了 dwarf 格式的 debug info(我们还禁止了优化,这有利于调试)。

一、ELF & DWARF

DWARF 和 ELF 的关系与区别

DWARF

  • DWARF 是一种关于调试信息的标准格式,用于在编译时生成的调试信息中描述程序的各种数据结构。这包括但不限于变量的名称、类型、存储位置,函数的名称、参数列表和源代码中的行号
  • DWARF 是与平台无关的,这意味着它可以用在各种不同的操作系统和硬件上。

ELF (Executable and Linkable Format)

  • ELF 是一种常用的文件格式,用于定义在类 Unix 系统(如 Linux)上运行的可执行文件、可重定位的代码和共享库。
  • ELF 文件包含程序的代码和数据,并定义了一个文件结构,这个结构描述了如何在运行时将程序加载到内存中。

关系与区别

  • DWARF 信息通常被嵌入到 ELF 文件中(在 .debug 节),作为程序的一部分。这意味着 ELF 文件作为容器,包含了执行程序所需的机器代码和(如果编译时指定)调试信息。
  • 在调试过程中,调试器利用 ELF 文件中的 DWARF 信息来提供程序执行的详细视图,比如变量值、程序执行的当前行等
  • 虽然 DWARF 和 ELF 通常一起使用,但它们是独立的标准:DWARF 关注于描述调试数据,ELF 关注于程序的布局和执行

二、DWARF line table & DWARF debug info

在讨论 DWARF 格式的调试信息时,编译单元(Compilation Unit, CU)和行表(Line Table)是两个核心概念。这些信息极大地促进了源码级调试,使调试器能够有效地将执行的机器代码映射回源代码。下面详细介绍这两个概念:

编译单元(Compilation Unit, CU)

编译单元通常指的是单个源文件及其相关包含的文件(通过预处理器展开)在编译过程中形成的单元。在 DWARF 调试信息中,每个编译单元生成一组特定的调试信息记录,这些记录描述了该源文件中定义的数据结构、函数、变量、类型等。

编译单元的主要内容包括:
  1. 全局变量和类型定义:全局作用域中定义的变量和类型。
  2. 局部变量和类型定义:函数内部定义的变量和类型。
  3. 子程序信息:包括函数和方法的定义,如函数名、返回类型、参数信息以及函数内的代码结构。
  4. 源文件和目录信息:描述编译单元对应的源文件和其在文件系统中的位置。

编译单元的信息对于调试器来说至关重要,因为它们提供了代码结构的详细视图,使得调试器可以准确地知道在任何时刻程序正在执行的代码部分。

行表(Line Table)

行表是 DWARF 调试信息中的一部分,它为编译单元中的每一行源代码提供一个或多个对应的机器指令的映射。这允许调试器将正在执行的机器指令精确地对应到源代码中的具体行。

行表的关键作用:
  1. 源代码到机器代码的映射:行表记录了源代码行与生成的机器代码之间的对应关系。这包括代码地址的起始点和源代码行号。
  2. 断点设置:当在源代码中设置断点时,调试器使用行表来确定应该在哪个具体的机器指令地址上设置断点。
  3. 步进和步过操作:在单步执行(步进)和执行至下一行(步过)时,调试器利用行表来确定执行流程应该停留或跳过的代码段。

行表通常包含以下信息:

  • 地址:对应机器代码的开始地址。
  • 行号:源代码中的行号。
  • 文件名:源代码的文件名,尤其是在项目中包含多个文件时。
  • 其他标志:如是否是语句的开始、是否是基本块的开始等。

而 CU 就是 debug info 的基本组成,每一个 CU(事实上就是一个源代码文件)组成 debug info段的一部分。编译单元是调试信息的构建块,每个编译单元封装了一个源文件的所有相关调试信息。这种组织方式不仅有助于维护信息的结构性和可查询性,也使得调试过程更为高效和直观。

假设一个可执行程序(包含了 DWARF 格式的调试信息)包含了很多的源代码文件,它的 debug_info 可能如下:

objdump --dwarf=info hello
hello:     file format elf64-x86-64Contents of the .debug_info section:Compilation Unit @ offset 0x0:Length:        0x2612 (32-bit)Version:       2Abbrev Offset: 0x0Pointer Size:  8<0><b>: Abbrev Number: 1 (DW_TAG_compile_unit)<c>   DW_AT_producer    : (indirect string, offset: 0x109b): GNU C++17 11.4.0 -mtune=generic -march=x86-64 -g -gdwarf-2 -O0 -fasynchronous-unwind-tables -fstack-protector-strong -fstack-clash-protection -fcf-protection<10>   DW_AT_language    : 4	(C++)<11>   DW_AT_name        : (indirect string, offset: 0x4ba): /home/luyoung/mydebugger/examples/hello.cpp<15>   DW_AT_comp_dir    : (indirect string, offset: 0x154): /home/luyoung/mydebugger/build/examples<19>   DW_AT_low_pc      : 0x1189<21>   DW_AT_high_pc     : 0x1220<29>   DW_AT_stmt_list   : 0x0<1><2d>: Abbrev Number: 2 (DW_TAG_namespace)<2e>   DW_AT_name        : std<32>   DW_AT_decl_file   : 6<33>   DW_AT_decl_line   : 278<35>   DW_AT_decl_column : 11<36>   DW_AT_sibling     : <0xbab><2><3a>: Abbrev Number: 3 (DW_TAG_namespace)<3b>   DW_AT_name        : (indirect string, offset: 0x859): __cxx11<3f>   DW_AT_decl_file   : 6<40>   DW_AT_decl_line   : 302<42>   DW_AT_decl_column : 65<43>   DW_AT_export_symbols: 1<2><44>: Abbrev Number: 4 (DW_TAG_imported_module)...<2><25f2>: Abbrev Number: 0<1><25f3>: Abbrev Number: 88 (DW_TAG_subprogram)<25f4>   DW_AT_external    : 1<25f5>   DW_AT_name        : (indirect string, offset: 0x6b3): main<25f9>   DW_AT_decl_file   : 1<25fa>   DW_AT_decl_line   : 2<25fb>   DW_AT_decl_column : 5<25fc>   DW_AT_type        : <0xd3d><2600>   DW_AT_low_pc      : 0x1189<2608>   DW_AT_high_pc     : 0x11b1<2610>   DW_AT_frame_base  : 0xc0 (location list)<2614>   DW_AT_GNU_all_tail_call_sites: 1<1><2615>: Abbrev Number: 0

这些信息很难看,非常不利于人类阅读,因此我们可以利用更好的工具来理解这些信息,这些工具对这些信息进行了组织,比如 dwarfdump:


.debug_infoCOMPILE_UNIT<header overall offset = 0x00000000>:
< 0><0x0000000b>  DW_TAG_compile_unitDW_AT_producer              GNU C++17 11.4.0 -mtune=generic -march=x86-64 -g -gdwarf-2 -O0 -fasynchronous-unwind-tables -fstack-protector-strong -fstack-clash-protection -fcf-protectionDW_AT_language              DW_LANG_C_plus_plusDW_AT_name                  /home/luyoung/mydebugger/examples/hello.cppDW_AT_comp_dir              /home/luyoung/mydebugger/build/examplesDW_AT_low_pc                0x00001189DW_AT_high_pc               0x00001220DW_AT_stmt_list             0x00000000LOCAL_SYMBOLS:
< 1><0x0000002d>    DW_TAG_namespaceDW_AT_name                  stdDW_AT_decl_file             0x00000006 /usr/include/x86_64-linux-gnu/c++/11/bits/c++config.hDW_AT_decl_line             0x00000116DW_AT_decl_column           0x0000000bDW_AT_sibling               <0x00000bab>
< 2><0x0000003a>      DW_TAG_namespace
.
.
.
.debug_line: line number info for a single cu
Source lines (from CU-DIE at .debug_info offset 0x0000000b):NS new statement, BB new basic block, ET end of text sequencePE prologue end, EB epilogue beginIS=val ISA number, DI=val discriminator value
<pc>        [lno,col] NS BB ET PE EB IS= DI= uri: "filepath"
0x00001189  [   2,12] NS uri: "/home/luyoung/mydebugger/examples/hello.cpp"
0x00001191  [   3,16] NS
0x000011aa  [   4,10] NS
0x000011af  [   5, 1] NS
0x000011b1  [   5, 1] NS
0x000011c3  [   5, 1] NS
0x000011c9  [   5, 1] DI=0x1
0x000011d2  [  74,25] NS uri: "/usr/include/c++/11/iostream"
0x00001204  [   5, 1] NS uri: "/home/luyoung/mydebugger/examples/hello.cpp"
0x00001207  [   5, 1] NS
0x0000120f  [   5, 1] NS
0x00001220  [   5, 1] NS ET
.
.
.
.debug_str
name at offset 0x00000000, length    6 is 'getenv'
name at offset 0x00000007, length   16 is '__isoc99_vwscanf'
name at offset 0x00000018, length   13 is 'uint_fast16_t'
name at offset 0x00000026, length    7 is '__debug'
name at offset 0x0000002e, length   17 is 'int_p_cs_precedes'
name at offset 0x00000040, length   42 is '_ZNSt15__exception_ptr13exception_ptrC4EPv'
name at offset 0x0000006b, length    8 is 'strtoull'
name at offset 0x00000074, length   16 is '__uint_least64_t'
name at offset 0x00000085, length    7 is 'wcsxfrm'
name at offset 0x0000008d, length   51 is '_ZNSt15__exception_ptr13exception_ptr10_M_releaseEv'
name at offset 0x000000c1, length   14 is '~exception_ptr'
name at offset 0x000000d0, length    4 is 'atol'
name at offset 0x000000d5, length    9 is '_shortbuf'
name at offset 0x000000df, length   10 is '_IO_lock_t'
name at offset 0x000000ea, length    7 is 'setvbuf'
name at offset 0x000000f2, length    9 is 'gp_offset'
.
.
.
.debug_arangesCOMPILE_UNIT<header overall offset = 0x00000000>:
< 0><0x0000000b>  DW_TAG_compile_unitDW_AT_producer              GNU C++17 11.4.0 -mtune=generic -march=x86-64 -g -gdwarf-2 -O0 -fasynchronous-unwind-tables -fstack-protector-strong -fstack-clash-protection -fcf-protectionDW_AT_language              DW_LANG_C_plus_plusDW_AT_name                  /home/luyoung/mydebugger/examples/hello.cppDW_AT_comp_dir              /home/luyoung/mydebugger/build/examplesDW_AT_low_pc                0x00001189DW_AT_high_pc               0x00001220DW_AT_stmt_list             0x00000000arange starts at 0x00001189, length of 0x00000097, cu_die_offset = 0x0000000b
arange end.debug_frame is not present

可以看到,dwarfdump 输出的信息更好理解,它对信息进行了分类整理。还必须要理解的是,这里的 pc 地址都是 offset,在使用的时候需要加上 load_addr,另外:

<pc>        [lno,col] NS BB ET PE EB IS= DI= uri: "filepath"
0x00001189  [   2,12] NS uri: "/home/luyoung/mydebugger/examples/hello.cpp"
0x00001191  [   3,16] NS
0x000011aa  [   4,10] NS
0x000011af  [   5, 1] NS
0x000011b1  [   5, 1] NS
0x000011c3  [   5, 1] NS
0x000011c9  [   5, 1] DI=0x1
0x000011d2  [  74,25] NS uri: "/usr/include/c++/11/iostream"
0x00001204  [   5, 1] NS uri: "/home/luyoung/mydebugger/examples/hello.cpp"
0x00001207  [   5, 1] NS
0x0000120f  [   5, 1] NS
0x00001220  [   5, 1] NS ET

和源代码相对应,这为源代码 level 调试提供了基础:

#1 #include <iostream>
#2 int main() {
#3  std::cerr << "hello,world0.\n";
#4  return 0;
#5}

如果我们想在源代码第三行处打断点,就应该把地址定在 0x00001191,这也可以在 elf 中找到依据:

0000000000001189 <main>:1189:       f3 0f 1e fa             endbr64 118d:       55                      push   %rbp118e:       48 89 e5                mov    %rsp,%rbp1191:       48 8d 05 6c 0e 00 00    lea    0xe6c(%rip),%rax        # 2004 <_IO_stdin_used+0x4>1198:       48 89 c6                mov    %rax,%rsi119b:       48 8d 05 7e 2e 00 00    lea    0x2e7e(%rip),%rax        # 4020 <_ZSt4cerr@GLIBCXX_3.4>11a2:       48 89 c7                mov    %rax,%rdi11a5:       e8 d6 fe ff ff          call   1080 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>11aa:       b8 00 00 00 00          mov    $0x0,%eax11af:       5d                      pop    %rbp11b0:       c3               

另外还可以看到,编译单元中的:

COMPILE_UNIT<header overall offset = 0x00000000>:
< 0><0x0000000b>  DW_TAG_compile_unit
...DW_AT_low_pc                0x00001189DW_AT_high_pc               0x00001220DW_AT_stmt_list             0x00000000...

它们的 DW_AT_low_pcDW_AT_high_pc 和 line table 中的范围一致,因此这也是重要的信息。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/851289.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQL的执行顺序

1、连接与权限校验&#xff1a; 客户端与SQL服务器建立连接&#xff0c;进行账号认证和权限校验。 2、查询缓存&#xff1a; 服务器首先检查查询缓存&#xff0c;如果命中缓存&#xff0c;则直接返回结果。 3、SQL解析与预处理&#xff1a; 如果没有命中缓存&#xff0c;服务器…

在 Android App 里使用 C 代码 - NDK

原生开发套件 (NDK) 是一套工具&#xff0c;使能够在 Android 应用中使用 C 和 C 代码&#xff0c;并提供众多平台库&#xff0c;可使用这些平台库管理原生 activity 和访问实体设备组件&#xff0c;例如传感器和触控输入。 NDK 可能不适合大多数 Android 编程初学者&#xff…

追觅科技2025校园招聘测评已发(真题)

&#x1f4e3;追觅科技 2025校园招聘测评已发&#xff0c;正在申请的小伙伴看过来哦&#x1f440; ㊙本次校招面向全球于2023年7月 - 2025年12月期间毕业的同学&#xff0c;开放了四大类岗位&#xff1a;营销类、研发类、制作供应类、职能类~ ✅测评解析 &#x1f449; 测评自…

git的核心概念(合作开发时必须了解)

Git是我们管理项目的重要工作&#xff0c;它有几个核心概念&#xff0c;核心概念之间又有相关操作&#xff0c;在开发前应该学会。 4个概念&#xff1a;工作区、暂存区、本地仓库、远程仓库。 工作区&#xff1a;你的工作目录&#xff0c;可能是项目目录。 操作&#xff1a; …

芒果YOLOv10改进38:写作篇:一文了解YOLOv10如何打印FPS指标

只需订阅这一个专栏即可阅读:芒果YOLOv10所有改进内容 💡🚀🚀🚀本博客内含改进源代码,按步骤操作运行改进后的代码即可 💡更方便的统计更多实验数据,方便写作 新增YOLOv10打印FPS指标 完善(一键YOLOv10打印FPS指标) 文章目录 完善(一键YOLOv10打印FPS指标)YOLO…

Linux系统编程(十一)线程、线程控制

线程 一、线程概念&#xff1a; ps -eLf 查看线程号&#xff08;cpu 执行的最小单位&#xff09; 二、Linux内核线程实现原理 三、三级映射&#xff08;三级页表&#xff09; 进程PCB-->页面&#xff08;可看成数组&#xff0c;首地址位于PCB中&#xff09;--》页表--》页…

计算机系统基础笔记(10)——浮点数

前言 结合第四篇观看 效果更好^^ 数值型分为两类 定点数 整数小数 浮点数 注意&#xff1a;小数点不用占用二进制位 第一部分 编码 1.小数的二进制表示 位置计数法&#xff1a;小数点左边的权重是正指数&#xff0c;右边的数是负指数&#xff0c;如图 由此可知&#xff0…

贪心算法例子

贪心算法概述 贪心算法是一种在每一步选择中都做出局部最优选择的算法,以期望通过一系列局部最优选择达到全局最优。贪心算法在许多优化问题中表现良好,特别是在某些特定类型的问题中能够保证找到最优解。 活动选择问题(Activity Selection Problem)背包问题(贪心解法)霍…

MySQL数据库(二)和java复习

一.MySQL数据库学习(二) (一).DQL查询数据 DQL&#xff08;Data Query Language&#xff09;是用于从数据库中检索数据的语言。常见的 DQL 语句包括 SELECT、FROM、WHERE、GROUP BY、HAVING 和 ORDER BY 等关键字&#xff0c;用于指定要检索的数据、数据源、过滤条件、分组方…

格式化后硬盘数据能恢复吗?硬盘数据恢复这样做!

硬盘是电脑中必备的数据存储设备&#xff0c;另外还有移动硬盘。移动硬盘存储空间非常大、性价比高、便于携带&#xff0c;给我们带来和很多便利。但是和其他存储设备一样&#xff0c;各种硬盘也会出现各种问题&#xff0c;比如常见的格式化硬盘导致数据丢失的问题。 怎么样恢复…

【软考的系统分析师的考题考点解析2025】

2024-2025系统分析师考试&#xff08;简称软考&#xff09;是计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试中的高级资格考试&#xff0c;主要考察考生在系统分析、系统设计、项目管理等方面的知识和技能。以下是软考系统分析师的常见考点、考题和重点&#x…

【源码】Spring Data JPA原理解析之事务执行原理

Spring Data JPA系列 1、SpringBoot集成JPA及基本使用 2、Spring Data JPA Criteria查询、部分字段查询 3、Spring Data JPA数据批量插入、批量更新真的用对了吗 4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作 5、Spring Data JPA自定…

Python 虚拟环境 + 嵌入式 部署方案

Python 虚拟环境 嵌入式 部署方案 开发阶段1. 在虚拟环境下开发 Python 项目 部署阶段1. 创建项目文件夹2. 准备嵌入器 Python 解释器3. 处理第三方库4. 修改 ._pth 文件添加 Python 运行环境 5. 添加启动 bat 脚本最终目录结构参考资料 开发阶段 1. 在虚拟环境下开发 Python…

PHP 寿光蔬菜大棚宣传平台-计算机毕业设计源码88288

摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于寿光蔬菜大棚宣传平台当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了寿光蔬菜大棚宣传平台&#xff0c;它彻底…

ISO 19115-2:2019 附录C XML 模式实现

C.1 XML 模式 本文件中定义的 UML 模型的 XML 模式在 ISO/TS 19115-3 中定义的适当 XML 命名空间中提供。新增内容包括: 命名空间前缀模式文件名Metadata for ACquisition (mac)acquisitionInformationImagery.xsdMetadata for Resource Content (mrc)contentInformationImag…

BPF:BCC(BPF Compiler Collection)工具集认知

写在前面 博文内容为 《BPF Performance Tools》 读书笔记整理内容涉及 BCC 工具整体介绍理解不足小伙伴帮忙指正 &#x1f603;,生活加油 不必太纠结于当下&#xff0c;也不必太忧虑未来&#xff0c;当你经历过一些事情的时候&#xff0c;眼前的风景已经和从前不一样了。——村…

内存EDAC在AMD异构场景的应用

在异构系统中&#xff0c;AMD通过将CPU与GPU的数据织物网络通过定制的xGMI链接连接&#xff0c;实现了统一访问&#xff0c;使得GPU节点的数据织物网络如同CPU节点一样可访问。这样的设计使得系统能够高效地整合CPU与GPU资源&#xff0c;提升数据处理速度和效率&#xff0c;特别…

从河流到空气,BL340工控机助力全面环保监测网络构建

在环保监测领域&#xff0c;智能化、高效率的监测手段正逐步成为守护绿水青山的新常态。其中&#xff0c;ARMxy工业计算机BL340凭借其强大的处理能力、高度的灵活性以及广泛的兼容性&#xff0c;在水质监测站、空气质量检测、噪音污染监控等多个环保应用场景中脱颖而出&#xf…

MySQL深分页优化

MySQL中的深分页问题通常是指当我们通过LIMIT语句查询数据&#xff0c;尤其是在翻到较后面的页码时&#xff0c;性能会急剧下降。例如&#xff0c;查询第1000页的数据&#xff0c;每页10条&#xff0c;系统需要跳过前9990条数据&#xff0c;然后才能获取到所需的记录&#xff0…

Vue基础面试题(一)

1.Vue的基本原理 Vue.js的核心原理在于其响应式的数据绑定机制&#xff0c;当创建一个Vue实例时&#xff0c;Vue会遍历每个属性&#xff0c;用Object.defineProperty转化为getter和setter。这样使得Vue可以追踪属性的变化&#xff0c;在属性被修改和访问时通知变化。每个组件也…