记一次对链接、COMMON块、多重符号定义的理解

问题引入

首先是两个测试程序

// foo.c
long long int a;
// bar.c
#include <stdio.h>

int a;
int main(){
    a = 1;
    long long int len = sizeof(a);
    printf("%lld\n", len);
    return 0;
}

将两个程序链接到一起
问题:len等于几?

初步分析

环境:两个程序均为C程序,当前GCC版本为7.5.0,Ubuntu18下

  1. foo.cbar.ca均为弱符号( foo.cbar.ca均为未初始化的全局变量)
  2. 根据多重定义符号的处理规则
    • 可能任选一个

      (袁春风《计算机系统基础》第二版176页,CSAPP第三版471页)

      尝试不同链接顺序:预计输出一个4,一个8
      - gcc foo.c bar.c -o foo_bar
      - gcc bar.c foo.c -o bar_foo
      运行,输出都是4(???)

    • 也可能选占用空间最大的一个:应该是8(???)

      (《程序员的自我修养》92页)

逐步分析

  • 首先分开编译:

    • gcc -c bar.c
    • gcc -c foo.c
  • 查看目标文件的符号表:

    • readelf -s bar.o:a大小为4
      在这里插入图片描述

    • readelf -s foo.o:a大小为8
      在这里插入图片描述

    分析:sizeof(a)在编译期间就被处理完了,bar.clen可以被编译优化为4

  • 然后进行链接,与链接顺序无关,所以最终应该输出4

    • gcc bar.o foo.o bar_foo
    • gcc foo.o bar.o foo_bar
  • 反汇编看一下:objdump -d bar_foo
    在这里插入图片描述

  • 再多看一步,看一下符号表:readelf -s bar_foo
    在这里插入图片描述
    注意到a占用了8个字节,但是最终输出sizeof(a)=4,因为一个在链接阶段进行处理,一个在编译阶段进行处理,这也说明了如果有多个弱符号,最终选个最大的(?)

再分析

将两个程序变一下,重命名为foo.cppbar.cpp,编译运行一下
直接链接报错:
在这里插入图片描述

再逐步看一下

  • 分开编译,看一下符号表
    在这里插入图片描述
    前面a在COMMOM块中,现在a在放在了bss节中(foo.cpp相同)
    在这里插入图片描述

  • 强行将变量放到COMMON块中,修改bar.cpp
    在这里插入图片描述

  • 再编译,看一下符号表
    在这里插入图片描述
    没问题,之后正常输出4

小结

  • 如果是C++程序,对于未初始化的全局变量(称为tentative definition),放在bss节中(相当于编译器默认初始化为0)
    • 如果非要放在common块中,需要在变量前设置属性__attribute__((common)),设置gcc编译选项没用
  • 如果是C程序,对于未初始化的全局变量,编译时gcc选项:
    • -fcommon:未初始化的全局变量放在common块中(GCC 9之前默认)
    • -fno-common:未初始化的全局变量放在bss节中(GCC 10之后默认)

参考

为什么访问外部变量不需要extern也可以?
《程序员的自我修养——链接、装载与库》3.5.5节,4.3节
《计算机系统基础》第二版 4.3.2节

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值