可执行文件和可链接格式(.elf)与对象(.o)文件之间的区别

时间:2018-11-08 14:55:03

标签: c elf object-files

我在Linux(man gcc)上的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文件(如果我在目标文件上使用了它们)?

2 个答案:

答案 0 :(得分:1)

让我们举一个简单的例子。您有三个文件:

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文件则包含完整的程序。

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

答案 1 :(得分:0)

  • 两个文件之间的实际区别是什么,或者如果我有多个源文件,该区别是什么?

.o文件包含一个来源(编译单元)的已编译代码,但尚未准备好运行:它可以包含对库或其他目标文件中外部符号的引用。

  • 要运行的正确文件是什么,以及如何生成它?

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

  • 我是否需要目标文件,或者它们只是中间文件?

是的,您需要它们才能链接它们,但它们是中间文件。

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

某些标志“持久”是指它们确定.o文件,但不是全部。 -Wall仅在编译期间发出警告,-Os指定某种优化类型,这将导致.o文件对执行的代码进行某种优化。