浅析从DWARF到BTF @龙蜥社区eBPF SIG

一、背景

一个程序会经历编码、编译、运行的过程,但所有的开发几乎都不可能是一帆风顺的,总会有些意想不到的错误,这时便需要调试。那么调试器使用的调试信息是从哪里来的呢?答案就是从编译后的文件中来的(依赖编译的时候使用特定的编译选项,如GCC使用“-g”)。

在BPF程序开发过程中,我们也需要这类调试信息,首先LLVM 能够将调试信息附加到生成的 BPF 目标文件中。默认情况下,这是 DWARF 格式。但BPF 使用的是高度简化的版本称为 BTF。我们首先需要将DWARF 转换为 BTF。BPF程序加载到内核后,内核验证器将验证 BTF 数据的正确性并跟踪 BTF 数据包含的数据类型。

DWARF的全称是"Debugging With Attributed Record Formats",遵从GNU FDL授权。由以上述描述可知,DWARF是一种调试文件格式,许多编译器和调试器均使用DWARF来支持源代码级调试。简单来说,DWARF记录了源代码、二进制程序以及它们之间存在的联系。

本文主要从三个方面介绍DWARF:

  1. DWARF在elf文件存在哪些段,以及每个段的含义;
  2. 采用具体的实例,介绍了.deubg_str所包含的内容;
  3. 重点介绍了.debug_info段,其是理解BTF的关键;

二、elf中包含的DWARF段

DWARF在elf文件中共存在12个段。就本文而言,12个段重要的段是:.debug_info段和.deubg_str段,后面也是着重讲解这两个段。这两个段也是libbpf BTF最感兴趣的两个段。

三、.debug_str解析

.debug_str段主要包含了源代码中的各种可读文本。以下面的C代码为例:

struct test_struct{
    int testa;
    char testb;
}test_s;
int main()
{
    return 0;
}
  • 首先将该C代码编译成二进制,即gcc -g -o test test.c;
  • 运行命令:readelf -ws test,可以得到如下的文本。核心文本包括了test_s(结构体变量)、test_struct(结构体)、testa(结构体成员)、testb(结构体成员)、char(结构体成员类型)等。但是如果变量的名字是单个字符,则不会单独存储。
  0x00000000 74657374 5f73006d 61696e00 2f686f6d test_s.main./hom
  0x00000010 652f6368 656e6773 68757969 2e637379 e/chengshuyi.csy
  0x00000020 2f706168 6f6c6500 74657374 5f737472 /pahole.test_str
  0x00000030 75637400 474e5520 4320342e 382e3520 uct.GNU C 4.8.5 
  0x00000040 32303135 30363233 20285265 64204861 20150623 (Red Ha
  0x00000050 7420342e 382e352d 32382920 2d6d7475 t 4.8.5-28) -mtu
  0x00000060 6e653d67 656e6572 6963202d 6d617263 ne=generic -marc
  0x00000070 683d7838 362d3634 202d6700 74657374 h=x86-64 -g.test
  0x00000080 2e630074 65737461 00746573 74620063 .c.testa.testb.c
  0x00000090 68617200                            har.

四、.debug_info解析

.debug_info包含了DWARF的DIEs,DIE则重在描述数据类型。DIE包含一个tag,指定了DIE所描述的类型。DIE还包含属性列表,用于描述该类型的具体信息。

本节主要详细介绍了两种不同tag的DIE,分别是结构体类型(对应的tag是DW_TAG_structure_type)和引用类型(只介绍DW_TAG_const_type)。理解这两种DIE,非常有助于理解libbpf BTF的去重算法。

结构体类型
下面是一段简单的C语言代码。

struct test{
    int a;
    char b;
}test;
int main()
{
    return 0;
}
  • 首先将该C代码编译成二进制,即gcc -g -o test test.c;
  • 运行命令:readelf -wi test,可以得到如下文本:
<1><2d>: Abbrev Number: 2 (DW_TAG_structure_type)
    <2e>   DW_AT_name        : (indirect string, offset: 0x1c): test
    <32>   DW_AT_byte_size   : 8
    <33>   DW_AT_decl_file   : 1
    <34>   DW_AT_decl_line   : 1
    <35>   DW_AT_sibling     : <0x4e>
 <2><39>: Abbrev Number: 3 (DW_TAG_member)
    <3a>   DW_AT_name        : a
    <3c>   DW_AT_decl_file   : 1
    <3d>   DW_AT_decl_line   : 2
    <3e>   DW_AT_type        : <0x4e>
    <42>   DW_AT_data_member_location: 0
 <2><43>: Abbrev Number: 3 (DW_TAG_member)
    <44>   DW_AT_name        : b
    <46>   DW_AT_decl_file   : 1
    <47>   DW_AT_decl_line   : 3
    <48>   DW_AT_type        : <0x55>
    <4c>   DW_AT_data_member_location: 4
 <2><4d>: Abbrev Number: 0
 <1><4e>: Abbrev Number: 4 (DW_TAG_base_type)
    <4f>   DW_AT_byte_size   : 4
    <50>   DW_AT_encoding    : 5        (signed)
    <51>   DW_AT_name        : int
 <1><55>: Abbrev Number: 5 (DW_TAG_base_type)
    <56>   DW_AT_byte_size   : 1
    <57>   DW_AT_encoding    : 6        (signed char)
    <58>   DW_AT_name        : (indirect string, offset: 0x75): char
 <1><79>: Abbrev Number: 7 (DW_TAG_variable)
    <7a>   DW_AT_name        : (indirect string, offset: 0x1c): test
    <7e>   DW_AT_decl_file   : 1
    <7f>   DW_AT_decl_line   : 4
    <80>   DW_AT_type        : <0x2d>
    <84>   DW_AT_external    : 1
    <84>   DW_AT_location    : 9 byte block: 3 20 10 60 0 0 0 0 0       (DW_OP_addr: 601020)
 <1><8e>: Abbrev Number: 0

上述的文字可以转换成如下图所示的图形。可以理解以下几点:

  • DW_TAG_structure_type的结构体成员组织方式(通过DW_TAG_member字段);
  • 如何确定DW_TAG_variable的具体类型(通过DW_AT_type字段);


引用类型

const int a;
int main()
{
    return 0;
}

转换之后,得到如下内容:

 <1><4a>: Abbrev Number: 3 (DW_TAG_base_type)
    <4b>   DW_AT_byte_size   : 4
    <4c>   DW_AT_encoding    : 5        (signed)
    <4d>   DW_AT_name        : int
 <1><51>: Abbrev Number: 4 (DW_TAG_variable)
    <52>   DW_AT_name        : a
    <54>   DW_AT_decl_file   : 1
    <55>   DW_AT_decl_line   : 1
    <56>   DW_AT_type        : <0x64>
    <5a>   DW_AT_external    : 1
    <5a>   DW_AT_location    : 9 byte block: 3 20 10 60 0 0 0 0 0       (DW_OP_addr: 601020)
 <1><64>: Abbrev Number: 5 (DW_TAG_const_type)
    <65>   DW_AT_type        : <0x4a>

上述的文字可以转换成如下图所示的图形。可以理解DW_TAG_const_type存在的形式,
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

芯光未来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值