nm输出和gdb函数地址不同

Posted

技术标签:

【中文标题】nm输出和gdb函数地址不同【英文标题】:Function address is different in nm output and gdb 【发布时间】:2015-07-08 13:16:45 【问题描述】:

我们只关注Rect_IsEmpty() 函数。

nm 命令给了我这个输出:

(...)    
00021af0 T Rect_IsEmpty
(...)

另一方面,当我启动 gdb 并查看此函数的地址时,我得到:

(gdb) info address Rect_IsEmpty
Symbol "Rect_IsEmpty" is at 0x8057c84 in a file compiled without debugging.

谁能解释一下为什么这些地址不一样? gdb 从哪里得到这个地址?

【问题讨论】:

库中的偏移量与内存中的偏移量无关。 "The nm command give me ..." -- 请告诉你正在运行什么nm(可能不在你在GDB下运行的可执行文件上),所以你的问题可以得到解答正确(当前的两个答案都是完全虚假的)。 @EmployedRussian 你不能回答 3 种可能的情况吗:目标文件、共享目标文件、可执行文件?是什么阻止了你?大而正确的答案是好的。 @EmployedRussian 我在链接到我的可执行文件的 *.so 库上运行它。 【参考方案1】:

nm 为您提供错位名称符号表的地址偏移量,而gdb 为您提供实际虚拟进程的内存地址,每次运行该进程时都会更改。

nm 只是一个工具,它显示您从代码段的开头偏移。在你的情况下:

00021af0 T Rect_IsEmpty

简单地说,在整个代码段中,在所有其他函数中,Rect_IsEmpty 与代码段的偏移量为00021af0,因此如果代码段的基址重定位到00000000,则偏移量将与地址相同,00021af0

在 Linux 上运行可执行文件之前,ASLR 机制用于随机化地址,但不是所有地址,只是段的开始。因此,在运行可执行文件之前,您不能总是知道或依赖动态符号的地址,您只能从段起始地址获得偏移量。

当使用调试器查找函数的地址时,您会在 ASLR 完成工作后看到进程代码段内的符号地址。

Here is 来自 IBM 的一篇关于共享库的好文章,another one 是关于过程链接表和全局偏移表的文章。

【讨论】:

这个答案完全在所有点上都是错误的(在 Linux 上)。关于“在卸载前保持不变”的观点在 Windows 上是部分正确的,但这个问题是关于 Linux 的。 @EmployedRussian 请详细说明一下? 每个程序执行都会加载一次共享库,并且通常会有随机地址。 @Ant 我一个月前尝试过:创建了一个库并打开了几个链接到它并同时使用它的应用程序:它们的地址都是一样的,这意味着它们的代码段已共享。 @Ant 另外,请参阅this - “代码段已共享”。只有在静态链接的情况下(但问题不在于静态链接),当每次程序执行时解析目标文件的静态实体(函数、方法)时,您才是正确的,因为在执行之前只有它的偏移量是已知的。因此,操作系统每次执行都会解析对实际内存的所有内部引用,因为它会加载应用程序的代码段(在静态绑定的情况下)。【参考方案2】:

可执行文件将从不同的内存位置开始,从而使其中的任何分配都不同。因此,任何函数都将具有与其先前执行不同的内存地址。

关于您的问题,GDB 从调试信息中获取地址 - 它将显示绝对内存地址。

【讨论】:

以上是关于nm输出和gdb函数地址不同的主要内容,如果未能解决你的问题,请参考以下文章

段错误

使用 GNU nm 实用程序时跳过地址输出

GDB 和 OpenOCD:输出所有执行的函数

如何区分静态函数与 C 中的 nm 或 readelf 输出

如何比较 nm 输出

由于链接问题,程序无法运行