未解决的弱函数的GCC行为 [英] GCC behavior for unresolved weak functions
问题描述
考虑以下简单程序:
__attribute__((weak)) void weakf(void);
int main(int argc, char *argv[])
{
weakf();
}
当使用gcc进行编译并在Linux PC上运行时,它会出现段错误.在ARM CM0(arm-none-eabi-gcc)上运行它时,链接器通过跳转到以下指令和nop来替换未定义的符号.
When compiling this with gcc and running it on a Linux PC, it segfaults. When running it on ARM CM0 (arm-none-eabi-gcc), the linker replace the undefined symbol by a jump to the following instruction and a nop.
此行为记录在哪里?是否可以通过命令行选项更改它?我已经通过 GCC 和
Where is this behavior documented? Is there possible ways to change it through command line options? I have been through GCC and LD documentations, there is no information about that.
If I check the ARM compiler doc however, this is clearly explained.
推荐答案
man nm
man nm
我正在阅读一些文档,碰巧遇到了相关的报价:
I was reading some docs and happened to come across a related quote for this:
man nm
说:
"V"
"v"该符号是一个弱对象.当弱定义符号与普通定义符号链接时,使用普通定义符号不会出错.当链接了一个弱的未定义符号时, 如果未定义符号,则弱符号的值将变为零且没有错误.在某些系统上,大写表示已指定默认值.
"V"
"v" The symbol is a weak object. When a weak defined symbol is linked with a normal defined symbol, the normal defined symbol is used with no error. When a weak undefined symbol is linked and the symbol is not defined, the value of the weak symbol becomes zero with no error. On some systems, uppercase indicates that a default value has been specified.
"W"
"w"符号是一个弱符号,尚未专门标记为弱对象符号.当弱定义符号与普通定义符号链接时,普通定义符号为
没有错误地使用.当链接了一个未定义的弱符号而未定义该符号时,该符号的值将以系统特定的方式确定,而不会出现错误.在某些系统上
大写表示已指定默认值.
"W"
"w" The symbol is a weak symbol that has not been specifically tagged as a weak object symbol. When a weak defined symbol is linked with a normal defined symbol, the normal defined symbol is
used with no error. When a weak undefined symbol is linked and the symbol is not defined, the value of the symbol is determined in a system-specific manner without error. On some systems,
uppercase indicates that a default value has been specified.
nm
是Binutils的一部分,GCC在后台使用了Binutils,因此应该足够规范.
nm
is part of Binutils, which GCC uses under the hood, so this should be canonical enough.
然后,在您的源文件上添加示例:
Then, example on your source file:
main.c
__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的源代码做得更好.
and so it is a system-specific value. I can't find where the per-system behavior is defined however. I don't think you can do better than reading Binutils source here.
v
would be fixed to 0, but that is used for undefined variables (which are objects): How to make weak linking work with 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和段错误:
Then since I don't fully understand PLT, I experimentally verify that it resolves to address 0 and segfaults:
gdb -nh -ex run -ex bt main.out
我想在ARM上也会发生同样的情况,它也必须将其也设置为0.
I'm supposing the same happens on ARM, it must just set it to 0 as well.
这篇关于未解决的弱函数的GCC行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!