《程序员的自我修养》--目标文件有什么和ELF文件格式简述

来源:互联网 发布:他知而自不知的我属于 编辑:程序博客网 时间:2024/06/05 11:27

A.目标文件的格式

目标文件定义

源码经过编译之后但是没有经过链接的中间文件。

理解:

目标文件和可执行文件的结构相似,所以和可执行文件采用一种格式存储。可执行文件的格式,主要有PE(Portable Executable )和ELF(Executable Linkable Format)。目标文件和可执行文件可以看成一种类型的文件。同理,动态链接库和静态链接库也是按照可执行文件的格式存储的。

以后这里主要说的是Linux下的ELF存储格式。

文件类型 备注和解释 例子 可重定位文件 包含代码和数据,可以链接成可执行文件和共享目标文件 hello.o 可执行文件 包含可以直接执行的程序,一般没有扩展名 /bin/bash 共享目标文件 包含代码和数据,有两个用处,一是和目标文件或者其他的共享目标文件进行链接,产生新的目标文件,二是动态链接器可以把几个共享目标文件与可执行文件结合,作为进程映像的一部分来运行 Linux的.so文件 核心转储文件 进程意外中指时,系统把该进程的地址空间中内容和一些终止时信息转储到核心转储文件 Linux 下的core dump

几个例子:

这里写图片描述

B.目标文件的内容

段:目标文件的不同信息放在不同的段里。

#include <stdio.h>#include <stdlib.h>int global_var1 = 66;            //全局变量1放在.data段int global_var2;                 //全局变量2放在.bss段int func(int temp){    return temp;}                                //func函数放在.text段int main(int argc, char *argv[]){    static int s_var1 = 77;      //局部静态变量放在.data段    static int s_var2  = 0;      //局部静态变量放在.bss段    int a = 1;                   //局部变量放在.text段    int b;                       //局部变量放在.text段    printf(" %d \n",func(a));    return 0;}

额外的解释:

ELF文件的简单结构 文件头:描述文件属性,是否可执行,如果可执行,目标硬件和操作系统等,段表 .text section:目标文件的代码段 .data section:目标文件的数据段,存放非零全局变量和非零静态变量 .bss section:这个段特殊,它是为没有初始化的全局变量和局部静态变量寓预留位置,没有内容,在文件中不占有空间,只有到真正可执行的时候才有空间

段表:
一个描述文件中各个段的数组,它描述了段的属性,每个段在文件中的偏移等。

为什么数据和指令要分开?

1.代码安全。数据可读写,但是指令一定是只读,,如果把他们映射到两个虚存区域,则这两个虚存区域的权限可以设置成可读写和只读。
2.提高程序的局部性。
3.节省空间。指令只读就可以在内存中只留一份这样的指令(程序共享指令),可以节省空间。

这里写图片描述

C.再看目标文件的段们

源代码是这样的:

#include <stdio.h>#include <stdlib.h>int global_var1 = 66;            //全局变量1放在.data段int global_var2;                 //全局变量2放在.bss段int func(int temp){    return temp;}                                //func函数放在.text段int main(int argc, char *argv[]){    static int s_var1 = 77;      //局部静态变量放在.data段    static int s_var2  = 0;      //局部静态变量放在.bss段    int a = 1;                   //局部变量放在.text段    int b;                       //局部变量放在.text段    printf(" %d \n",func(a));    return 0;}

它的目标文件是这样的:
(命令:objdump -h test.o)
这里写图片描述
.rodatal : 只读数据段
.comment : 注释信息段
.note.GNU-stack : 堆栈提示段
eh_frame: 异常调试段

查看源文件和目标文件数据的对应:

(命令:size test.o)
这里写图片描述

代码段:

这里写图片描述
这里写图片描述

数据段和只读数据段

bss段

其他段

段名称 解释 .rodatal 存放的是只读数据,比如字符串常量,const变量 .comment 编译器版本信息 .debug 调试信息 .dynamic 动态链接信息 .hash 符号哈希表 .line 调试的行号表,源代码行号与编译后指令的对应表 .note 额外的编译信息 .strtab 存储ELF文件中用到的字符串们 .symtab Symbol table符号表 .shstrtab section string table 段名表 .plt & .got 动态链接调转表和全局入口表 .init & .fini 初始化代码段和终结代码段

D.说ELF文件结构

文件头:

这里写图片描述
第一个02:64位
第一个01:小端
第二个01:ELF版本号

**文件头结构**typedef struct{      unsigned char e_ident[16];      Elf32_Half e_type;      Elf32_Half e_machine;      Elf32_Word e_version;      Elf32_Addr e_entry;      Elf32_Off e_phoff;      Elf32_Off e_shoff;      Elf32_Word e_flags;      Elf32_Half e_ephsize;      Elf32_Half e_phentsize;      Elf32_Half e_phnum;      Elf32_Half e_shentsize;      Elf32_Half e_shnum;      Elf32_Half e_shstrndex;  } Elf32_Ehdr;  

成员 readelf输出结果与含义
e_ident Magic:7f 454c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2’s complement, little endian
Version: 1 (current)
OS/ABI: UNIX-System V
ABI Version: 0
e_type Type: EXEC (executable file)
ELF文件类型
e_machine Machine: Intel 80386
ELF文件的CPU平台属性
e_version Version:0x1
ELF版本号
一般为常数1
e_entry Entry point address: 0x8048320
入口地址
规定ELF程序的入口虚拟地址,操作系统在加载完成该程序后,从这个地址开始执行进程的指令。可重定位文件一般没有入口地址,则这个值为0
e_phoff Start of program headers: 52(bytes into file)
程序头表的偏移。参考下文的“连接视图和执行视图”
e_shoff Start of section headers: 4472(bytes into file)
段表在文件中的偏移,也就是从文件的4473个字节开始是段表内容
e_word Flags: 0x0
ELF标志位
用来标志一些ELF文件平台相关的属性。
e_ehsize Size of this header: 52(bytes)
ELF文件头本身的大小
e_phentsize Size of program headers: 32(bytes)
程序头大小
e_phnum Number of program headers:9
在执行视图中,Segments的数量
e_shentsize Size of section headers:40(bytes)
段表描述符大小
e_shnum Number of section headers:30
段表描述符的数量
这个值等于ELF文件中拥有的段(section)的数量。
e_shstrndx Section header string table index:
段表字符串表所在的段在段表中的下标。

段表

我的:

这里写图片描述

这里写图片描述

这里写图片描述

符号(链接的接口)

符号:函数和变量
符号名:函数名和变量名

类别区分 举例 定义在本身目标文件中的全局符号 global_var1 本身目标文件引用的,来自别人的全局符号 printf 段名符号 .test 局部符号 s_var1 行号信息 … 等等 …

ELF符号表结构体

typedef struct {Elf32_Word st_name; //该符号名在字符串表中的下标。Elf32_Addr st_value; //符号相对应的值。Elf32_Word st_size; //符号大小。对于包含数据的符号,这个值是该数据类型的大小unsigned char st_info; //符号类型和绑定信息。unsigned char st_other; //该成员目前为0,没用。Elf32_Half st_shndx; //符号所在的段。}Elf32_Sym;st_name符号名。这个成员包含了该符号名在字符串表中的下标(还记得字符串表吧?)st_value符号相对应的值。这个值跟符号有关,可能是一个绝对值,也可能是一个地址等,不同的符号,它所对应的值含义不同,见下文“符号值”st_size符号大小。对于包含数据的符号,这个值是该数据类型的大小。比如一个double型的符号它占用8个字节。如果该值为0,则表示该符号大小为0或未知st_info符号类型和绑定信息,见下文“符号类型与绑定信息”st_other该成员目前为0,没用st_shndx符号所在的段,见下文“符号所在段”

几个特殊符号

_executable_start 程序起始地址
_etext 代码段结束地址
edata 数据段结束地址
_end 程序结束地址

E.强弱符号和符号多次定义

出现原因
一个符号可能被多次重复定义。那么编译时候需要归并这些相同的符号。

强弱符号规则
1.不允许强符号多次被定义。
2.一个符号在a文件是强符号,b文件是弱符号,则选择强符号
3.都是弱符号,以占用空间最大的那个为准。

That is all.

0 0
原创粉丝点击