深入理解计算机—链接深入理解计算机系统(原书第3版)(美)布赖恩特(Bryant,R.E.) 等
#include<stdio.h>
int main()
{
printf("hello ,world!\n");
}
hello.c —> 预处理器(cpp)—>编译器—>汇编器—>链接器—>可执行
gcc使用
GCC编译选项 | |
---|---|
-c | 编译、汇编指定的源文件,但是不进行链接 |
-s | 编译指定的源文件,但是不进行汇编 |
-E | 预处理指定的源文件,不进行编译 |
-o | 选项用来指定输出文件,[infile] -o [outfile],如不使用 -o 选项,默认的输出a.out |
-I directory | 指定 include 包含文件的搜索目录 |
-g | 生成调试信息,该程序可以被调试器调试 |
-save-temps | 生成中间文件.i、.s和.o,分别表示为预处理输出、汇编语言输出和对象文件 |
编译器驱动程序
linking 将不同部分的代码和数据收集组合成一个单一文件过程。该文件被加载或被拷贝到存储器并执行。链接可执行于:编译时,加载时,或运行时。使大型软件的分离编译成为可能。
关于连接器:
- 构造大型程序
- unix,默认下,错误定义多个全局变量的程序将通过连接器。迷惑难以调试
- 作用域规则全局和局部变量区别,静态属性的变量或函数意义
- 理解其他系统概念。eg.加载和运行程序,虚拟存储器,分页和存储器映射
- 理解开发共享库
1、编译器驱动程序
/*swap.c*/
extern int buf[];
int *bufp0=&buf[0];
int *bufp1;
void swap()
{
int temp;
bufp1=&buf[1];
temp=*bufp0;
*bufp0=*bufp1;
*bufp1=temp;
}
/*main.c*/
void swap();
int buf[2]={1,2};
int main()
{
swap();
return 0;
}
gcc -o2 -g -o p main.c swap.c
//-v 查看步奏
cpp [other argumnets] main mian.i
cc1 main.i main.c -o2[other arguments] -o mian.s
2、静态链接
输入:一组可重定位目标文件和命令行参数 ( 代码和数据节组成,指令在一个节中,初始化的全局变量或未初始化的变量又在另一节中)
输出:完全链接可加载和运行的可执行目标文件
两个主要任务:
- 符号解析 ,目标文件定义和引用符号。目的:将每个符号引用和一个符号定义联系起来
- 重定位 ,编译器和汇编器生成从地址0开始的代码和数据节。连接器通过把每个符号定义与一个存储器位置联系起来,然后修改所有对这些符号的引用,使得它们指向这个存储器位置,从而定位这些节。
—目标文件—纯粹是字节块的集合。包含代码和数据,指导链接器和加载器的数据结构。
—链接器将这些块链接起来,确定链接块的运行时位置,并修改代码和数据块中的各种位置。
—链接器对目标机器了解甚少,产生目标文件的编译器和汇编器已完成大部分工作。
3、目标文件
- 可重定位目标文件,其形式可在编译时与其他可重定位目标文件合并起来,创建一个可执行目标文件
- 可执行目标文件,其形式可以被直接拷贝到存储器并执行
- 共享目标文件,一种特殊的可重定位目标文件,可在加载或运行时,被动态加载到存储器并链接
- 编译器和汇编器生成可重定位目标文件(包含共享目标文件)。链接器生成可执行目标文件。
4、可重定位目标文件
c语言的ELF文件格式学习
/*elf.c*/
#include <stdio.h>
#include <stdlib.h>
int data[100] ={0};
int bss[100];
int main()
{
int i=0;
for(i=0; i<100; i++)
bss[i] = i;
printf("the bss[3]= %d\n", bss[3]);
return 1;
}
// readelf -h
//gcc elf.c //output a.out
//readelf -h a.out
ELF Header:
//确认读入是否是elf头。7f默认,45,4c,46 表示
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 elf
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x400430
Start of program headers: 64 (bytes into file)
Start of section headers: 6672 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 31
Section header string table index: 28
//objdump -d a.out >assembel.txt
0000000000400430 <_start>:
400430: 31 ed xor %ebp,%ebp
400432: 49 89 d1 mov %rdx,%r9
400435: 5e pop %rsi
400436: 48 89 e2 mov %rsp,%rdx
400439: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
40043d: 50 push %rax
40043e: 54 push %rsp
40043f: 49 c7 c0 f0 05 40 00 mov $0x4005f0,%r8
400446: 48 c7 c1 80 05 40 00 mov $0x400580,%rcx
40044d: 48 c7 c7 26 05 40 00 mov $0x400526,%rdi
400454: e8 b7 ff ff ff callq 400410 <__libc_start_main@plt>
400459: f4 hlt
40045a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
objdump -x a.out //objdump -x a.out 显示目标文件的所有头的信息,包括ELF文件头、
//程序头(Program Header)和节头(Section Header)。除了这些头之外,
//还包括动态节区(Dynamic Section)和符号表(Symbol Table)内的信息。例
a.out: file format elf64-x86-64
a.out
architecture: i386:x86-64, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x0000000000400430
//程序头表内容,记录程序需拷贝到内存的内容。
Program Header:
//PHDR 表示要保存的内容是程序头表。offset 表示该项内容保存的起始地址与程序头地址的偏移
PHDR off 0x0000000000000040 vaddr 0x0000000000400040 paddr 0x0000000000400040 align 2**3
filesz 0x00000000000001f8 memsz 0x00000000000001f8 flags r-x
//指定程序从已经从可执行映射到内存之后,必须调用解释器。?
INTERP off 0x0000000000000238 vaddr 0x0000000000400238 paddr 0x0000000000400238 align 2**0
filesz 0x000000000000001c memsz 0x000000000000001c flags r--
//表示一个二进制文件映射到虚拟地址空间的段,其中保存了常量数据(如字符串),程序的目标代码等等
LOAD off 0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**21
filesz 0x000000000000073c memsz 0x000000000000073c flags r-x
LOAD off 0x0000000000000e10 vaddr 0x0000000000600e10 paddr 0x0000000000600e10 align 2**21
filesz 0x0000000000000228 memsz 0x0000000000000580 flags rw-
//保存其他动态链接器(即INTERP中指定的解释器,第二个)
DYNAMIC off 0x0000000000000e28 vaddr 0x0000000000600e28 paddr 0x0000000000600e28 align 2**3
filesz 0x00000000000001d0 memsz 0x00000000000001d0 flags rw-
NOTE off 0x0000000000000254 vaddr 0x0000000000400254 paddr 0x0000000000400254 align 2**2
filesz 0x0000000000000044 memsz 0x0000000000000044 flags r--
EH_FRAME off 0x0000000000000614 vaddr 0x0000000000400614 paddr 0x0000000000400614 align 2**2
filesz 0x0000000000000034 memsz 0x0000000000000034 flags r--
STACK off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4
filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
RELRO off 0x0000000000000e10 vaddr 0x0000000000600e10 paddr 0x0000000000600e10 align 2**0
filesz 0x00000000000001f0 memsz 0x00000000000001f0 flags r--
Dynamic Section:
NEEDED libc.so.6
INIT 0x00000000004003c8
FINI 0x00000000004005f4
INIT_ARRAY 0x0000000000600e10
//......................
//......
Dynamic Section:
NEEDED libc.so.6
INIT 0x00000000004003c8
FINI 0x00000000004005f4
INIT_ARRAY 0x0000000000600e10
INIT_ARRAYSZ 0x0000000000000008
FINI_ARRAY 0x0000000000600e18
FINI_ARRAYSZ 0x0000000000000008
GNU_HASH 0x0000000000400298
STRTAB 0x0000000000400318
SYMTAB 0x00000000004002b8
STRSZ 0x000000000000003f
SYMENT 0x0000000000000018
DEBUG 0x0000000000000000
PLTGOT 0x0000000000601000
PLTRELSZ 0x0000000000000030
PLTREL 0x0000000000000007
JMPREL 0x0000000000400398
RELA 0x0000000000400380
RELASZ 0x0000000000000018
RELAENT 0x0000000000000018
VERNEED 0x0000000000400360
VERNEEDNUM 0x0000000000000001
VERSYM 0x0000000000400358
Version References:
required from libc.so.6:
0x09691a75 0x00 02 GLIBC_2.2.5
//text,data,bss三个段。
//其中,text表示的是程序的代码段。
//后面的data和bss段。.bss段在文件中是不占大小的。因为,bss段代表的是未初始化的全局变量。在C里面,未初始化的全局变量会被初始化为0
Sections:
Idx Name Size VMA LMA File off Algn
0 .interp 0000001c 0000000000400238 0000000000400238 00000238 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .note.ABI-tag 00000020 0000000000400254 0000000000400254 00000254 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .note.gnu.build-id 00000024 0000000000400274 0000000000400274 00000274 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .gnu.hash 0000001c 0000000000400298 0000000000400298 00000298 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .dynsym 00000060 00000000004002b8 00000000004002b8 000002b8 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .dynstr 0000003f 0000000000400318 0000000000400318 00000318 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .gnu.version 00000008 0000000000400358 0000000000400358 00000358 2**1
CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .gnu.version_r 00000020 0000000000400360 0000000000400360 00000360 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 .rela.dyn 00000018 0000000000400380 0000000000400380 00000380 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 .rela.plt 00000030 0000000000400398 0000000000400398 00000398 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
10 .init 0000001a 00000000004003c8 00000000004003c8 000003c8 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
11 .plt 00000030 00000000004003f0 00000000004003f0 000003f0 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .plt.got 00000008 0000000000400420 0000000000400420 00000420 2**3
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .text 000001c2 0000000000400430 0000000000400430 00000430 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .fini 00000009 00000000004005f4 00000000004005f4 000005f4 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
15 .rodata 00000014 0000000000400600 0000000000400600 00000600 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
16 .eh_frame_hdr 00000034 0000000000400614 0000000000400614 00000614 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
17 .eh_frame 000000f4 0000000000400648 0000000000400648 00000648 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
18 .init_array 00000008 0000000000600e10 0000000000600e10 00000e10 2**3
CONTENTS, ALLOC, LOAD, DATA
19 .fini_array 00000008 0000000000600e18 0000000000600e18 00000e18 2**3
CONTENTS, ALLOC, LOAD, DATA
20 .jcr 00000008 0000000000600e20 0000000000600e20 00000e20 2**3
CONTENTS, ALLOC, LOAD, DATA
21 .dynamic 000001d0 0000000000600e28 0000000000600e28 00000e28 2**3
CONTENTS, ALLOC, LOAD, DATA
22 .got 00000008 0000000000600ff8 0000000000600ff8 00000ff8 2**3
CONTENTS, ALLOC, LOAD, DATA
23 .got.plt 00000028 0000000000601000 0000000000601000 00001000 2**3
CONTENTS, ALLOC, LOAD, DATA
24 .data 00000010 0000000000601028 0000000000601028 00001028 2**3
CONTENTS, ALLOC, LOAD, DATA
25 .bss 00000350 0000000000601040 0000000000601040 00001038 2**5
ALLOC
26 .comment 00000035 0000000000000000 0000000000000000 00001038 2**0
CONTENTS, READONLY
SYMBOL TABLE:
0000000000400238 l d .interp 0000000000000000 .interp
0000000000400254 l d .note.ABI-tag 000000000
ELF头 (16字节序列:描述字的大小和生成该文件的系统的字节顺序) |
---|
.text —(已编译的机器代码) |
.rodata — (只读数据) |
.data — (已初始化的全局C变量.局部C变量在运行时被保存在栈中,不出现在.data,也不出现在.bss中) |
.bss —未初始化的全局C变量 |
.symtab — 一个符号表存放在程序中被定义和引用的函数和全局变量的信息,注意区别与编译选项-g。.symtab符号表不包含局部变量的表目 |
.rel.text — 当链接器把这个目标文件和其他文件结合时,该节中许多位置都需要修改。 |
.rel.data — 被模块定义或引用的任何全局变量信息。一般而言,任何已被初始化全局变量的初始值是全局变量或者外部定义函数的地址都需要修改 |
.debug 调试符号表 — 其有些表目是程序中定义的局部变量和类型定义, |
.line 原始C源程序中的行号和.text中机器指令之间的映射。只有以 -g 选项调用编译驱动程序时,才会得到这张表 |
.strtab 一个字符串表 ,.symtab 和.debug节中的符号表 |
节头部表 |
5、符号和符号表
每个可重定位目标模块m,都有一个符号表,包含m所定义和引用的符号的信息。在链接器上下文,有三种不同的符号;
- 由m定义并能被其他模块引用的全局符号。 全局链接器符号对应于非静态的C函数以及被定义为不带C的static属性的全局变量
- 由其他模块定义并被模块m引用的全局符号。这些符号称为外部符号external,对应于定义在其他模块中的C函数和变量
- 只被模块m定义和引用的本地符号。有的本地链接器符号对应与带static属性的C函数和全局变量。在模块m中任何地方可见,但不能被其他模块引用。
文件中对应于模块m的节和相应的源文件的名字也能获得本地符号。
注意,本地链接器符号和本地程序变量的不同是很重要的。*.symtab中的符号表不包含对应与本地非静态程序变量的任何符号。这些符号在运行时在栈中被管理。
定义为带有c static 属性的本地过程变量是不在栈中管理的。编译器在.data和.bss中为每个定义分配空间,并在符号表中创建一个唯一名字的本地连接器符号。
int f()
{
static int x=0;
return x;
}
int g()
{
static int x =1;
return x;
}
// 在这种情况下,编译器在.bss中为两个整数分配空间,并引用(export)
//两个唯一的本地连接器符号给汇编器。如:x.1g表示f中的定义,x.2表示函数g中的定义。
.