欢迎访问我的个人博客: luomuxiaoxiao.com
上篇文章我们从整体上介绍了从C文件到可执行文件的编译过程,并逐个分析了单步编译时生成的中间文件的类型。为了搞清楚编译和链接过程中主要做了哪些工作, 我们应该首先明白编译前后,链接前后文件内容的改变。根据上篇文章内容, 编译前的文件格式是
汇编文件
,编译后的文件是可重定位文件
。汇编文件就是简单的文本文件,而可重定位文件是一个ELF格式的二进制文件。因此,本章我们将从ELF文件格式入手分析
可重定位文件
的结构。
下面我们以C语言中经典的“hello, world”的产生的可重定位文件为例来说明。使用的环境如下:
- ubuntu 64位操作系统
- gcc作为编译工具
(版本信息:gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.10))
一、生成中间文件
我们依然使用计算机原理系列之三 -------- 如何编译目标文件中的hello.c文件来研究,使用下列命令生成hello.o
:
gcc -E hello.c -o hello.i
gcc -S hello.i -o hello.s
gcc -c hello.s -o hello.o
二、可重定位文件分析
2.1 解析文件头,说明文件构成
由于可重定位文件类型是ELF格式(关于ELF文件的知识,请阅读详解ELF文件),我们可以使用READELF
命令查看其构成。
$ readelf -h hello.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: 672 (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: 10
其中,我们关注以下字段:
Type: REL (Relocatable file)
: 可以看出.o
文件的类型为可重定位文件
;Number of program headers: 0
:可以看出可重定位文件
的program header table
的长度为0。这是因为program header table
保存的是segment
信息,而segment
是为了给加载器提供可执行程序在加载时所需的信息的,又因为可重定位文件本身并不能直接执行,因此在可重定位文件里不需要program header table
;Entry point address: 0x0
: 同上,由于可重定位文件不能直接执行,因此其入口地址为0(默认值);*** of section headers:***
:从ELF文件起始地址偏移672
个字节处是section header table
的起始地址,section header table
中共有13
项,每项的大小为64 byte
;Size of this header: 64
: ELF文件头大小为64 byte
。
2.2 分析ELF文件各部分
可重定位文件属于二进制文件。在linux机器上,可以使用hexdump
命令来查看二进制文件的内容。
$ hexdump -C hello.o
00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF............|
00000010 01 00 3e 00 01 00 00 00 00 00 00 00 00 00 00 00 |..>.............|
00000020 00 00 00 00 00 00 00 00 a0 02 00 00 00 00 00 00 |................|
00000030 00 00 00 00 40 00 00 00 00 00 40 00 0d 00 0a 00 |....@.....@.....|
00000040 55 48 89 e5 bf 00 00 00 00 e8 00 00 00 00 b8 00 |UH..............|
00000050 00 00 00 5d c3 68 65 6c 6c 6f 2c 20 77 6f 72 6c |...].hello, worl|
... snip ...
这实际上就是hello.o
文件的实际内容。
注:
- 最前面一列是hexdump命令添加的,并非ELF文件的内容。它是一个十六进制的数字,表示字节序号。例如,
00000040 55 48 89 e5 bf 00 00 00 00 e8 00 00 00 00 b8 00
,其中0000040
即0x40
,十进制为64
。表示hello.o
文件的第64
个字节是‘55’
。- 最后一部分由两个
‘|’
包含的数字和字符也是hexdump命令添加的,它将其左侧的十六进制数字转化成了对应的ASCII字符,所有的控制字符表示为‘.’
,所有的可显示字符表示为对应的字符或图形。
2.2.1 ELF header
根据ELF文件的结构,ELF文件最开始的部分是ELF header
,它是一个64字节大小的结构体,也就是对应了hello.o
的前64个字符,即从0000000 - 00000040
的部分。前十六个字节应该是对应其magic number
的部分。我们注意到,从0000000 - 0000000F
正好就是使用readelf读出来的magic的值。剩下的部分只要结合struct ElfN_Ehdr
的成员信息和hexdump
命令输出的内容即可一一验证。
想第一时间查看我的文章吗?请关注我的微信公众号号,搜索“落木萧萧技术论坛”或登陆我的个人博客:www.luomuxiaoxiao.com,更多精彩文章等你。
![qrcode](https://i-blog.csdnimg.cn/blog_migrate/18761891c55b2bfa97548506179f7601.jpeg)