Linux
操作系统。
Linux version 4.15.0-30deepin-generic
。
示例代码有两个:main.cpp
和 sum.cpp
:
// main.cpp
#include <iostream>
using namespace std;
extern int gdata;
int sum(int,int);
int data = 20;
int main()
{
int a = gdata;
int b = data;
int ret = sum(a,b);
return 0;
}
// sum.cpp
#include <iostream>
using namespace std;
int gdata = 10;
int sum(int a, int b)
{
return a+b;
}
预编译过程主要处理那些源代码文件中#
开头的预编译指令:
例如:#include<XXX>
、#define XXX
、#ifdef XXX
等;
预编译过程相当于如下命令:
gcc -E main.c -o main.i
主要规则如下:
注:
#pragma lib
、pragma link
等命令是在链接过程处理的。
预编译后得到的文件为:.i
文件。
编译的过程就是把预编译后得到的.i
文件进行一系列词法分析、语法分析、以及优化,随后生产相应的汇编代码文件。
上面的编译过程相当于如下命令:
gcc -S main.i -o main.s
编译后得到的文件为:.s
文件。
汇编是将汇编代码转变为机器可以执行的指令的过程:汇编代码 -> 指令
;
上面的汇编过程我们可以调用汇编器as
来完成:
as main.s -o main.o
或者:
gcc -c main.s -o mian.o
汇编完成后得到二进制可重定位目标文件:.o
文件。
我们可以通过objdump
命令来查看.o
或者.exe
文件的相关信息;
例如:objdump -t main.o
来查看main.o
里面的符号表信息:
我们可以看到在
main.o
里面引用了外部文件的gdata
变量和sum
函数,在符号表中都是UND的,也就是(undefine);
这就意味着汇编器生成符号的时候在main.cpp
文件中使用到了但是未找到gdata
和sum
的定义,所以只能暂时存放在UND
段中。
其次,我们会注意到,符号表中关于sum的部分是
_Z3sumii
,其实这就是C++生成符号的规则,具体细节我们不用去深究,但是可以看到其中包含了函数名
和形参列表
,这也是C++和C语言不一样的地方,如果我们相同的代码使用C语言来看看符号表,就会发现长这样:
同理objdump -t sum.o
来查看sum.o
里面的符号表信息:
链接:编译完成的所有.o
文件+.a / .lib
文件。
步骤一:
所有.o
文件段的合并,符号表合并后,进行符号解析;
步骤二:
符号的重定位(重定向)。
首先是所有.o
文件段的合并,
也就是main.o
和sum.o
的.text
、.data
等段合并到一起。
其次是符号解析,可以理解为:
所有对符合引用,都要找到该符号定义的地方
;
也就是链接器寻找main.o
文件中*UND*
的gdata
和sum
符号定义的地方,如果找遍了所有地方都没有找到,那么链接器就会报错:符号未定义!
,或者是在多个地方都找到了相同的符号定义,那么也会报错:符号重定义!
对于本例来说,这两个符号会在sum.o
的.text
和.data
段找到符号的定义地方。
最后是符号的重定向:
给所有的符号分配虚拟地址,之后去代码段中给所有的符号重定向
。
通过objdump -S main.o
我们可以发现:
在汇编器生成符号的时候,并未给符号分配虚拟地址,所有在汇编代码上填充的都是00 00 00 00
;
那么在符号解析完成后,给所有符号分配完虚拟地址后,还需要做一件重要的事情:去代码段.text
将之前填充的00 00 00 00
修改为该符号正确的地址。
待到链接完成后,我们再去通过相同的指令去查看objdump -S a.out
:
可以看到此时经过符号重定位
后,代码段的具体地址已经被重定位
为新的地址了。
为什么目标文件(.obj)不能运行?
首先编译阶段不会分配虚拟地址,通过objdump -S main.o
指令我们可以看到反汇编下的代码:
我们可以发现,在引用外部变量及函数的时候,汇编指令的地址都是00 00 00 00
,这个地址是不可访问区
,这也就意味着,如果不进行链接的话,仅通过目标文件,机器根本无法正常执行指令。
从这个角度来看,目标文件是无法运行的。(原因之一)
符号什么时候分配虚拟地址?
在链接过程,具体是在符号解析完成后,会给所有的符号分配虚拟地址。
目标文件(.o)的格式组成?
同时我们也可以使用readelf -S XXX.o
来查看Linux系统下ELF可重定位目标文件的格式:
可执行文件(.exe)的格式组成?
通过readelf -S a.out
:
【1】俞甲子.《程序员的自我修养—链接、装载与库》.北京:电子工业出版社.2009:04
【2】Randal E. Bryant. 《深入理解计算机系统》.北京. 机械工业出版社,2016:1
当一个正则表达式扫描目标字符串时,从左到右逐个扫描正则表达式的组成部分,在...
!--显示任意年、月的日历,可选择不同的年、月。author:wildfield-- %@pagelangu...
你可以将FCKEDITOR放置到任何文件夹,默认情况下,将其放入到FCKEDITOR文件夹是最...
Netflix 已开放其 Domain Graph Service(DGS)框架的源代码 ,该框架是为独立和联...
写在最前 记得以前在人人上看到一个分享,讲解基于js的截图方案,详细的不记得了...
复制代码 代码如下: Function CutStrX(ByVal Str,ByVal StrLen) Dim l,t,c,i,r '...
一、 安装后首先换国内源 源通常指软件源。什么是软件源? 在Ubuntu下它相当于软...
本文实例为大家分享了JavaScript实现简单计算器功能的具体代码,供大家参考,具...
一个很可爱的登录界面: 进行一下目录扫描,发现源码泄露 www.zip ,把源码给出...
需求背景: DDR3 内存颗粒由原来的 2Gb*9(其中 1 颗为 ECC 颗粒 ) 替换为三星 和...