【问题标题】:GCC behavior for unresolved weak functions未解决的弱函数的 GCC 行为
【发布时间】:2015-09-21 02:15:58
【问题描述】:

考虑下面的简单程序:

__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

【问题讨论】:

    标签: c gcc arm ld weak


    【解决方案1】:

    man nm

    我正在阅读一些文档,碰巧遇到了一个相关的引用:

    man nm
    

    说:

    “V”
    "v" 符号是弱对象。当弱定义符号与正常定义符号链接时,使用正常定义符号没有错误。当一个弱的未定义符号被链接并且 符号未定义,弱符号的值变为零,没有错误。在某些系统上,大写表示已指定默认值。

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

    nm 是 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。

    【讨论】:

      【解决方案2】:

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

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

      所以我建议你测试一下:

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

      【讨论】:

      • 我正在寻找文档中的官方行为,而不是任何解决方法
      • 我知道,但是这个特性不是 C 标准的一部分,而且它看起来像许多 GCC 的特性没有足够的文档记录。 gcc.gnu.org/onlinedocs/gcc/… `weak 属性导致声明作为弱符号而不是全局符号发出。这主要用于定义可以在用户代码中覆盖的库函数,尽管它也可以与非函数声明一起使用。 ELF 目标支持弱符号,使用 GNU 汇编器和链接器时也支持 a.out 目标。 `
      猜你喜欢
      • 2020-06-23
      • 1970-01-01
      • 2020-05-14
      • 2016-11-18
      • 2012-11-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多