程序的链接(二):ELF目标文件

在学习链接的具体过程前,有必要好好了解一下ELF目标文件。

ELF的目标文件分为三类:

  • 可重定位目标文件(.o)
  • 其代码和数据可和其他可重定位文件合并为可执行文件
  • 每个 .o 文件由对应的 .c 文件生成
  • 每个 .o 文件的代码和数据地址都是从0开始的偏移
  • 可执行目标文件(默认为a.out)
  • 包含的代码和数据可以被直接复制到内存并执行
  • 代码和数据的地址是虚拟地址空间中的地址
  • 共享的目标文件(.so 共享库)
  • 特殊的可重定位目标文件,能在装载到内存或运行时自动被链接,称为共享库文件

可通过objdump命令对比可重定位目标文件和可执行目标文件的不同:

image.png

可以看到,确实可重定位目标文件中的地址是从0开始的,而可执行目标文件中的地址是虚拟地址空间中的地址。


接下来介绍ELF文件的两种视图:

  • 链接视图:可重定位文件(Relocatable object files)
  • 执行视图:可执行目标文件(Executable object files)


    image.png

链接视图 —— 可重定位目标文件

来看一个简单的C代码及其所生成的可重定位目标文件的关系图:


image.png

如上图,编译后的代码部分放到 .text节,已初始化的全局变量和已初始化的静态变量会放到 .data节,未初始化的全局变量和未初始化的静态变量会放到 .bss节。
实际上,为了进行链接,可重定位目标文件还需要许多其他信息,如符号表、重定位信息等。这些后面会陆续介绍。

在这里要特别说明一下 .bss节。该节在可重定位目标文件中并不占用空间,只是在节头表相应的表项中说明要为 .bss节预留多大的空间。

可重定位目标文件中包含有很多的节,格式如下图:


image.png

其中:

  • ELF头

包括16字节的标识信息、文件类型(.o,exec,.so)、机器类型(如Intel 80386)、节头表的偏移、节头表的表项大小及表项个数。

  • .text节

编译后的代码部分。

  • .rodata节

只读数据,如 printf用到的格式串、switch跳转表等。

  • .data节

已初始化的全局变量和静态变量。

  • .bss节

未初始化全局变量和静态变量,仅是占位符,不占据任何磁盘空间。区分初始化和非初始化是为了空间效率。

  • .symtab节

存放函数和全局变量(符号表)的信息,它不包括局部变量。

  • .rel.text节

.text节的重定位信息,用于重新修改代码段的指令中的地址信息。

  • .rel.data节

.data节的重定位信息,用于对被模块使用或定义的全局变量进行重定位的信息。

  • .debug节

调试用的符号表(gcc -g)

  • .strtab节

包含 .symtab节和 .debug节中的符号及节名

  • 节头表(Section header table)

包含每个节的节名在.strtab节中的偏移、节的偏移和节的大小.

下边分别举例讲一下ELF头节头表

-------------------------------------- ELF头 (ELF Header)------------------------------
ELF头位于ELF文件的开始,其包含了文件结构的说明信息。其结构体定义如下:

#define EI_NIDENT 16

typedef struct {
    unsigned char e_ident[EI_NIDENT];              
    uint16_t      e_type;
    uint16_t      e_machine;
    uint32_t      e_version;
    ElfN_Addr    e_entry;
    ElfN_Off      e_phoff;
    ElfN_Off      e_shoff;
    uint32_t      e_flags;
    uint16_t      e_ehsize;
    uint16_t      e_phentsize;
    uint16_t      e_phnum;
    uint16_t      e_shentsize;
    uint16_t      e_shnum;
    uint16_t      e_shstrndx;
} ElfN_Ehdr;

用readelf -h 查看ELF头。下表是ELF头中各个成员的含义与readelf输出结果的对照表:

成员 readelf输出结果及含义
e_ident Magic: 7f 45 4c 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: REL(Relocatable file)
ELF文件类型
e_machine Machine: Intel 80386
ELF文件的CPU平台属性,相关常量以EM_开头
e_version Version: 0x1
ELF版本号。一般为常数1
e_entry Entry point address: 0x0
入口地址,规定ELF程序的入口虚拟地址,操作系统在加载完该程序后从这个地址开始执行进程的指令。可重定位目标文件一般没有入口地址,则这个值为0
e_phoff Start of program header: 0 (bytes into file)
程序头表在文件中的偏移,可重定位目标文件不存在程序头表,故该值为0
e_shoff Start of section header: 280(bytes into file)
节头表在文件中的偏移
e_flags Flags: 0x0
ELF标志位,用来标识一些ELF文件平台相关的属性,相关常量的格式一般为EF_machine_flag,machine为平台,flag为标志
e_ehsize Size of this header: 52(bytes)
即ELF文件头本身的大小
e_phentsize Size of program headers: 0(bytes)
程序头表表项的大小
e_phnum Number of program headers: 0
程序头表表项的数目
e_shentsize Size of section headers: 40(bytes)
节表表项的大小
e_shnum Number of section headers: 11
节表表项的数目
e_shstrndx Section header string table index: 8
节表字符串表在节头表中的下标

-------------------------------------- 节头表(Section Header Table) ----------------------
除了ELF头之外,节头表是ELF可重定位目标文件中最重要的部分内容。
它描述了每个节的节名、在文件中的偏移、大小、访问属性、对齐方式等。其32位结构定义如下(每项占40字节):

typedef struct {
    Elf32_Word sh_name;   //节名字符串在.strtab中的偏移
    Elf32_Word sh_type;   //节类型:无效/代码或数据/符号/字符串/…
    Elf32_Word sh_flags;  //节标志:该节在虚拟空间中的访问属性
    Elf32_Addr sh_addr;   //虚拟地址:若可被加载,则对应虚拟地址
    Elf32_Off sh_offset;  //在文件中的偏移地址,对.bss节而言则无意义
    Elf32_Word sh_size;   //节在文件中所占的长度
    Elf32_Word sh_link;   //sh_link和sh_info用于与链接相关的节(如
    Elf32_Word sh_info;   //     .rel.text节、.rel.data节、.symtab节等)   
    Elf32_Word sh_addralign; //节的对齐要求
    Elf32_Word sh_entsize;   //节中每个表项的长度,0表示无固定长度表项
} Elf32_Shdr;

使用 readelf -S命令查看节头表,示例如下:


image.png

image.png

A(alloc)标志表示该节将进程空间中必须要分配空间。可以看到,.text、.data、.bss、.rodata节都有这个标志。

执行视图 —— 可执行目标文件

再来看ELF的执行视图,也就是可执行目标文件的格式:


image.png

它与可重定位目标文件稍有不同:

  1. ELF文件头中的字段e_entry给出了执行程序时第一条指令的地址,而在可重定位目标文件中,此字段为0;程序头表的偏移e_poff和大小e_phentsiz和程序头表项的个数e_phnum不为0;
  2. 多了一个程序头表,也称为段头表(segment header table),是一个结构体数组;
  3. 多了一个 .init节,用于定义 _init函数,该函数用于在可执行目标文件开始执行时的初始化工作。
  4. 少了两个 .rel节(.rel.text和.rel.data),因为可执行目标文件已经在链接的过程中完成了重定位,已无须重定位。

使用readelf -h 来看可执行目标文件的ELF头,示例如下


image.png

程序头表描述的可执行文件中的节(section)与虚拟地址空间中的存储段(segment)之间的映射关系。

程序头表的结构定义如下:

typedef struct {
    Elf32_Word p_type;      //段类型
    Elf32_Off p_offset;        //该段在文件中的起始偏移
    Elf32_Addr p_vaddr;     //该段的虚拟地址
    Elf32_Addr p_paddr;     //该段的物理地址
    Elf32_Word p_filesz;     //该段在文件中的大小
    Elf32_Word p_memsz;  //该段在内存中的大小
    Elf32_Word p_flags;     //段的标志位,表示访问权限(Read|Write|Exec)
    Elf32_Word p_align;     //段在内存中的对齐要求
} Elf32_Phdr;

下图是用readelf -l 查看到的某可执行目标文件的程序头表:


image.png

Type为Load表示可装入内存的段。
第一个可装入段:第0x00000~0x004d3字节(包括ELF头、程序头表、.init节、.text节和.rodata节),映射到虚拟地址0x8048000开始长度为0x4d4字节的区域,按0x1000=4KB对齐,具有只读/执行权限(Flg=RE),是只读代码段。
第二个可装入段:第0x000f0c~开始长度为0x108字节的 .data节,映射到虚拟地址0x8049f0c开始长度为0x110字节的存储区域,在0x110=272B存储区中,前0x108=264B用 .data节内容初始化,后面272-264=8B对应.bss节,初始化为0,按0x1000=64K对齐,具有可读可写权限(Flg=RW),是可读写数据段。

映射到虚拟地址空间中如图:


image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,039评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,426评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,417评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,868评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,892评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,692评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,416评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,326评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,782评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,957评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,102评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,790评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,442评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,996评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,113评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,332评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,044评论 2 355

推荐阅读更多精彩内容