CSAPP——chap7 Link

一、gcc编译程序的四个过程

在这里插入图片描述

预处理(hello.c --> hello.i)

1.命令:gcc -E hello.c -o hello.i 或 cpp hello.c > hello.i
2.过程操作:处理源文件中以“ # ”开头是预编译指令,包括:
(1)删除“ #define ”并展开所定义的宏。
(2)处理所有文件预编译指令,如“ #if ”,“ ifdef ”,“ #endif ”等。
(3)插入头文件到“ #include ”处,可以递归方式进行处理。
(4)删除所有的注释“ // ”和“ /* */ ”。
(5)添加行号和文件名标识,以便编译时编译器产生调试用的行号信息。
(6)保留所有 #pragma 编译指令(编译器需要用)
3.结果:生成预处理文件( hello.i )——可读性文本文件,不包含任何宏定义。

编译(hello.i --> hello.s)

1.命令: gcc -S hello.i -o hello.s 或 gcc -S hello.c -o hello.s
2.结果:生成汇编代码文件(hello.s )——可读性文本文件,CPU无法执行理解。

汇编(hello.s --> hello.o)

1.命令: gcc -c hello.s -o hello.o 或 gcc -c hello.c -o hello.o
2.结果:生成可重定位目标文件(hello.o )——不可读的二进制形式

链接

连接过程是将多个可重定位目标文件合并以生成可执行目标文件
1.命令: gcc static -o myproc main.o test.o (static 表示静态链接,如果不指定 -o 选项,则可执行文件名为“ a.out ”)
结果:生成可执行目标文件(如 a.out 文件)

二、三类目标文件

1.可重定位目标文件(.o):
数据和代码可和其他可重定位文件合并成可执行文件。每个 .o 文件由对应的 .c 文件生成。每个 .o 文件代码和数据地址都从0开始。
2.可执行目标文件(Linux 默认为 a.out )
包含的代码和数据可以被直接复制到内存并执行。代码和数据地址为虚拟地址空间中的地址。
3.共享目标文件。特殊的可重定位目标文件,能在装入或运行时被装入到内存并自动保存并自动被链接,成为共享库文件。

三、ELF (Executable and Linkable Format)格式

一种目标文件的格式,称为可执行可链接格式。

两种视图

#include <stdio.h>
int x=100;
int y;
void prn(int n)
{
 printf("%d\n",n);
}
void main( )
{
 static int a=1;
 static int b;
 int i=200,j;
 prn(x+a+i);
}

以下均为该代码运行结果。

1.链接视图(被链接):可重定位目标文件

ELF头
. text
. rodata
. data
. bss
. symtab
. rel . text
. rel .data
. debug
. line
. strtab
节部头表

节(section)是ELF文件中具有相同特征的最小可处理单位。
| ELF头 | :包含16字节标识信息、文件类型、机器类型、节头表的偏移、节头表 表项大小以及表项个数。
| . text | :已编译程序的机器代码(编译后的代码部分)。
| . rodata | :只读数据,如 printf 格式串、switch 跳转表等。
| . data | :已初始化的全局变量和静态变量。
| . bss | :未初始化的全局变量和静态变量,以及所有被初始化为0的全局或静态变量。仅是占位符,不占任何实际磁盘空间。
| . symtab | :存放函数名和全局变量(符号表)信息,不包括局部变量(在栈里)。
| . rel . text | :.text 节的重定位信息,用于重新修改代码段的指令中的地址信息。
| . rel .data | :.data 节的重定位信息,用于对被模块使用或定义的全局变量进行重定位的信息。
| . debug | : 调试用符号表。(gcc -g)
| . line |:原始C源程序中的行号和 .text 中机器指令之间的映射,只有 gcc -g 时才会得到。
| . strtab | :包含symtab 和 debug 节中符号及节名。
| 节部头表 | :每个节的节名、偏移和大小。

ELF头

命令: readellf -h 10_3.o 结果如下:
在这里插入图片描述入口点地址为0,因为是可重定位目标文件。

程序头起点为0,因为没有程序头表

节部头表

命令: readellf -S 10_3.o 结果如下:
在这里插入图片描述
起始虚拟地址都为0
4个节将会分配内存空间 .text .data 和 .bss .rodata
如 .text 的起始位置(对齐)00000040 + .text 的大小 50 = 00000090,即 . data 的起始位置

. symtab

命令: readellf -s 10_3.o 结果如下:在这里插入图片描述

2.执行视图(被执行):可执行目标文件

ELF头
段头部表
. init
. text
. rodata
. data
. bss
. symtab
. debug
. line
. strtab
节部头表

为了能执行,需要将具有相同访问属性的节合并成段。
与可重定位文件差别:
1.ELF头中字段e_entry 给出执行程序时第一条指令的地址,在重定位文件中则为0。
在这里插入图片描述
入口点地址为确定的值
程序头为确定的值,由确定数值可以看出 程序头表紧接着在ELF头后面

2.多一个程序头表(段头表),是一个结构数组。
在这里插入图片描述
3.多一个 .init 节,英语定义 init 函数,该函数用来进行可执行目标文件开始执行时的初始化工作。
4.少两个 .rel 节(无需重定位)

四、链接

步骤

(1)确定符号间引用关系
(2)合并相关.o文件
(3)确定每个符号的地址
(4)在指令中填入新地址

1.符号解析(1)

程序中有定义和引用的符号(包括变量和函数)
编译器将定义的符号存放在一个符号表中,符号表是一个结构数组,在 symtab 中
编译器将每个符号的引用都与一个确定的符号定义建立关联

符号的定义和引用

定义:有类型说明 存储类型说明和数据类型的说明。
引用:无类型说明。
符号定义的实质:分配存储空间。
在这里插入图片描述
局部变量不是符号 不出现在符号表。

链接符号的类型

1.全局符号
模块内部定义的,能被其他模块引用的符号,如非 static 函数和非 static 全局变量。
2.外部符号
外部定义的,由其他模块定义由本模块引用的全局符号。
3.局部符号
本模块定义并引用的,仅由本模块定义和引用的本地符号。
在这里插入图片描述

全局符号的强、弱

强符号:函数名和已初始化的全局变量名。
弱符号:未初始化的全局变量。
在这里插入图片描述
多重定义符号的处理规则
1.强符号不能多次定义
2.若一个符号被定义为一次强符号和多次弱符号,,则按强定义为准
将可执行目标文件中符号引用处的地址修改为重定位后的地址信息

Linux 下
//mismatch-main.c
long int x;  /* Weak symbol */
#include <stdio.h>
 int main(int argc, char *argv[]) {
 printf("%ld\n", x);
return 0;
}
//文件mismatch-variable
/* Global strong symbol */
double x = 3.14;

第一段代码中定义了一个长整型的弱符号x,并且没有赋予初值,在第二段代码mismatch-variable中定义了一个长整型的强符号x,并赋值为3.14。
在Linux平台下输入 gcc -Wall -Og -o mismatch mismatch-main.c mismatch-variable.c
得到一个重定位目标文件 mismatch
再输入 ./mismatch ,得结果4614253070214989087
如图:
在这里插入图片描述
-Wall是表示允许发出gcc提供的所有有用的报警信息
-Og是表示启用全局优化
-o是表示设定输出文件名,不加此选项会默认可执行文件名为a.out
此外,这条命令也可以将.o文件链接到可执行目标文件。

//global.h
extern int g;
int f();
//global-c1.c
#include "global.h"
int f() {
    return g+1;
}
//global-c2.c
#include <stdio.h>
#include <stdlib.h>
#include "global.h"
int g = 0;
 int main(int argc, char *argv[]) {
  if (argc >= 2) {
  g = atoi(argv[1]);
     }
     printf("g = %d.  f() = %d\n", g, f());
    return 0;
}

在这里插入图片描述
在这里是定义了一个int型的全局变量g,并且赋予了初值0,所以执行出来的结果是没有问题的。

2.重定位(2)(3)(4)

将多个代码段和数据段分别合并为一个单独的代码段和数据段
计算每个定义的符号在虚拟地址空间的绝对地址
将可执行目标文件中符号引用处的地址修改为重定位后的地址信息
————————————————————————————————————
声明:本文图片及部分内容来自中国MOOC 南京大学袁春风老师的计算机系统基础(一):程序的表示、转换与链接。
附链接:https://www.icourse163.org/learn/NJU-1001625001#/learn/content

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值