计算机系统篇之链接(2):目标文件
Author:stormQ
Saturday, 21. December 2019 11:08AM
目标文件类型
从技术角度来看,目标文件就是一个字节序列(Technically, object file is a sequence of bytes)。
目标文件可以分为三类:可重定位目标文件、可执行目标文件和可共享目标文件(可共享目标文件是一种特殊的可重定位目标文件)。
三者之间的关系:可重定位目标文件和可共享目标文件是用于生成可执行目标文件的。
目标文件类型 | 生成者 | 是否可以直接被加载到内存中执行 | Linux 中的目标文件后缀(习惯上) |
---|---|---|---|
可重定位目标文件 | 汇编器 | 不可以 | .o |
可执行目标文件 | 链接器 | 可以 | 无后缀 |
可共享目标文件 | 链接器 | 不可以 | .so |
注:对于 Linux 中的目标文件来说,从技术角度讲,文件后缀可以是任意的或无后缀,习惯上的文件后缀只是为了便于区别。
1)如何生成可重定位目标文件(On X86-64 Linux):
$ g++ -c main.cpp -o main.o
# 或 g++ -c main.cpp
# 这两个命令都会生成名称为 main.o 的可重定位目标文件
注:-c
选项表示执行编译和汇编过程,但不执行链接过程(Compile or assemble the source files, but do not link.)。
查看 main.cpp 的内容:
$ cat main.cpp
int main()
{
sum(1, 2);
return 0;
}
2)如何生成可执行目标文件(On X86-64 Linux):
# 默认生成的可执行目标文件为 a.out
$ g++ main.cpp
# 指定生成的可执行目标文件为 main
$ g++ main.cpp -o main
3)如何生成可共享目标文件(On X86-64 Linux):
# 默认生成的可共享目标文件为 a.out
$ g++ -shared sum.cpp
# 指定生成的可共享目标文件为 sum.so
$ g++ -shared sum.cpp -o sum.so
注:-shared
选项表示生成可共享目标文件,默认名称也为 a.out。
注意: 含有main()
函数的源文件不能用于成可共享目标文件,但可以用于生成可重定位目标文件(即 .o 文件)。
查看 sum.cpp 的内容:
$ cat sum.cpp
int sum(int a, int b)
{
return a + b;
}
目标文件格式
目标文件格式是指目标文件的组织方式。目标文件格式因系统而异。
系统 | 目标文件格式 |
---|---|
Windows | PE(Portable Executable) |
Mac OS-X | Mach-O(Mach Object) |
X86-64 Linux 和 Unix | ELF-64(Executable and Linkable Format) |
aarch64 Linux 和 Unix | ELF-64 |
注:aarch64 表示 ARM 64-bit 系统。
1)如何生成目标文件格式为PE
的可执行目标文件(On X86-64 Linux):
$ /usr/bin/x86_64-w64-mingw32-g++ -o main_w64.exe main.cpp
在 Windows 64-bit 系统上的命令行窗口中执行 main_w64.exe 程序:
C:\Users\x\Desktop>main_w64.exe
g_val_1=0x0, g_val_2=0x10
查看 main.cpp 的内容:
$ cat main.cpp
#include <stdio.h>
int g_val_1;
int g_val_2 = 16;
const int g_val_3 = 2;
int main()
{
printf("g_val_1=0x%x, g_val_2=0x%x\n", g_val_1, g_val_2);
return 0;
}
注:要在 X86-64 Linux 系统上生成在 Windows 64-bit 系统上运行的可执行目标文件,需要使用交叉编译器,比如:g++-mingw-w64
(GNU C++ compiler for MinGW-w64)。安装命令为sudo apt-get install g++-mingw-w64
。
2)如何生成目标文件格式为ELF-64
的可重定位目标文件(On X86-64 Linux):
$ g++ -c sum.cpp -o sum.o
查看 sum.cpp 的内容:
$ cat sum.cpp
extern int g_val;
int g_val_1 = 0;
int g_val_2 = 1;
int g_val_3;
int sum(int a, int b)
{
static int val_1;
static int val_2 = 0;
static int val_3 = 1;
static int val_4 = 0;
static int val_5 = 2;
const static int val_6 = 0;
return a + b;
}
注:在 X86-64 Linux 上,g++ 默认的目标文件格式为 ELF-64(对应的缺省编译选项为-m64
)。如果要在 X86-64 Linux 上使用 g++ 生成 ELF-32 的目标文件格式,需要添加-m32
选项,如下所示:
$ g++ -c sum.cpp -o sum_elf32.o -m32
$ readelf -h sum_elf32.o
ELF Header:
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
Type: REL (Relocatable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 740 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 40 (bytes)
Number of section headers: 12
Section header string table index: 9
从上面的打印结果中可以看出,可重定位目标文件 sum_elf32.o 的目标文件格式为 ELF32。
可重定位目标文件(ELF-64 格式)
典型的 ELF-64 可重定位目标文件格式:
组成部分 | 描述 | 如何查看 |
---|---|---|
ELF header | 描述生成该目标文件的系统的字大小和字节顺序、目标文件类型、机器类型等信息。 |
|
.text | 已编译程序的机器代码。 |
|
.rodata | 只读数据,比如:常量字符串、带 const 修饰的全局变量和静态变量等。 | 无 |
.data | 已初始化的且初始值非0的全局变量和静态变量。 | 无 |
.bss | 未初始化的或初始值为0的全局变量和静态变量。 | 无 |
.symtab | 一个符号表,它存放着在目标文件中定义和引用的函数和全局变量、静态变量的信息。 |
|
.rel<name> |
<name> section 的可重定位信息。 |
|
.debug | 一个调试符号表,其条目是程序中定义的局部变量和类型定义(typedefs),程序中定义和引用的全局变量,以及原始的 C 源文件。(只有带 -g 编译时才会产生) |
|
.comment | 版本控制信息。 |
|