man nm
我正在阅读一些文档,碰巧遇到了一个相关的引用:
man nm
说:
"V"
"v" 符号是弱对象。当弱定义符号与正常定义符号链接时,使用正常定义符号不会出错。当链接一个弱未定义符号且该符号未定义时,该弱符号的值变为零且没有错误。在某些系统上,大写表示已指定默认值。
“W”
“w” 该符号是一个弱符号,尚未被专门标记为弱对象符号。当弱定义符号与正常定义符号链接时,使用正常定义符号不会出错。当链接弱未定义符号且未定义符号时,符号的值以系统特定的方式确定而不会出错。在某些系统上,大写表示已指定默认值。
nm
是 GCC 在后台使用的 Binutils 的一部分,因此这应该足够规范。
然后,在您的源文件中举例:
主程序
__attribute__((weak)) void weakf(void);
int main(int argc, char *argv[])
{
weakf();
}
我们的确是:
gcc -O0 -ggdb3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
nm main.out
其中包含:
w weakf
所以它是一个系统特定的值。但是,我找不到每个系统行为的定义位置。我认为没有比在这里阅读 Binutils 源代码更好的了。
v
将固定为 0,但用于未定义的变量(对象):如何使弱链接与 GCC 一起工作?
然后:
gdb -batch -ex 'disassemble/rs main' main.out
给出:
Dump of assembler code for function main:
main.c:
4 {
0x0000000000001135 <+0>: 55 push %rbp
0x0000000000001136 <+1>: 48 89 e5 mov %rsp,%rbp
0x0000000000001139 <+4>: 48 83 ec 10 sub $0x10,%rsp
0x000000000000113d <+8>: 89 7d fc mov %edi,-0x4(%rbp)
0x0000000000001140 <+11>: 48 89 75 f0 mov %rsi,-0x10(%rbp)
5 weakf();
0x0000000000001144 <+15>: e8 e7 fe ff ff callq 0x1030 <weakf@plt>
0x0000000000001149 <+20>: b8 00 00 00 00 mov $0x0,%eax
6 }
0x000000000000114e <+25>: c9 leaveq
0x000000000000114f <+26>: c3 retq
End of assembler dump.
这意味着它在 PLT 得到解决。
然后由于我不完全理解 PLT,我通过实验验证它可以解决 0 和段错误:
gdb -nh -ex run -ex bt main.out
我假设在 ARM 上也会发生同样的情况,它也必须将其设置为 0。