您的位置:首页 > 职场人生

读书笔记——《程序员自我修养》——目标文件

2015-09-28 14:59 411 查看
PC机上流行的可执行文件格式主要是windows下的PE(portable Executable)和Linux的ELF(Executable Linkable Format),它们都是COFF(Common file format)格式的变种。

不光是可执行文件,动态链接库以及静态链接库文件都是按照可执行文件格式存储的。

使用file命令来查看相应的格式:



SimpleSection.c

/*
*SimpleSection.c
*Linux
*	gcc -c SimpleSection.c
*/
int printf(const char *format, ...);

int global_init_var = 84;
int global_uninit_var;

void func1(int i)
{
printf("%d\n", i);
}

int main(void)
{
static int static_var = 85;
static int static_var2;

int a = 1;
int b;
func1(static_var + static_var2 + a + b);
return a;
}
使用GCC编译,参数-c 表示只编译不链接



可以使用objdump来查看文件结构,-h 可以打印出基本信息,-x会输出复杂信息



看一下文件的大小



画出基本的ELF结构:



一个专门的命令"size",它可以用来查看ELF文件的代码段、数据段和BSS段的长度,des表示十进制的和,hex表示十六进制的和



代码段

objdump的"-s"参数可以将所有端的内容以十六进制的方式打印出来,"-d"参数可以将所有包含指令的段反汇编。

root@mdd-desktop:/media/mdd0/slzsource/ddu/LinkLoader# objdump -s -d SimpleSection.o

SimpleSection.o: file format elf64-x86-64

Contents of section .text:
0000 554889e5 4883ec10 897dfc8b 45fc89c6 UH..H....}..E...
0010 bf000000 00b80000 0000e800 000000c9 ................
0020 c3554889 e54883ec 10c745fc 01000000 .UH..H....E.....
0030 8b150000 00008b05 00000000 8d040203 ................
0040 45fc0345 f889c7e8 00000000 8b45fcc9 E..E.........E..
0050 c3 .
Contents of section .data:
0000 54000000 55000000 T...U...
Contents of section .rodata:
0000 25640a00 %d..
Contents of section .comment:
0000 00474343 3a202855 62756e74 7520342e .GCC: (Ubuntu 4.
0010 342e332d 34756275 6e747535 2e312920 4.3-4ubuntu5.1)
0020 342e342e 3300 4.4.3.
Contents of section .eh_frame:
0000 14000000 00000000 017a5200 01781001 .........zR..x..
0010 1b0c0708 90010000 18000000 1c000000 ................
0020 00000000 21000000 00410e10 4386020d ....!....A..C...
0030 06000000 18000000 38000000 00000000 ........8.......
0040 30000000 00410e10 4386020d 06000000 0....A..C.......

Disassembly of section .text:

0000000000000000 <func1>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 83 ec 10 sub $0x10,%rsp
8: 89 7d fc mov %edi,-0x4(%rbp)
b: 8b 45 fc mov -0x4(%rbp),%eax
e: 89 c6 mov %eax,%esi
10: bf 00 00 00 00 mov $0x0,%edi
15: b8 00 00 00 00 mov $0x0,%eax
1a: e8 00 00 00 00 callq 1f <func1+0x1f>
1f: c9 leaveq
20: c3 retq

0000000000000021 <main>:
21: 55 push %rbp
22: 48 89 e5 mov %rsp,%rbp
25: 48 83 ec 10 sub $0x10,%rsp
29: c7 45 fc 01 00 00 00 movl $0x1,-0x4(%rbp)
30: 8b 15 00 00 00 00 mov 0x0(%rip),%edx # 36 <main+0x15>
36: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 3c <main+0x1b>
3c: 8d 04 02 lea (%rdx,%rax,1),%eax
3f: 03 45 fc add -0x4(%rbp),%eax
42: 03 45 f8 add -0x8(%rbp),%eax
45: 89 c7 mov %eax,%edi
47: e8 00 00 00 00 callq 4c <main+0x2b>
4c: 8b 45 fc mov -0x4(%rbp),%eax
4f: c9 leaveq
50: c3 retq

看一下代码段

Contents of section .text:
0000 554889e5 4883ec10 897dfc8b 45fc89c6 UH..H....}..E...
0010 bf000000 00b80000 0000e800 000000c9 ................
0020 c3554889 e54883ec 10c745fc 01000000 .UH..H....E.....
0030 8b150000 00008b05 00000000 8d040203 ................
0040 45fc0345 f889c7e8 00000000 8b45fcc9 E..E.........E..
0050 c3

反汇编出来的代码正好对应着

开始 0: 55 push %rbp

结束 50: c3 retq

可见.text段中存放的是代码的指令

数据段和只读数据段

.data段保存的是那些已经初始化了的全局静态变量和局部静态变量

int global_init_var = 84;
static int static_var = 85;

这两个变量每个4个字节,一共刚好8个字节,所以".data"这个段的大小为8个字节。

SimpleSection.c里面我们调用"printf"的时候,用到了一个字符串常量"%d\n"

,它是一种只读数据,所以被放到了".rodata"段,我们可以从输出结果看到".rodata"这个段的

4个字节刚好是这个字符串常量ASCII字节序,最后以\0结尾。

".rodata"段存放的是只读数据段,一般是程序里面的只读变量(如const修饰的变量)和字符串常量。

BSS段

.bss段存放的是未初始化的全局变量和局部静态变量,

如上诉代码中

int global_uninit_var;

static int static_var2;

这两个变量是存放在.bss段,但两个变量占内存大小只有4个字节,而实际是8个字节,这里实际上只是预留一个未定义的全局变量符号,

等到最终连接成可执行文件的时候再在.bss段分配空间。

ELF文件头

readelf命令查看ELF文件



0x7f为ASCII字符里面的DEL控制符,后面三个字节刚好是ELF这3个字母的ASCII码也就是0x45 0x4c 0x46。

接下来的一个字节是用来标示ELF文件类型的0x01标示32位的, 0x02表示64位的,第六个字节是字节序,规定

该ELF文件是大端的还是小端的

段表

ELF文件中有很多各种各样的段,这个段表(Section Header Table)就保存这些段的基本属性的结构。

readelf查看elf文件的段



.rela.text 重定位表(rRelocation Table)

链接器在处理目标文件时,须要对目标文件中某些部位进行重定位,即代码段和数据段中那些绝对地址引用的位置。这些重定位的信息都记录在ELF文件的重定位表里面,对于每个须要重定位的代码段或数据段,都会有一个相应的重定位表。比如SimpleSection.o中的".rela.text"就是针对".text"段的重定位表,因为".text"段中至少有一个绝对地址的引用,那就是"printf"函数调用。

.shstrtab字符串表

符号

目标文件的链接实际上是目标文件之间对地址的引用,即对函数和变量的地址的引用。

比如目标文件B要用到了目标文件A中的函数“foo”,那么我们就称目标文件A定义了函数"foo",称目标文件B引用了目标文件A中的函数"foo"。

在链接中,我们将函数和变量统称为“符号”,函数名或变量名就是符号名。

每一个目标文件都会有一个符号表"Symbol Value"

nm查看符号



readelf -s 查看符号

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: