可重定向文件生成
文件的格式与操作系统和编译器密切相关,linux的可执行文件是基于COFF的ELF格式,可重定向文件(也叫做目标文件.o)与可执行文件格式很接近,只是没有经过链接调整符号和地址。
源代码:
/*
* SimpleSection.c
*
* Linux:
* gcc -c SimpleSection.c
*
* Windows:
* cl SimpleSection.c /c /Za
*/
int printf(const char* format, ...);
int global_init_var = 84; //存储在.data section
int global_uninit_var; //.bss section 实际上先放在COMM符号表中,在链接时确定地址后放入.bss
void func1(int i) //.text section
{
printf("%d\n", i);
}
int main(void) //.text section
{
static int static_var = 85; //.data section
static int static_var2; //.bss section
int a = 1;
int b;
func1(static_var + static_var2 + a + b);
return a;
}
gcc -c Simplesection.c //生成可重定位文件,如果加上-o选项,会生成可共享文件
使用file查看文件类型
可重定向文件类型
liyumin@liyumin-B250M-D3H:~/compile_link_exec/elf_file$ file SimpleSection.o
SimpleSection.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
共享文件类型
SimpleSection.o: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=13352fe3ae9e92467df387aaf04926d89f599792, not stripped
本文解析可重定位文件
程序的编译链接过程解析参考另一篇文章:https://blog.csdn.net/starcraft501/article/details/100823506
ELF目标文件总体结构
ELF目标文件除了有编译后的机器指令码,数据,还有一些辅助信息,比如符号表,调试信息,字符串表等。这些信息分布在不同的section中存储。节section的概念用在链接视图中,另外一个段segment的概念用在装载视图中。
| ELF Header
| .text //代码段 code section
| .data //数据段,保存初始化后的全局变量和局部静态变量 data section
| .bss //数据段,保存未初始化的局部静态变量,预留位置,不占ELF文件空间 data section
| ... other sections
| Section Header Table 段表
| String Tables 字符串表
| Symbol Tables 符号表
| ...
解析elf文件使用的工具:binutils
binutils的工具objdump用于查看目标文件的内部结构
-h 打印段的基本信息,忽略了复杂性的段,比如符号表,字符串表,段名字符串表,重定位表等
-x 打印段的详细信息
$objdump -s -d SimpleSection.o //-s 按16进制打印段的内容 -d 将包含指令的段反汇编(从机器指令转换为汇编指令)
objdump -t SimpleSection.o //查看符号表详情
c++filt _ZN1N1C4funcEi //c++filt用于解析被修饰过的名称
size命令查看ELF文件的代码段,数据段,bss段的长度
liyumin@liyumin-B250M-D3H:~/compile_link_exec/elf_file$ size SimpleSection.o
text data bss dec hex filename
179 8 4 191 bf SimpleSection.o
readelf查看ELF文件
readelf -h SimpleSection.o //查看ELF文件头
readelf -S SimpleSection.o //查看ELF完整的段表结构
readelf -s SimpleSection.o //查看符号表
readelf -a //查看所有信息
nm查看ELF文件符号表
nm SimpleSection.o
strip去掉可执行文件中的调试信息
strip xxx
ELF文件头
描述文件属性信息
- 文件类型,是否可执行,静态链接,动态链接;
- 可执行文件类型的入口地址;
- 目标硬件;
- 目标操作系统;
使用$readelf -h SimpleSection.o读取文件头,文件头后就是各个段seciton的内容
ELF文件的详细定义查看ELF标准文档,由编译器厂商,CPU厂商,操作系统厂商供共同完成
ELF 头:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 //e_ident ELF魔数, 包含类别,数据,版本,OS/ABI,ABI版本这5项数据
类别: ELF64 //7f 45 4c 46 , DEL控制符,E,L,F 对应的ASCII码表,是ELF文件的标识,用于操作系统检查文件类型
数据: 2 补码,01 小端序 (little endian) //00无效文件 01 32位 02 64位ELF文件 01 代表小端,02代表大端,00是无效的
版本: 1 (current) //01 第7个字节
OS/ABI: UNIX - System V //00
ABI 版本: 0 //00
类型: REL (可重定位文件) //e_type ELF文件类型,REL:.o目标文件, EXEC:可执行文件, DYN:共享目标文件
系统架构: Advanced Micro Devices X86-64 //e_machine, CPU平台属性,表明该文件只能在X86-64上运行
不同平台下的ELF文件都遵循同一标准,但是文件不是通用的。需要gcc和binutils工具根据不同平台适配。
liyumin@liyumin-B250M-D3H:~/compile_link_exec/elf_file$ objdump -x SimpleSection.o
SimpleSection.o: 文件格式 elf64-x86-64
SimpleSection.o
体系结构:i386:x86-64, 标志 0x00000011:
HAS_RELOC, HAS_SYMS
起始地址 0x0000000000000000
版本: 0x1 //ELF版本,通常为1
入口点地址: 0x0 //入口虚拟地址,操作系统在加载该ELF文件后从这个地址开始执行指令,可重定位文件没有入口地址,为0
程序头起点: 0 (bytes into file)//start of program headers
Start of section headers: 1104 (bytes into file) //e_shoff 段表位置,在ELF文件中的偏移为0x450,
标志: 0x0 //标识平台相关属性,格式为EF_machine_flag, machine为平台,flag为标志
本头的大小: 64 (字节) //文件头大小
程序头大小: 0 (字节) //Size of program headers
Number of program headers: 0
节头大小: 64 (字节) //Size of section headers == sizeof(Elf64_Shdr), 段表描述符大小,每个段一个
节头数量: 13 //Number of section headers,段表描述符数量,即段数量
字符串表索引节头: 12 //Section header string table index 段表字符串表所在段,在段表中的下标
64位文件头Elf64_Ehdr的结构定义在/usr/include/elf.h,另外还有32位的版本Elf32_Ehdr。两个版本的文件头内容一样,但成员占用的字节不一样。使用typedef定义特定的变量类型,规定在不同编译环境下都使用相同的字段长度。
/* Type for a 16-bit quantity. */
typedef uint16_t Elf32_Half; //无符号短整型 2字节
typedef uint16_t Elf64_Half;
/* Types for signed and unsigned 32-bit quantities. */
typedef uint32_t Elf32_Word; //4字节
typedef int32_t Elf32_Sword;
typedef uint32_t Elf64_Word;
typedef int32_t Elf64_Sword;
/* Types for signed and unsigned 64-bit quantities. */
typedef uint64_t Elf32_Xword; //8字节
typedef int64_t Elf32_Sxword;
typedef uint64_t Elf64_Xword;
typedef int64_t Elf64_Sxword;
/* Type of addresses. */
typedef uint32_t Elf32_Addr;
typedef uint64_t Elf64_Addr;
/* Type of file offsets. */
typedef uint32_t Elf32_Off;
typedef uint64_t Elf64_Off;
/* Type for section indices, which are 16-bit quantities. */
typedef uint16_t Elf32_Section;
typedef uint16_t Elf64_Section;
/* Type for version symbol information. */
typedef Elf32_Half Elf32_Versym;
typedef Elf64_Half Elf64_Versym;
typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info, EI_NIDENT = 16, 16个字符*/
Elf64_Half e_type; /* Object file type, typedef uint16_t Elf64_Half; 2*/
Elf64_Half e_machine; /* Architecture ,typedef uint16_t Elf64_Half; 2*/
Elf64_Word e_version; /* Object file version, typedef uint32_t Elf64_Word; 4*/
Elf64_Addr e_entry; /* Entry point virtual address, typedef uint64_t Elf64_Addr; 8*/
Elf64_Off e_phoff; /* Program header table file offset,typedef uint64_t Elf64_Off; 8*/
Elf64_Off e_shoff; /* Section header table file offset,typedef uint64_t Elf64_Off; 8*/
Elf64_Word e_flags; /* Processor-specific flags, typedef uint32_t Elf64_Word; 4*/
Elf64_Half e_ehsize; /* ELF header size in bytes,typedef uint16_t Elf64_Half; 2*/
Elf64_Half e_phentsize; /* Program header table entry