强弱引用以及common段

强符号:编译器默认函数和初始化的全局变量为强符号
弱符号:未初始化的全局变量为弱符号。也可以通过atrribute((weak))来指定。
编译器关于强弱符号的规则有:
(1)强符号不允许多次定义,但强弱可以共存;
(2)强弱共存时,强覆盖弱;
(3)都是弱符号时,选择占用空间最大的,如选择double类型的而不选择int类型的。
假如有文件a.c

long int  i = 5;

b.c如下:

#include<stdio.h>

int i;
long int global_longint=4;
 int main(int argc, char** argv)
 {
        static int x;
        static char arr[255];
       printf("i = %d\n", i);
       return 0;      
 }

gcc b.c -c -o b.o
readelf -s b.o

Symbol table '.symtab' contains 15 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   255 OBJECT  LOCAL  DEFAULT    4 arr.2290
     7: 0000000000000100     4 OBJECT  LOCAL  DEFAULT    4 x.2289
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
     9: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
    10: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
    11: 0000000000000001     1 OBJECT  GLOBAL DEFAULT  COM i
    12: 0000000000000000     8 OBJECT  GLOBAL DEFAULT    3 global_longint
    13: 0000000000000000    49 FUNC    GLOBAL DEFAULT    1 main
    14: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf

Ndx表示相应的符号位于elf文件下的哪一段,比如上面global_longint就是位于elf的第三个段,static 局部变量位于第四个段。

readelf -S b.o

共有 13 个节头,从偏移量 0x360 开始:

节头:
  [号] 名称              类型             地址              偏移量
       大小              全体大小          旗标   链接   信息   对齐
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00000040
       0000000000000031  0000000000000000  AX       0     0     1
  [ 2] .rela.text        RELA             0000000000000000  00000298
       0000000000000048  0000000000000018   I      11     1     8
  [ 3] .data             PROGBITS         0000000000000000  00000078
       0000000000000008  0000000000000000  WA       0     0     8
  [ 4] .bss              NOBITS           0000000000000000  00000080
       0000000000000104  0000000000000000  WA       0     0     32
  [ 5] .rodata           PROGBITS         0000000000000000  00000080
       0000000000000008  0000000000000000   A       0     0     1
  [ 6] .comment          PROGBITS         0000000000000000  00000088
       0000000000000035  0000000000000001  MS       0     0     1
  [ 7] .note.GNU-stack   PROGBITS         0000000000000000  000000bd
       0000000000000000  0000000000000000           0     0     1
  [ 8] .eh_frame         PROGBITS         0000000000000000  000000c0
       0000000000000038  0000000000000000   A       0     0     8
  [ 9] .rela.eh_frame    RELA             0000000000000000  000002e0
       0000000000000018  0000000000000018   I      11     8     8
  [10] .shstrtab         STRTAB           0000000000000000  000002f8
       0000000000000061  0000000000000000           0     0     1
  [11] .symtab           SYMTAB           0000000000000000  000000f8
       0000000000000168  0000000000000018          12    11     8
  [12] .strtab           STRTAB           0000000000000000  00000260
       0000000000000032  0000000000000000           0     0     1

我们可以看到第三个段为数据段,第四个段为bss段,也印证了初始化的全局变量位于数据段,未初始化的静态位于bss段
这里我们可以发现,bss起始于00000080,结束于00000080,bss的大小为0,也就是说,bss段只有存在于section header table (段头表,这个表记录所有段的基本信息,例如段大小,在elf的具体位置)中的那一项,而没有相应的实体段。
这是因为,未初始化变量嘛,不用记录具体数值,我们只要记录所有未初始化变量的总大小就行了,而记录总大小,在section header table中的段大小这个字段就足够了。所以bss段实体就没有存在的必要了。
然后,我们注意看弱引用i是未初始化全局变量,是在common段中,而不是在我们所认为的bss段中。
这是因为,未初始化变量是弱引用,如果多个文件存在多个相同的弱引用,其大小取其最大那个类型,现在我们只是编译一个文件,根本不知道是否还存在其他相同的弱引用,所以,编译阶段无法确定其大小,但是,在链接时,我们已经可以看到所有文件了,也就可以看到所有弱引用了,这个时候,我们就可以确定其大小。所以,为初始化全局变量在编译阶段其实是存在于common段的,然后链接的时候,比较所有文件的common段,确定为初始化全局变量弱引用大小,将其放到bss段中。
所以我们现在把两个链接

gcc b.c a.c -o main -g
readelf -s main

  62: 0000000000601038     8 OBJECT  GLOBAL DEFAULT   25 global_longint
    63: 0000000000601028     0 NOTYPE  GLOBAL DEFAULT   25 __data_start
    64: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    65: 0000000000601030     0 OBJECT  GLOBAL HIDDEN    25 __dso_handle
    66: 00000000004005e0     4 OBJECT  GLOBAL DEFAULT   16 _IO_stdin_used
    67: 0000000000400560   101 FUNC    GLOBAL DEFAULT   14 __libc_csu_init
    68: 0000000000601188     0 NOTYPE  GLOBAL DEFAULT   26 _end
    69: 0000000000400430    42 FUNC    GLOBAL DEFAULT   14 _start
    70: 0000000000601040     8 OBJECT  GLOBAL DEFAULT   25 i
    71: 0000000000601048     0 NOTYPE  GLOBAL DEFAULT   26 __bss_start
    72: 0000000000400526    49 FUNC    GLOBAL DEFAULT   14 main

因为上面a.c中i强引用是有初始值的,所以最后i变为初始化全局变量,而25是.data段。我们将a.c中的i改为未初始化的,再链接,可以看到,i最后被放在bss段。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值