ELF格式中的可执行文件和可重定位文件有何区别?
正如您所知道的,每个编译的可执行文件都是一个具有相对和绝对地址的二进制文件,因此重新定位格式是一种格式,在该格式中,函数和其他符号仍然具有它们在其他单词中的名称定义,换句话说,函数和变量不绑定到任何特定的地址。相反,这些地址仍然是符号。
看:
unix > gcc -c main.c
unix > readelf --symbols main.o
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FILE LOCAL DEFAULT ABS main.c
2: 00000000 0 OBJECT GLOBAL DEFAULT 3 buf
3: 00000000 0 OBJECT GLOBAL DEFAULT 1 main
unix > gcc main.c -o main
unix > readelf --symbols main
Num: Value Size Type Bind Vis Ndx Name
53: 08048460 2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
54: 08048462 0 FUNC GLOBAL HIDDEN 13 __i686.get_pc_thunk.bx
55: 0804a018 4 OBJECT GLOBAL DEFAULT 13 bufp0
你明白我在说什么吗?
大多数情况下,我们将它们用作静态库。
看这里的例子:
#ifndef MATH_H
#define MATH_H
int add(int a, int b);
#endif
/* math_test.c */
#include <stdio.h>
#include "math.h"
int main(void)
{
int result = add(1, 2);
printf("result: %d\n", result);
return 0;
}
尝试编译它
unix > gcc math_test.c -o math_test
/tmp/cclRibQq.o: In function `main':
math_test.c:(.text+0x19): undefined reference to `add'
collect2: ld returned 1 exit status
由于 math_test.c
中的函数 add
没有函数体,我们可以这样做:
that due to function add
has no body in math_test.c
so we can do the flowing:
由于 math_test.c
中的函数 add
没有函数体,我们可以这样做:
int add(int a, int b)
{
return a + b;
}
然后使用gcc将其编译为可重定位文件
unix > gcc -c math.c # Create relocatable obj file (math.o)
unix > readelf -h math.o | grep Type # Read math.o with readelf
Type: REL (Relocatable file) # and verify its type
那么你可以像这样使用连接器将它链接:
unix > gcc math_test.c math.o -o math_test
unix > ./math_test
result: 3
您可以在这里找到一篇关于ELF格式可执行文件和可重定位文件之间区别的好文章。
如下图所示,可重定位的ELF文件是链接器的输入,而可执行的ELF文件则是链接器的输出。
可重定位文件没有任何加载地址,它只是具有偏移量的二进制代码序列(例如与主函数相关的偏移量)。但是,可执行文件具有加载地址,而不仅仅是与任何函数相关的偏移量。
它们之间的另一个基本区别是可执行文件具有引导应用程序,而可重定位文件则没有。
ELF可执行文件,从其名称中可以理解,是一个可执行的文件。例如,这个文件可以从C代码生成。
重定位的过程是修复在代码中创建的标签和符号的地址。例如,如果你使用汇编语言编写程序,并查看源代码清单文件,你会发现某些地方写有[00000000],而不是这行提到的标签。这些零意味着链接器使用重定位来修复地址到其未来的值。