Questions
text段的每一条指令因操作数不同,所占内存空间也不一样,
那么pc++,怎么知道下一条指令是几个字节呢?一般是4字节?
比如:
289 00000000004004d0 <main>:
290 4004d0: 55 push %rbp
291 4004d1: 48 89 e5 mov %rsp,%rbp
292 4004d4: 48 83 ec 10 sub $0x10,%rsp
293 4004d8: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
294 4004df: be 03 00 00 00 mov $0x3,%esi
295 4004e4: bf 02 00 00 00 mov $0x2,%edi
296 4004e9: e8 16 00 00 00 callq 400504 <swap>
Comments:
skip: no build note for this section
missing: no study for this section
Chapter Seven LINKING
链接(linking)是将各种代码和数据收集并组合起来为单一文件的过程,生成的文件可以被加载到存储器并执行。
linking可以执行于compile time,也可以执行于load time,甚至可以执行于run time
linking过程是由连接器(linker)来完成的。
编译系统流程
cpp(C Preprocessor)将C代码翻译成*.i;
cc(C compiler)翻译成*.s;
as(assemble)生成relocatable object file(可重定向目标文件)*.o;
ld(linker)最终链接为executable object file
输入段 vs 输出段
输 入段和输出段是相对于要生成最终的elf或binary时的Link过程说的,Link过程的输入大都是由源代码编绎生成的目标文件.o,那么这些.o文 件中包含的段相对link过程来说就是输入段,而Link的输出一般是可执行文件elf或库等,这些输出文件中也包含有段,这些输出文件中的段就叫做输出段。
7.2 Static linking
ld是静态连接器。主要完成两件事情:
1. sybmol resolution:将每个符号引用和对应定义建立联系
2. relocation:修改符号引用地址,使指向其符号在储存器存放的位置
7.3 Object file
目标文件的三种形式:
1. relocatable object file,由as生成,可以被linking为executable object file
2. executable object file
3. shared object file, like dynamic lib fie
在Unix(linux)版本中,以上三种目标文件的格式为Unix Executable and Linkable Format(ELF)
7.4 Relocatable object file
ELF relocatable object file格式如下:
ELF header(16 bytes)+ each section ..... + section header table
eg.
-------------------------------------------------------------
chuan.jiang:~/temp$ readelf -a main.o
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 312 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 64 (bytes)
Number of section headers: 12
Section header string table index: 9
其中ELF header主要包含系统字长和字节序,目标文件类型,节头部表的offset和section number等
主要的section介绍,其余的参见【P451 7.4】
.text 机器代码
.rodata 只读数据
.data 已初始化全局C变量
.bss 未初始化全局C变量(主要为了节省elf空间)
.symtab 符号表,存放定义和引用的函数和全局变量信息,不包含局部变量
7.5 Symble table
符号表中的符号有三类:
1. 本模块定义的可以被其他模块引用的全局符号
2. 其他模块定义,由本模块引用的全局符号
3. 只被本模块定义和引用的本地符号
注意:不包含局部变量,他们只在stack中创建和使用
Example of Symbol table '.symtab'
For example, dump symbol table of main.o by using readelf
-------------------------------------------------------------
chuan.jiang:~/temp$ cat main.c
#include <string.h>
#include <stdio.h>
extern int swap(int a, int b);
int buf[2] = {1, 2};
int main()
{
int *ret = NULL;
*ret = swap(2, 3);
return *ret;
}
-------------------------------------------------------------
chuan.jiang:~/temp$ readelf main.o -aV
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000000000 00000040
000000000000002d 0000000000000000 AX 0 0 4
[ 2] .rela.text RELA 0000000000000000 00000558
0000000000000018 0000000000000018 10 1 8
[ 3] .data PROGBITS 0000000000000000 00000070
0000000000000008 0000000000000000 WA 0 0 4
[ 4] .bss NOBITS 0000000000000000 00000078
0000000000000000 0000000000000000 WA 0 0 4
[ 5] .comment PROGBITS 0000000000000000 00000078
0000000000000026 0000000000000001 MS 0 0 1
[ 6] .note.GNU-stack PROGBITS 0000000000000000 0000009e
0000000000000000 0000000000000000 0 0 1
[ 7] .eh_frame PROGBITS 0000000000000000 000000a0
0000000000000038 0000000000000000 A 0 0 8
[ 8] .rela.eh_frame RELA 0000000000000000 00000570
0000000000000018 0000000000000018 10 7 8
[ 9] .shstrtab STRTAB 0000000000000000 000000d8
0000000000000059 0000000000000000 0 0 1
[10] .symtab SYMTAB 0000000000000000 00000438
0000000000000108 0000000000000018 11 8 8
[11] .strtab STRTAB 0000000000000000 00000540
0000000000000016 0000000000000000 0 0 1
........
Symbol table '.symtab' contains 11 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS main.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 6
6: 0000000000000000 0 SECTION LOCAL DEFAULT 7
7: 0000000000000000 0 SECTION LOCAL DEFAULT 5
8: 0000000000000000 8 OBJECT GLOBAL DEFAULT 3 buf
9: 0000000000000000 45 FUNC GLOBAL DEFAULT 1 main
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND swap
其中,Ndx表示本符号对应的section在section header table中的index,其中有三种特殊伪节(pseudo section), ABS代表不被重定义的符号;UNDEF代表未定义的符号,即在本模块引用,在其他模块定义;COMMON表示未被分配位置的未初始化数据目标。
可见,符号main在section 1中,即 .text section;buf在 .data section
其中value为section offset(对于executable file为absolute runtime address),size表示object的大小,Bind表示local or global
7.6 Symbol resolution
7.6.1 解析多重定义的全局变量
规则1:不允许有多个strong symbol
规则2:存在一个strong symbol和多个weak symbol,选择strong
规则3:存在多个weak,任意选择一个weak
strong symbol:函数和已经初始化的全局变量
weak symbol:未初始化的全局变量
举例1:
int f()
{
static int x = 0;
return x;
}
int f()
{
static int x = 1;
return x;
}
NOTE:以上是合法的,并且符号表main.o 和 swap.o 中x的name 有区分,如下:
5: 0000000000000000 4 OBJECT LOCAL DEFAULT 4 x.2216
5: 0000000000000000 4 OBJECT LOCAL DEFAULT 5 x.1595
举例2:
file1.c
int x = 1;
int y = 0;
....
file2.c
double x;
x = 1.0;
以上编译OK,但是file2.c会关联到file1.c的int类型x,导致1.0赋值后会对y存储区覆盖
7.6.2 Static library
静态库为一种存档(archived file)文件,是将一组组合起来的relocatable object file的set,后缀名为*.a。
由ar工具生成,作为ld的输入,ld仅仅将依赖的*.o链接入ELF中。
ld使用*.a来解析符号时,*.a一般放在命令行的末尾,否则无法找到*.a中相应的符号,如下:
gcc -static main.c ./libvector.a
另外,各个*.a之间也会存在互相依赖,一种方法是把互相依赖的*.a重复交替写在gcc命令参数中,一种方法是将这些*.a合并成一个*.a
7.7 Relocation
大体上说,重定位的步骤是按照section的顺序,依次合并各个section
,并依次将各个section中引用的地址改写为运行时地址
重定位分两步骤:
1. 重定位section和symbol definition
2. 重定位symbol reference
NOTE:linker会对每个符号引用记录一个relocation entry,以便在linking时修改这个符号的引用,代码和数据的重定位条目放在.rel.text and .rel.data。
注意,不同寻址方式对应的地址有所差异
7.7.2 symbol relocation
没有完全理解,书中是x86_32的汇编码,不同机器的汇编码有所差异。
skip left
7.8 Executable Object File
ELF文件格式大致如下:
[ELF header + segment header table + .init + .text + .rodata ]+ [.data + .bss] + [.symtab ....... section header table]
以上三部分分别为:只读代码段,读写数据段,不加载到存储器的符号表和调试信息
其中segment header table为连续文件节到运行存储器的映射关系;
.init包含_init函数,负责程序的初始化