有没有办法修改 .elf 文件中关于外部变量的 DWARF 格式输出

问题描述

我们有一个第三方软件,它从生成的 .elf 中挑选出关于我们使用 DWARF 调试信息来查找全局变量的信息。 以前我们一直使用 IAR 编译器,它生成 .elf 文件包括调试 DWARF 信息。 使用 IAR 生成的 .elf 文件,可以轻松找到内存中的位置地址和变量名称。但是,当使用 gcc "GNU Arm Embedded Toolchain 9-2020-q3-update) 9.3.1 20200408 (release)" 生成 .elf 文件时,软件无法定位在头文件和定义中声明为 extern 的全局变量的变量我.c文件

所以对于我的问题,有没有办法让关于外部全局变量的 DWARF 信息看起来更像 IAR 生成的信息,请看下面的示例,显示使用 readelf 的外部全局变量

编程语言:C CFLAGS: (-ggdb -ffunction-sections -O0 -gpubnames)(我尝试了很多关于 DWARF 设置的不同编译器选项,但没有成功)

IAR 产生这个:

<1><2f1ab>: Abbrev Number: 3 (DW_TAG_variable)
    <2f1ac>   DW_AT_name        : StopBtn_StopBtnActvd_flg
    <2f1c5>   DW_AT_type        : <0x29d80>
    <2f1c9>   DW_AT_decl_file   : 4
    <2f1ca>   DW_AT_decl_line   : 46
    <2f1cb>   DW_AT_decl_column : 11
    <2f1cc>   UnkNown AT value: 276a: 34
    <2f1cd>   DW_AT_external    : 1
    <2f1ce>   DW_AT_location    : 5 byte block: 3 aa 45 1 20    (DW_OP_addr: 200145aa)
    <2f1d4>   UnkNown AT value: 2768: 1
 <1><2f1d5>: Abbrev Number: 0

GCC 产生这个(在 _debug:info 中使用 gcc 时,信息分为 2 个部分):

 <1><46158>: Abbrev Number: 45 (DW_TAG_variable)
    <46159>   DW_AT_name        : (indirect string,offset: 0x21a4): StopBtn_StopBtnActvd_flg
    <4615d>   DW_AT_decl_file   : 11
    <4615e>   DW_AT_decl_line   : 102
    <4615f>   DW_AT_decl_column : 17
    <46160>   DW_AT_type        : <0x45dc1>
    <46164>   DW_AT_external    : 1
    <46164>   DW_AT_declaration : 1
 <1><46164>: Abbrev Number: 45 (DW_TAG_variable)
--
 <1><46655>: Abbrev Number: 46 (DW_TAG_variable)
    <46656>   DW_AT_specification: <0x46158>
    <4665a>   DW_AT_decl_file   : 12
    <4665b>   DW_AT_decl_line   : 46
    <4665c>   DW_AT_decl_column : 11
    <4665d>   DW_AT_location    : 5 byte block: 3 7a 4 0 20     (DW_OP_addr: 2000047a)
 <1><46663>: Abbrev Number: 46 (DW_TAG_variable)
    <46664>   DW_AT_specification: <0x46164>
    <46668>   DW_AT_decl_file   : 12
    <46669>   DW_AT_decl_line   : 47

解决方法

我们有一个第三方软件,它从生成的 .elf 中挑选出有关我们使用 DWARF 调试信息来查找全局变量的信息。

问题似乎是您的第 3 方软件不符合 DWARF 标准。特别是,它不会跟随 DW_AT_specification 指针来查找位于 2000047a 处的变量的名称。

自从 1993 年 DW_AT_specification 成为 DWARF2 的一部分以来,除了向 3rd 方供应商抱怨之外,您几乎无能为力:他们已经将近 30 年了!做对了。

您可以做的另一件事是将您的全局变量定义移动到一个不包含任何标题的文件中。

比较:

// foo.c
int var1 = 42;

与:

// bar.h
extern int var2;
// bar.c
#include "bar.h"
int var2 = 43;

使用 9.3.0-17ubuntu1~20.04 时,readelf -wi foo.o 产生:

 <1><1d>: Abbrev Number: 2 (DW_TAG_variable)
    <1e>   DW_AT_name        : (indirect string,offset: 0x92): var1
    <22>   DW_AT_decl_file   : 1
    <23>   DW_AT_decl_line   : 1
    <24>   DW_AT_decl_column : 5
    <25>   DW_AT_type        : <0x33>
    <29>   DW_AT_external    : 1
    <29>   DW_AT_location    : 9 byte block: 3 0 0 0 0 0 0 0 0  (DW_OP_addr: 0)

readelf -wi bar.o 产生:

  <1><1d>: Abbrev Number: 2 (DW_TAG_variable)
    <1e>   DW_AT_name        : (indirect string,offset: 0x93): var2
    <22>   DW_AT_decl_file   : 1
    <23>   DW_AT_decl_line   : 1
    <24>   DW_AT_decl_column : 12
    <25>   DW_AT_type        : <0x29>
    <29>   DW_AT_external    : 1
    <29>   DW_AT_declaration : 1
...
 <1><30>: Abbrev Number: 4 (DW_TAG_variable)
    <31>   DW_AT_specification: <0x1d>
    <35>   DW_AT_decl_file   : 2
    <36>   DW_AT_decl_line   : 2
    <37>   DW_AT_decl_column : 5
    <38>   DW_AT_location    : 9 byte block: 3 0 0 0 0 0 0 0 0  (DW_OP_addr: 0)

更新:

但是变量的 c 文件中的 decalarion 和定义使得很难访问与该变量有关系的其他 c 文件。所以我想这对我来说是不可能的解决方案

您可以将声明保留在头文件中,并在任何地方使用它除了提供定义的一个文件。

这是我建议的解决方法。假设您当前的代码如下所示:

// foo.h
extern int var;
int foo1();
int foo2();

// foo.c
#include "foo.h"
int var = 42;
int foo1() { ...}
int foo2() { ...}

// bar.c
#include "foo.h"
int bar() { return var + foo1() + foo2(); }

建议的解决方法:将 var定义移动到一个单独的文件中,并且不要将 #include "foo.h" 移动到该文件中:

// foo.c
#include "foo.h"
/* no longer define var here. */
int foo1() {...}
int foo2() {...}

// foo_var.c
int var = 42;

其他文件将保持不变。