强符号与弱符号

Posted 车子 chezi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了强符号与弱符号相关的知识,希望对你有一定的参考价值。


简而言之,在 C 语言中,函数和初始化的全局变量(包括显示初始化为 0)是强符号,未初始化的全局变量是弱符号。

在链接器进行链接的时候,有下面的规则

  1. 强符号不允许多次定义(即不同的目标文件中不能有同名的强符号)
  2. 强弱可以共存,共存时,强覆盖弱
  3. 都是弱符号时,选择占用空间最大的;

我们举例子说明。

强符号不允许多次定义

针对 1,代码片段是:

// a.c 
int tea = 2; // strong

// b.c
int tea = 1; // strong
printf("tea = %d\\n",tea);
gcc a.c b.c

会报错:`tea’被多次定义

强弱可以共存

针对 2,代码片段是:

// a.c 
int tea = 2; // strong

// b.c
int tea; // weak
printf("tea = %d\\n",tea);
gcc a.c b.c

编译通过,运行结果是:

tea = 2

可见,链接器采用的是 a.c 文件中的强定义。

都是弱符号

针对 3,都是弱符号时,选择占用空间最大的(似乎表现出了 union 的行为)。完整代码是:

// a.c

#include <stdio.h>

void foo(void);  // 在 b.c 中定义
char tea;
int main(void)
{
    foo();  
    printf("tea = %d\\n", tea);
    printf("tea address = %p\\n",&tea);   
    return 0;
}

// b.c

#include <stdio.h>
int tea;
void foo(void)
{     
    tea = 0x12ff;   
    printf("tea = %d\\n", tea);
    printf("tea address = %p\\n",&tea);   
}

编译通过,运行结果是:

tea = 4863
tea address = 0x60103c
tea = -1
tea address = 0x60103c

可见,变量 tea 的地址都一样,但是因为在不同文件的变量类型不一样,呈现出了 union 的特性。第三行的 tea 只占用一个字节,0xff 就是 -1,第一行的 tea 占用四个字节,4863 就是 0x12ff。

有细心的读者会问,如果都是弱符号,且占用空间一样大,那么链接器如何选择呢?这个我也不知道,尝试做实验,也没有探究出什么结论。

对于函数,如果都是弱符号,采用哪一个符号和链接顺序有关。实验代码如下:

// a.c

__attribute__((weak)) void foo(void)
{
    printf("I am in a.c\\n");
}
int main(int argc, const char *argv[])
{
       foo();
       return 0;
}
// b.c

__attribute__((weak)) void foo(void)
{
    printf("I am in b.c\\n");
}

如果是这样编译:

$ gcc a.c b.c

运行结果是:

I am in a.c

如果是这样编译:

$ gcc b.c a.c

运行结果是:

I am in b.c

需要特别说明的是:**链接器允许开发者使用 __attribute__((weak)) 来将一个强符号转变为弱符号。**在上面的例子里面,代码第 3 行强制函数 foo 为弱符号。

为了加深印象,我们查看一下符号表。

首先去掉 __attribute__((weak))

$ gcc -c a.c b.c

$ readelf -s a.o b.o

文件:a.o

Symbol table '.symtab' contains 12 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS a.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     9: 0000000000000000    17 FUNC    GLOBAL DEFAULT    1 foo
    10: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND puts
    11: 0000000000000011    27 FUNC    GLOBAL DEFAULT    1 main

文件:b.o

Symbol table '.symtab' contains 11 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS b.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     9: 0000000000000000    17 FUNC    GLOBAL DEFAULT    1 foo
    10: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND puts

可以看到第 14、31 行,Bind 这列是 GLOBAL

我们再加上 __attribute__((weak))

$ gcc -c a.c b.c

$ readelf -s a.o b.o

文件:a.o

Symbol table '.symtab' contains 12 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS a.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     9: 0000000000000000    17 FUNC    WEAK   DEFAULT    1 foo
    10: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND puts
    11: 0000000000000011    27 FUNC    GLOBAL DEFAULT    1 main

文件:b.o

Symbol table '.symtab' contains 11 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS b.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     9: 0000000000000000    17 FUNC    WEAK   DEFAULT    1 foo
    10: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND puts

可以看到第 14、31 行,Bind 这列变成了 WEAK

说明:本文的实验环境是 gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.12)

以上是关于强符号与弱符号的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Javadoc 中使用 @ 和 符号格式化代码片段?

GDT/IDT

Android性能优化之巧用软引用与弱引用优化内存使用

强符号和若符号,强引用和弱引用

弱符号与强符号,弱引用与强引用

强符号,弱符号