https://cloud.tencent.com/developer/article/1446849
目标文件是源代码编译后未进行链接的中间文件(Windows的.obj和Linux的.o),与可执行文件(Windows的.exe和Linux的ELF)的结构和内容相似,因此跟可执行文件采用同一种格式存储。PC平台常见的可执行文件格式主要有Windows的PE(Portable Executable)和Linux的ELF(Executable and Linkable Format)。PE和ELF都是通用目标文件格式(COFF,Common Object File Format)的变种。在Windows下,我们将目标文件与可执行文件统称为PE-COFF文件,Linux统称为ELF文件。除此之外,还有些不常用的目标文件与可执行文件格式,比如Intel和Microsoft以前使用的对象模型文件(OMF,Object Module File)、Unix的最初使用的a.out和MS-DOS的.COM格式等。
不光是可执行文件按照可执行文件格式存储,动态链接库(DLL,Dynamic Linking Library)(Windows的.dll和Linux的.so)及静态链接库(Static Linking Library)(Windows的.lib和Linux的.a)都按照可执行文件格式存储。它们在Windows下都按照PE-COFF格式存储,Linux下按照ELF格式存储。静态链接库稍有不同,它是把所有目标文件打包成一个文件,再加上一些索引,可以简单理解为一个包含很多目标文件的文件包。
Linux下的ELF文件主要有如下几种:
ELF文件类型 |
说明 |
实例 |
---|---|---|
可重定位文件(Relocatable File) |
包含了代码与数据,可以用来连接成可执行文件或共享目标文件,如目标文件与静态链接库 |
Linux的.o与.a,Windows的.obj与.lib |
共享目标文件(Shared Object File) |
包含了代码和数据,主要有两种用途,一是与目标文件或其它共享目标文件链接成新的共享目标文件,二是与可执行文件结合,作为进程映像的一部分来运行 |
Linux的.a,Windows的.dll |
可执行文件(Executable File) |
包含了可直接执行的程序 |
Linux下无后缀的ELF可执行文件,Windows的.exe文件 |
核心转储文件(Core Dump File) |
当进程意外终止时,系统可以将该进程的地址空间的内容及终止时的一些其他信息转储到核心转储文件 |
Linux下的core dump |
Linux下可以根据file命令查看上面表格中列出的四种ELF文件的格式。 (1)目标文件.o。 编译如下代码生成目标文件。
//@file:foo.cpp
#include <iostream>
using namespace std;
int foo()
{
cout<<"hello world"<<endl;
}
编译生成目标文件foo.o:
g++ -c foo.cpp -o foo.o
使用file命令查看foo.o文件类型:
file foo.o
foo.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
(2)共享目标文件.so,以C++标准库/lib64/libstdc++.so.6.0.19为例。
file /lib64/libstdc++.so.6.0.19
/lib64/libstdc++.so.6.0.19: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, BuildID[sha1]=8941888bf8ee9ced585599be5397a385fc1c73ce, stripped
(3)可执行文件,以GNU的Shell /usr/bin/bash为例。
file /usr/bin/bash
/usr/bin/bash: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=d0c7bc3186c85673fb2b14c90ab92eeaa27a18a5, stripped
(4)核心转储文件core dump。 编译如下代码生成可执行文件a.out,运行a.out访问非法地址NULL后生成core文件。
//@file:main.cpp
#include <iostream>
using namespace std;
int main()
{
int* iBar = NULL;
cout<<"*iBar"<<*iBar<<endl;
}
编译并运行:
g++ main.cpp
./a.out
Segmentation fault (core dumped)
使用file命令查看core文件类型:
file core.28355
core.28355: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style, from './a.out'
可见,Linux下的目标文件.o,共享目标文件.so、可执行文件以及核心转储文件core dump均属于ELF文件。
目标文件与可执行文件的格式和操作系统和编译器密切相关,不同的系统平台下会有不同的格式,但是这些格式又大同小异,可以说,目标文件与可执行文件格式的历史几乎是操作系统的发展史。
COFF是由Unix System V Release 3首次提出并使用的格式规范,后来Microsoft在其基础上,制定了PE格式标准,并将其应用于自家的Windows NT系统。后台,System V Release 4 在 COFF的基础上引入了ELF格式,目前流行的Linux系统也是以ELF作为基本的可执行文件格式。这也是为什么目前PE和ELF如此相似的原因,因为它们都是源于同一种可执行文件格式COFF。
在COFF之前,Unix最早的可执行文件格式是a.out格式,中文意为汇编器输出。因其设计简单,以至于后来共享库出现的时候,a.out格式变得捉襟见肘,难以满足共享库实现的要求,于是从Unix System V Release 3开始被COFF取代。由于COFF格式的设计非常通用,以至于COFF的继承者PE和ELF目前还在被广泛地使用。COFF的主要贡献是在目标文件中引入了“段”的机制,不同的目标文件可以拥有不同数量及不同类型的段。另外,还定义了调试数据的格式。
[1]俞甲子,石凡.程序员的自我修养——链接、装载与库[M].北京:电子工业出版社,2009-04.C3.1目标文件格式.P56-58 [2]wikipedia.COFF [3]wikipedia.a.out