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

news/2024/12/29 7:58:01/

问题引入

首先是两个测试程序

// 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节


http://www.ppmy.cn/news/1073770.html

相关文章

Navicat激活时出现rsa public key not find错误

Navicat激活时出现rsa public key not find错误 在激活时&#xff0c;先不打开应用&#xff0c;先用管理员身份打开注册机Navicat_Keygen_Patch_v5.6_By_DFoX.exe&#xff0c;Navicat v15——>MySql——>Simplified Chinese——>Patch&#xff0c;执行完这些步骤之后…

1.3.2背包模型(二)

1.二维费用的背包问题 有 N N N件物品和一个容量是 V V V的背包&#xff0c;背包能承受的最大重量是 M M M。 每件物品只能用一次。体积是 v i v_{i} vi​&#xff0c;重量是 m i m_{i} mi​&#xff0c;价值是 w i w_{i} wi​。 求解将哪些物品装入背包&#xff0c;可使物…

人工智能学习专栏

这个专栏就专门用来记录自己的深度学习的历程吧。从做MCU开始、Soc、Linux系统转行到AI领域&#xff0c;其过程是痛苦的。至少数学这块&#xff0c;那是花了很多时间去从头去学。但是还是有很多不懂的地方。坚持&#xff01;&#xff01;&#xff01;&#xff01;

Android 使用模拟器模拟Linux操作系统

1. 简介 在Android手机上使用模拟器模拟ubuntu等操作系统&#xff0c;便于测试 2. 软件准备 Termux&#xff1a;是一款 Android 终端模拟器和 Linux 环境应用程序&#xff0c;无需 root 或设置即可直接运行。虽然酷安和谷歌菜市场都能下载&#xff0c;但这些渠道都很久没更新…

Python Flask swagger自动生成文档

首先安装依赖&#xff1a; pip install flask_siwadoc pydantic封装swagger.py文件&#xff0c;代码如下&#xff1a; from flask_siwadoc import SiwaDoc siwa SiwaDoc()然后在主应用中&#xff08;项目入口文件&#xff09;加入以下代码&#xff1a; from flask import F…

go语言 go mod生成

1. go hello world 创建文件夹gotest&#xff0c;在其中创建test1.go文件&#xff0c;并写入 package mainimport ("fmt" )func main() {fmt.Println("hello world") } 运行命令 go run test1.go 可以看到输出hello world 2. cli 命令行的使用 代码如下…

C++八股记录

C内存管理 C中&#xff0c;内存分成5个区。 栈&#xff1a;函数内局部变量&#xff1b;自动管理&#xff0c;效率高&#xff0c;但空间较小&#xff1b; 堆&#xff1a;new分配的内存块&#xff1b;手动管理&#xff0c;效率低&#xff0c;但空间大&#xff1b; 自由存储区&…

一篇掌握高级交换技术原理与配置(三):QINQ

一、概述 随着以太网技术在网络中的大量部署&#xff0c;利用VLAN对用户进行隔离和标识受到很大限制。因为IEEE802.1Q中定义的VLAN Tag域只有12个比特&#xff0c;仅能表示4096个VLAN&#xff0c;无法满足城域以太网中标识大量用户的需求&#xff0c;于是QinQ技术应运而生。 …