程序员的自我修养:第二部分静态链接

本文详细介绍了编译过程的四个步骤:预编译、编译、汇编和链接,以及ELF文件的结构和内容。通过实例展示了如何使用g++进行预编译、编译、汇编和链接操作,并探讨了段表、重定位表、符号表和.bss段等关键概念。此外,还讨论了静态链接中符号解析与重定位的过程。
摘要由CSDN通过智能技术生成

一、编译过程简介

g++ -o hello hello.cpp 

上述过程包括了四个步骤:预编译、编译、汇编、链接

1、预编译

g++ -E -o hello.i hello.cpp

主要处理#开始的预编译命令
1)#define: 将所有的#define删除,并在源文件中展开
2)#if 、#ifdef、#elif、#endif条件预编译指令:决定该代码是否需要编译
3)#include预编译指令,递归地(被包含的文件也有可能包含其他文件)将被包含的文件插入到该预编译指令的位置
4)删除所有注释
5)添加行号,用于显示错误需要的行号

2.编译

g++ -S -o hello.s hello.i 得到汇编文件

3.汇编

g++ -c -o hello.o hello.s 得到目标文件

4.链接
主要处理各个目标文件相互引用的部分,在独立编译的时候无法确定引用的标识符的地址,因此在链接的时候将引用的标识符的地址修正,包括:地址和空间分配、符号决议和重定位。

二、ELF文件介绍
在这里插入图片描述

#include<iostream>
using namespace std;

int main(){
    int a=0;
    cout<<a<<endl;
}
readelf -h main.o //查看elf文件头

在这里插入图片描述

文件头描述了整个文件属性,包括魔数、数据存储方式(大端、小端)、程序入口地址、段表位置段表字符串表在段表里的下标、硬件平台等
魔数前几个十六进制用来确定ELF文件的类型(COEF、ELF等,不同平台会根据魔数是否正确来确认是否能够加载),后面几个十六进制确定了大小端、32位还是64位程序。

g++ -c -o main.o main.cpp 
readelf -S main.o //显示.o文件的头部段表

在这里插入图片描述
1)段表为数组,每个元素为一个结构体,包括10个成员变量,Name,Type等
2)所有包含.rela开头的为重定位表,.rela.text为.text段的重定位表,main中调用了cout(对绝对地址的引用)因此需要重定位,而data则不需要,所以没有.rela.data段
3).bss段存放未初始化的全局变量和局部静态变量,在elf文件中实际不存在,只会在段表中声明.bss段的大小、偏移等,能够表示所有未初始化的全局变量和局部变量的size和,会在程序运行的时候将对应的空间清空。
4) common块,由于弱符号可以重复定义,因此编译的时候一个文件中的弱符号用shm_common字段来表示,在链接的时候选择size更大作为该符号值
5).strtab为字符表,将普通字符串存在这里,利用偏移来获取对应的字符串
在这里插入图片描述
.shstrtab段表字符串表,保存段表用到的字符串
.symtab为符号表,为一个结构数组,每个结构表示一个符号
在这里插入图片描述
1).text代码段

objdump -s main.o //以十六进制显示内容
objdump -d main.o //反汇编

2).data和.rodata数据段和只读数据段
.data保存已经初始化的全局变量和静态变量,原函数没有这种变量,因此.data区域大小为0
.rodata保存只读数据(const修饰)和字符串

3).bss存放未初始化的全局变量和静态变量

三、静态链接

三、符号
所有的变量、函数均可以看成是符号,符号值为其地址。符号可以分成以下几类:
1)本目标文件的全局符号,可以被其他文件引用
2)本文件引用的其他文件定义的全局符号,外部符号,如cout
3)段名,值为该段的起始地址
4)局部符号在编译单元内可见
5)行号

readelf -s main.o //查看符号表

在这里插入图片描述
链接主要做了两件事情,一是空间与地址分配,二是符号解析与重定位。由于文件是分别编译,因此.o文件的段地址用0X0000来代替,链接后会将相似段落合并,会给每个段分配地址,将所有.o文件的符号表联合成全局符号表。一个.o文件引用了外部变量,需要重定位,每个段都有重定位表.rel开头,重定位表为一个结构数组,每个结构表示一个重定位入口,表示该入口在所属段的偏移,会去查找全局符号表,找到对应的符号值进行重定位。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值