可执行文件和可链接文件(.elf)与目标文件(.o)之间的区别

6
我在Linux的gcc手册(man gcc)中查找-c选项(gcc -c infile),该选项说明如下:

-c:编译或汇编源文件,但不进行链接。不执行链接阶段。每个源文件的最终输出形式是一个目标文件。
默认情况下,源文件的目标文件名是通过将后缀.c、.i、.s等替换为.o而生成的。

此外,在检查ELF文件和目标文件(用file命令)时,输出结果相同:
file ./out/main.o: ELF 32-bit LSB relocatable, Atmel AVR 8-bit, version 1 (SYSV), not stripped
file ./out/main.elf: ELF 32-bit LSB executable, Atmel AVR 8-bit, version 1 (SYSV), statically linked, not stripped

因此,它们都具有相同的描述。

  • 两个文件之间的实际区别是什么?如果我有多个源文件呢?
  • 正确的文件是哪一个,如何生成它?
  • 我需要对象文件吗,还是它们只是中间文件?
  • 如果我使用-c选项和一些标志(-Wall -g -std=c99 -Os)编译一些源文件并从中获取对象文件,那么这些标志是否在生成ELF文件时保留(如果我在对象文件上使用了它们,那么在生成ELF文件时是否可以跳过这些标志)?

不同的描述:一个是“可重定位”的,另一个是“可执行”的。 - rodrigo
哪一个是可重定位的,哪一个是可执行的? - Upgrade
file 输出中可见:.o 文件是 ELF 32-bit LSB 可重定位 的。 - rodrigo
哦,谢谢你的提示,我太糊涂了 :) - Upgrade
2个回答

10
让我们举一个简单的例子。你有三个文件: cnt.h
void inc_counter();
void print_counter();

cnt.c

#include <stdio.h>
#include <cnt.h>

static int counter= 0;

void inc_counter() {
    couner++;
}

void print_counter() {
    printf("Counter: %d\n", counter);
}

main.c

#include <counter.h>

int main(char** args) {
    inc_counter();
    print_counter();
    return 0;
}

您可以先编译 cnt.cmain.c 文件,生成 cnt.omain.o 文件。
  • cnt.o 文件包含用于执行 get_counterinc_counter 函数的可执行代码。每个函数都有一个入口点。但是该代码无法执行。由于还不知道 printf 的地址,因此调用 printf 将无法工作。因此文件包含的信息需要在以后修复。
  • main.o 文件包含用于执行 main 函数和其入口点的可执行代码。同样,对于 inc_counterprint_counter 的引用将无法工作。

第二步,将链接文件 cnt.omain.o 和标准 C 库,并创建一个可执行输出文件(带有 .elf 扩展名或没有扩展名)。链接器将创建调用 inc_counter 函数和实际的 inc_counter 函数之间缺失的链接。它也会为 print_counterprintf 创建相同的链接,从而包括来自标准库的 printf 代码。

因此,虽然这两种文件类型主要由可执行代码组成,但 .o 文件仅包含代码片段,而 .elf 文件包含完整的程序。

注意:当您创建或使用动态链接库时,还有其他变体。但是为了简单起见,我已将它们排除在外。


正如我之前所问,你能帮助我或指向一些生成.map文件的文档吗?更重要的是,*.out扩展名的文件是什么? - Upgrade

1
  • 这两个文件有什么实际区别?在我有多个源文件的情况下呢?

.o文件包含了一个源代码(编译单元)的已编译代码,但它还不能直接运行:它可能包含对来自库或其他目标文件的外部符号的引用。

  • 应该运行哪个文件?如何生成它?

那就是可执行文件(Windows上为.exe)。它由链接阶段(由链接器)生成,该阶段搜索库和其他目标文件以解析.o文件中的外部引用。

  • 我需要目标文件吗,还是它们只是中间文件?

是的,你需要它们才能将它们链接起来,但它们只是中间文件。

  • 如果我使用-c选项和一些标志(-Wall -g -std=c99 -Os)编译一些源文件,并从中获取对象文件,那么这些标志是否会保留在ELF文件生成中(如果我在对象文件中使用了它们,可以跳过在生成ELF文件时使用这些标志吗)?

一些标志“持久存在”,因为它们确定了.o文件,但并非全部。 -Wall 仅在编译期间提供警告,-Os 指定某种类型的优化,这将导致具有对代码执行的某些优化的 .o 文件。


Unix 不使用扩展名来表示可执行文件(它使用权限位)。.a是存档文件的扩展名。 - Jens
谢谢您的快速回答,更重要的是,您能告诉我/指导我如何生成.map文件吗?我猜它们是在链接阶段生成的一些辅助文件(当使用链接器时,我是对的吗?) - Upgrade
不,我不能因为我不知道gcc。但是你可以阅读文档。 - Paul Ogilvie
在链接步骤中使用@Upgrade -Xlinker -Map=output.map - Antti Haapala -- Слава Україні
他们将列出符号。 - Antti Haapala -- Слава Україні
最后一个问题。如上所述,有些标志是持久的,我能找到每个标志及其持久性的详细信息吗? - Upgrade

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接