未解决的弱函数的GCC行为

时间:2015-07-03 09:33:34

标签: c gcc arm ld weak

考虑下面的简单程序:

__attribute__((weak)) void weakf(void);

int main(int argc, char *argv[])
{
        weakf();
}

使用gcc编译并在Linux PC上运行时,会出现段错误。在ARM CM0(arm-none-eabi-gcc)上运行时,链接器通过跳转到以下指令和nop来替换未定义的符号。

这种行为记录在哪里?是否有可能通过命令行选项更改它?我查看过GCCLD个文档,但没有相关信息。

但是,如果我检查ARM编译器文档,this is clearly explained

2 个答案:

答案 0 :(得分:1)

man nm

我正在阅读一些文档,碰巧遇到了相关的报价:

man nm

说:

  

“ V”
  “ v”该符号是一个弱对象。当弱定义符号与普通定义符号链接时,使用普通定义符号不会出错。当弱的未定义符号链接时,              如果未定义符号,则弱符号的值将变为零且没有错误。在某些系统上,大写表示已指定默认值。

     

“ W”
  “ w”符号是一个弱符号,尚未专门标记为弱对象符号。当弱定义符号与普通定义符号链接时,普通定义符号为              没有错误地使用。当链接了一个未定义的弱符号而未定义该符号时,该符号的值将以系统特定的方式确定,而不会出现错误。在某些系统上              大写表示已指定默认值。

nm是Binutils的一部分,GCC在后台使用了Binutils,因此应该足够规范。

然后,在源文件上添加示例:

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的源代码做得更好。

v将固定为0,但这用于未定义的变量(它们是对象):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.

这意味着它将获得resolved at the PLT

然后,由于我不完全了解PLT,因此我实验性地验证了它可以解决地址0和段错误:

gdb -nh -ex run -ex bt main.out

我想在ARM上也会发生同样的情况,它也必须将其设置为0。

答案 1 :(得分:0)

在带有gcc的ARM上,这段代码对我不起作用(使用gcc Debian 4.6.3-14 + rpi1在armv7上测试)。看起来arm编译器工具链有不同的行为。

我没有找到有关此行为的有用文档。如果它在链接时取消定义,则弱似乎等于NULL。

所以我很想你测试它:

if (weakf == NULL) printf ("weakf not found\n");
else weakf();