强符号与弱符号


简而言之,在 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)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值