当我们编译包含' main'的源代码时没有链接,我们为什么不能运行它?

时间:2017-03-01 20:04:46

标签: c++ c ubuntu gcc compilation

我正在学习编译过程,我知道链接主要用于链接包含' main'的二进制文件。与其他二进制文件一起使用,其中包含在我们的主函数中使用的其他辅助函数。

但是,当我尝试使用代码运行目标文件时:

int main() {
    return 0;
}

在Ubuntu的gcc中使用-c命令编译,我尝试运行它并得到错误:

  

" bash:./ source.o:无法执行二进制文件:执行格式错误"

3 个答案:

答案 0 :(得分:6)

阅读Levine的Linkers & Loaders

了解ELF

尝试使用gcc -v进行编译(您将看到使用的实际程序是什么:cc1compile C代码到一些汇编程序,as到{{ 3}}进入某个目标文件,assemble& ldcollect2)。另请查看生成的汇编程序文件gcc -S -fverbose-asm -O。请注意gcc知道{{1>}函数(并编译特别main函数。可执行文件的起点由一些link等提供(它是不是 main,但是一些_start例程在汇编程序中编码,调用您的{{ 1}} ....)。

crt0Object files不同。可执行文件包含executablescrt0之类的内容,或某种方式C standard library dynamically link(并且您需要链接main编译的shared objectsource.o中的空main - 因此而变为可执行文件。

在Linux上,使用objdump(1)& readelf(1)(在一些现有的二进制文件上,也在您的source.c目标文件上)

另请参阅elf(5)execve(2)ld-linux(8)Linux assembly howtosyscalls(2)Advanced Linux ProgrammingOperating Systems: Three Easy Pieces和(至)了解source.o)Drepper的How To Write Shared LibrariesDragon Book ......

(你需要阅读整本书才能理解细节;我提供了一些参考资料)

另请参阅Common Lisp& SBCL。它的编译器有一个非常不同的模型(与C完全不同)。

答案 1 :(得分:3)

你没有引导程序。你是在这个鸡和蛋的问题。

代码(用于该函数)就在那里,但有一些假设,首先你需要一个堆栈。例如,根据架构,您的返回地址可能位于该堆栈上。返回值可以在该堆栈上。 C语言本身并不直接在语言中提供,因此总是至少需要一些程序集或一些其他语言才能“引导”你的函数。例如在ARM中用于gnu:

bs.s

.globl _start
_start:
    mov sp,#0x8000
    bl main
    b .

so.c

int main ( void )
{
    return(0);
}

对于ARM,函数完成后,链接器不需要修改指令。但是没有定义地址空间,无论是指定的还是反汇编程序都假定零作为此对象的地址,但它是一个对象而不是可加载的二进制文件。

00000000 <main>:
   0:   e3a00000    mov r0, #0
   4:   e12fff1e    bx  lr

现在,如果我们添加引导程序并链接到某个地址,我们将获得一个真实的,可执行的程序

00008000 <_start>:
    8000:   e3a0d902    mov sp, #32768  ; 0x8000
    8004:   eb000000    bl  800c <main>
    8008:   eafffffe    b   8008 <_start+0x8>

0000800c <main>:
    800c:   e3a00000    mov r0, #0
    8010:   e12fff1e    bx  lr

这并不意味着无法创建操作系统,也无法使用编译器对象输出以这种方式加载函数。但这就是单词链,工具链的原因。编译器使用汇编语言,汇编程序汇编汇编语言,结合其他必要的对象(引导程序加编译器库和C库等),链接器为所有内容定义地址空间,并根据需要修改代码/数据以解析外部。获得最终结果的序列或事件链。

答案 2 :(得分:-2)

即使是最基本的命令,例如exit,也不是直接使用语言,而是需要链接。

http://en.cppreference.com/w/c/program/exit