devc++源文件未编译_编译原理——链接过程(上)

50b922e03d8b2e45ebafba7014b12184.png
这个星期已经把b站中,袁春风老师讲解的计算机系统基础(一)看完了。写这个专栏已经差不多一个月了。也就是说,花费了一个月的时间把计算机系统基础(一)视频看完了。但是,最近我又陷进了“看这些基础知识有什么用?”、“这个知识,了解一下就够了,脑海里知道有这个知识”。就是这些想法,让我对一些知识浅尝即止,没法深入的去学习。学习后面链接的过程的视频,也有点囫囵吞枣。所以我还是下定决心,创建Linux的虚拟机,踏踏实实敲好每条指令。回想起建立专栏的初衷,不就是为了记录知识,将知识系统化。其实,在写专栏的时候,我也发现写文章,其实就是一个思考的过程。因为在这个过程中,你需要把知识分享给其他人,你就必须详细去理解知识。这时候,自己就会产生一些问题,就会去思考为什么会这样?还是要不断砥砺自己。不忘初心,继续前行。

在这篇中,链接是主菜。在上主菜之前,我们还是先来尝一下开胃菜——编译的过程。在Linux中,gcc命令实际上是具体程序的包装命令,用户通过gcc命令来使用具体的 预处理程序cpp、编译程序cc1和汇编程序as等。

预处理(生成.i预处理文本文件)

命令-$gcc -E hello.c -o hello.i -$cpp helloc.c > hello.i

处理源文件中以“#”开头的预编译指令,包括:

-展开定义的宏

-处理所有条件预编译指令,如#if,#ifdef,#endif等

-插入头文件到#include处

-删除所有的注释"//"和"/**/"

-添加行号和文件标识,以便编译时编译器产生调试用的行号信息

-保留所有#pragma编译指令(编译器需要使用)

编译(生成.s文本文件)

命令-$gcc -s hello.i -o hello.s

进行词法分析、语法分析、语义分析并优化,生成汇编代码文件。

汇编(生成.o可重定向目标文件,不可读二进制代码)

命令-$gcc -c hello.s -o hello.o -$as -c hello.s -o hello.o

0a442c6babfdca69be1baf8d3bdc1c52.png

819addb68135a99e2da3511c36771f33.png

6b58bd747732bf58f400767522c1b1e5.png
ElF头
  • ELF头:文件类型、机器类型、节头表的表项大小以及表项个数。
  • .text节:编译后的汇编代码
  • .rodata节:只读数据,如printf格式串、switch跳转表等。
  • .data节:已初始化的全局变量,局部静态变量。
  • .bss节:未初始化的全局变量,局部静态变量。仅是占位符,不占据任务实际磁盘空间。
  • .symtab节:符号表,存放函数名和全局变量信息(不包括局部变量)。
  • .rel.text节:代码段的重定位信息
  • .rel.data节:数据段的重定位信息
  • .debug节:调试用的符号表
  • .strtab节:包含symtab和debug节中符号及节名。?
  • .line节:?
  • Section Header table(节头表):每个节的节名、偏移和大小

看完,还是不知道.strtab 和 .line 有什么具体作用?先留一个疑问。后续再回来看看。

链接(将多个目标文件,生成一个可执行目标文件)

命令-$gcc -static -o myproc main.o test.o -$ld -static ...

可执行目标文件,与可重定位目标文件一样,是一个不可读的二进制代码文件。可通过工具逆向转化为文本文件objdump -d test.o。以下是两个目标文件的逆向转化后的比较。

df401d9b68b2dfe0cee3f79e7b5a76a4.png

073919e5692c1071665e4d218d04141a.png

链接操作的步骤:

1.符号解析

程序中有定义和引用的符号(包括变量名,函数名),存放在符号表(.symtab)。符号表是一个结构数组,包含符号名、长度和位置等信息。编译器将符号的引用存放在重定位节(.rel.text和.rel.data)。链接器将每个符号的引用都与一个确定的符号定义建立关联。

705af57972f45e96cf7cddeb16d77640.png
重定位信息

2.重定位

将多个代码段和数据段分别合并为一个单独的代码段和数据段,计算每个定义的符号在虚拟地址空间的绝对地址。将可执行文件中的符号引用处修改为重定位后的地址信息。


符号类型:

  1. 全局符号(Global symbols):由模块m定义并能被其他模块引用的符号。(指不带static的全局变量)
  2. 外部符号(External symbols):由其他模块定义,并被模型m引用的全局符号。
  3. 局部符号(Local symbols):仅由模块m定义和引用的本地符号。例如,在模型m中定义的static函数和变量。

294f1647936b42468e6e0d357e378930.png

在开发过程中,容易遇到的符号重复定义的问题。比如:

//a.cpp文件
//b.cpp文件

两个文件在合并数据段时,就会发生链接错误。因为两个文件重复定义了变量名,在链接过程中,无法用同一符号,表示两个变量。如果,是一个强定义,一个弱定义编译通过。所谓,强定义就是定义的同时赋值,而弱定义只定义。所以,养成良好的编程习惯。

  • 尽量使用本地变量(static),模块内引用不太会出错
  • 全局变量要赋初值,使成为强符号,易查出链接错误
  • 外部全局变量使用extern,以示其引用其他模块

817d3a0d414ddee429331dc439152675.png
在链接后,函数p1修改yz的值,会重定位到main.c中定义的yz的地址

d4838fd498ff7835301f9efec1d8faa2.png
在编译阶段,两个文件是分开编译的,所以p1.c中把d当作一个double来处理了,生成相对应的汇编代码。在对d赋值时,设计上就会修改main.c中定义的在数据段中的d,同时会覆盖x。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值