目标文件就是源代码编译后但未进行链接的那些中间文件。
COFF、ELF、PE又是什么?
COFF(Common file format)又称通用目标文件格式,是一种用于可执行文件、目标代码、共享库(shared library)的文件格式,使用于类UNIX系统上。它最早使用于UNIX System V上,用来取代先前的a.out格式,后来又发展出XCOFF与ECOFF。COFF最主要的贡献是引入了“段”的机制,不通的目标文件可以拥有不同数量及不同类型的“段”
ELF (Executable and Linkable Format)可执行与可链接格式 常被称为ELF格式,在计算机科学中,是一种用于可执行文件、目标文件、共享库和核心转储的标准文件格式。COFF变种 LINUX使用ELF
PE (Portable Execuable)可移植性可执行文件是一种用于可执行文件、目标文件和动态链接库的文件格式,主要使用在32位和64位的Windows操作系统上。COFF变种 WINDOWS使用
目标文件格式
ELF文件类型 | 说明 | 实例 |
可重定位文件(Relocatable File) | 这类文件包含了代码和数据,可以被用来 | Linux的.o windows的.obj |
可执行文件(Executable File) | 这类文件包含了可以直接执行的程序,它 | 如:Liunx /bin/bash windows .exe |
共享目标文件(Shared Object File) | 1、首先链接编辑器可以将它和其它可重定位文件和共享目标文件一起处理, 生成另外一个目标文件。 2、其次动态链接器(Dynamic Linker)可能将它与某 个可执行文件以及其它共享目标一起组合,创建进程映像。 | 如:Linuxx下的.so widnows的DLL |
核心转存文件(Code Dump File) | 当进程意外终止时,系统可以将该进程的 | Linux下的core dump |
Linux下查看文件格式
重定位文件
[root@localhost ~]# file /usr/lib64/crt1.o
/usr/lib64/crt1.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), for GNU/Linux 2.6.32, not stripped
可执行文件
[root@localhost ~]# file /bin/bash
/bin/bash: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared lib s), for GNU/Linux 2.6.32, BuildID[sha1]=9223530b1aa05d3dbea7e72738b28b1e9d82fbad, stripped
目标共享文件
[root@localhost ~]# file /lib/ld-2.17.so
/lib/ld-2.17.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (GNU/Linux), dynamically linked, BuildID[sha1]=33446bb2067122aef759b21f15245840c26afa0a, not stripped
目标文件样子
上面少的.bss Section,未初始化的全局变量和局部变量一般放在.bss中,未初始化的变量默认值为0,实际上可以放在data段中,但是没啥意义还需要占用些内存。BSS中只是给变量预留位置并不占用内存
总体来说,程序源代码编译以后主要分成两个段,程序指令和程序数据。代码段属于程序指令,而数据段和.BSS都属于程序数据
实战分析
实验代码:
1 int printf( const char* format, ... ); 2 3 int global_init_var = 84; 4 int global_uninit_var; 5 6 void func1( int i ) { 7 printf( "%d\n", i ); 8 } 9 10 int main( void ) { 11 static int static_var = 85; 12 static int static_var2; 13 int a = 1; 14 int b; 15 func1( static_var + static_var2 + a + b ); 16 return a; 17 }
生成目标文件
[root@localhost ~]# gcc -c test1.c
查看目标文件结构和内容(-x选项输出更多内容)
[root@localhost ~]# objdump -h test1.o
补充其它段信息:
.rodata 只读数据段
.comment 注释信息段
.note.GNU-stack 堆栈提示段
段格式
Size 段的长度
File off 段所在位置
CONTENTS 表示该段在文件中存在
ALLOC 表示段的各种属性
查看ELF代码段、数据段、BSS段(dec表示三个段长度和的十进制,hex表示三个段长度和的十六进制)
[root@localhost ~]# size test1.o
text data bss dec hex filename
176 8 4 188 bc test1.o
[root@localhost ~]# objdump -s -d test1.o
-s 以十六进制显示
-d 反汇编包含所有指令的段
大致说下结果:
最左面一侧是偏移量,中间4列是十六进制内容,最后右面一列是text段的ASCII码,
data段(初始化全局和局部变量)
Contents of section .data:
0000 54000000 55000000 T...U...
54000000对应是84,55000000 对应的是85
int global_init_var = 84;
static int static_var = 85;
bss段(未初始化全局变量和局部变量)
.bss 00000004 0000000000000000 0000000000000000 0000009c 2**2
00000004=4字节 代码global_uninit_var; static int static_var2; 前面4个字节但是这里有2个变量,显然有问题。实际在.bss段中只有static_var2。查看 SYMBOL TABLE 发现global_uninit_var没有存在任何地方 只是一个*COM*符号,这其实跟不同的语言与不同编译器有关系,有些编译器会将未初始化全局变量放在.bss中
详细查看ELF文件头
[root@localhost ~]# readelf -h test1.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: 1048 (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: 13
Section header string table index: 12