我见过 使用 GDB 时缺少 ELF 符号“var”?,但这是一个不同的问题。
我在 openocd 上使用带有 RP2040 的 gdb。不幸的是,我无法提供重现该问题的完整代码,但在
.h
文件中我有:
extern const float kLDC;
...在
.c
文件中我有:
const float kLDC = (100-0)/(255-0);
当我通过 openocd 使用程序的调试版本启动 gdb 会话时,我可以
p
rint 几乎任何全局变量,但是对于 kLDC
,我得到:
(gdb) p kLDC
Missing ELF symbol "kLDC".
发生什么事了?是否期望声明为 extern const float 的符号不能在
gdb
中打印?我用:
$ gdb-multiarch.exe --version
GNU gdb (GDB) 14.2
感谢评论者指出
(100-0)/(255-0)
表达式为整数有错误;我自己最终意识到,当我第一次删除 const
时,我可以检查 gdb 中的值。有了这个声明:
float kLDC = (100.0-0)/(255-0);
...我确认该值约为0.3921;记下使用该变量获得的计算结果,然后将其设置回
const
。我可以确认计算结果是相同的,但是我再次得到Missing ELF symbol "kLDC".
。构建过程生成了一个 .map
文件,它输出:
$ grep kLDC myprogram.elf.map
.rodata.kLDC 0x00000000 0x4 CMakeFiles/myprogram.dir/my_function.c.obj
我也尝试过:
$ arm-none-eabi-objdump -g myprogram.elf | grep kLDC
<23e00> DW_AT_name : (indirect string, offset: 0x7ae4): kLDC
0x00007ae0 70657100 6b4c4443 00475049 4f5f4f56 peq.kLDC.GPIO_OV
(我不太明白上面的内容,但令我惊讶的是看到
kLDC
接近任何名为 GPIO
的东西 - 它根本不直接与代码中的 GPIO 一起使用)
我仍然无法理解这一点 - .elf 中有 kLDC 的痕迹,我可以想象编译器可能“知道”一些其他初始化的只读内存,其内容与 kLDC = 0.39 相同...,因此,通过不费心专门为 kLDC var 分配新内存,而是重新使用现有内存来“优化” - 但现在我费心声明“extern”(并使用
-g
/Debug 构建),不应该编译器至少尝试让这个常量在 gdb
中可用,这样我就可以打印它的值?至少,我想了解构建过程决定可以“错过”ELF 中的“符号”的条件 - 或者相反,如果我可以使用任何其他规范来强制该值可读在gdb
,即使是const
...
映射文件显示包含所需对象的部分的地址为 0x00000000,这意味着它已被链接器从最终的二进制文件中删除。
它这样做是因为您的构建过程包含链接器标志
--gc-sections
。 这告诉它删除任何未引用的部分。
您可以删除此标志,但这将使您的二进制文件变得巨大,其中包含各种未使用的垃圾。 这在嵌入式开发中不是一个好主意。
声明变量
extern
会阻止编译器对其进行优化,但这并没有帮助,因为这里的垃圾收集是由链接器完成的。 您可以通过将标志 --gc-keep-exported
添加到链接器(如果您通过 gcc 调用 ld,则为 -Wl,--gc-keep-exported
)来获得您期望的行为。
或者,您只需要让程序以无法优化的方式使用变量,例如:
printf("%08X", (unsigned int)&kLDC);
KEEP()
添加到链接器脚本来告诉链接器保留此特定部分,但这可能很难维护。