链接器如何确定代码执行的起始位置? [嵌入式]

时间:2015-10-09 19:32:45

标签: c++ c linker embedded

作为嵌入式C编程的初学者,我很好奇每个(我的经验)程序执行是如何从main()函数开始的?它就像链接器识别main()并将“特殊”函数的地址放入重置向量指向的地址。

6 个答案:

答案 0 :(得分:5)

通常,链接描述文件会创建一个特殊的部分,该部分映射到复位向量,并包含对C启动代码的跳转/转到指令,而C启动代码又调用main()。

答案 1 :(得分:5)

C定义了将在“托管”环境中运行的代码的不同规范以及将在“独立”环境中运行的代码。大多数程序员都会在不必处理独立环境的情况下完成整个职业生涯,但大多数例外都是使用嵌入式编程,内核编程,引导加载程序和其他在裸机上运行的软件。

在托管环境中,C指定程序执行以调用main()开始。这并不排除系统在该调用之前执行的初步设置,但这超出了规范的范围。 C编译器和/或链接器负责安排发生这种情况;细节是依赖于实现的。

另一方面,在独立实现中,程序入口点以实现选择的方式确定。可能没有main()函数,如果有函数,则其签名不需要与托管环境中运行的程序允许的签名相匹配。

答案 2 :(得分:2)

它不是链接器,它是处理器决定的。上电时,指令指针被设置为预定义的存储器地址,通常与复位中断向量相同。然后链接器通过将分支指令放在该地址的启动代码中来启动。

答案 3 :(得分:2)

链接器链接模块以进行处理器和运行时环境初始化。该模块从复位向量输入。在gcc工具链中,模块通常称为crt0.o,它是从源代码crt0.s(汇编代码)构建的。您的工具链可能会有所不同,但某些启动代码将被链接,并且源应该可用于自定义。

启动代码通常会执行硬件初始化,例如将PLL配置为所需的时钟速度,以及在使用外部存储器时初始化存储器控制器。 C运行时初始化需要设置堆栈指针,初始化全局静态数据,以及可能的运行时库初始化 - 例如堆和stdio初始化。对于C ++,它还调用任何全局静态对象的构造函数。最后调用main()

请注意,并非具体了解main()的链接器;这只是运行时启动模块中未解析的链接。如果您的程序没有main(),则无法链接。

您当然可以修改启动代码以使用main()以外的其他符号,但main()由语言标准定义为入口点。

某些应用程序框架或环境似乎没有main();例如,在RTOS VxWorks中,应用程序从usrAppInit()开始,但实际上这只是因为main()在VxWorks库中定义。

链接器根据程序集源中的指令或链接描述文件中的指令定位启动代码;工具链可能有所不同。

在ARM Cortex-M设备上,初始堆栈指针在向量表中定义并自动加载;因此,这些设备可以直接从reset运行C代码(虽然在一个有限的环境中),并允许大部分运行时环境初始化用C而不是汇编程序编写。

答案 4 :(得分:1)

每个处理器和工具链都不同。但是,一般情况下,它们的设置位置是从重置向量到达运行时库的入口点(很多次_start)。运行时库准备处理器状态,清除.bss内存,初始化.data内存,可能设置堆,并调用一些调用以允许自定义启动,然后调用所有全局构造函数(如果是c ++),最后跳到main()。

这是硬件需求,工具链假设,运行时库和系统代码的混合。你可以修剪很多,因为C的唯一真正要求是你有一个堆栈。其余的是您可能使用或不使用的库代码。

答案 5 :(得分:0)

为了满足程序员的标准或至少期望,在主要需要bss清除之前,编译时初始化变量(例如全局变量=某事物),c库和其他有趣的东西。所以你有这个鸡和蛋的问题,你怎么能有这样的假设或要求的C代码,并有C代码满足这些要求。你不。还有其他代码,并非常见的汇编,但可能来自C,其中假设是不正确的。有时称为bootstrap代码。无论这是嵌入式系统还是在操作系统上运行的应用程序都无关紧要。在那个"程序"中的第一个指令之间有一些粘合剂。主要的。如果你反编译gnu工具创建的东西,你可以在名为_start和main的标签之间看到这条执行路径。其他工具链可能会或可能不会以不同的方式命名其入口点。

在微控制器或情况下你可能是裸机(PC上的BIOS,启动rtos / os的启动代码),如果你不关心C的一些要求/假设,加载堆栈指针和分支到main是你需要的。清除bss并将.data从flash复制到ram中的正确主页,接下来需要接近C语言要求的两件事,你会发现这些是你在某些嵌入式系统中获得的所有步骤。

也可能是其他处理器,但是arm cortex-m硬件能够加载堆栈指针并分支到一个地址(复位总是分支到一个地址或从某个已知地址运行代码),进一步中断系统保存状态为了你所以你不需要在用C编写的中断服务程序周围包装asm(或做一些编译器特定的声明做同样的事情)(这是你需要问的下一个问题,1)重置为C代码2)中断到C代码),因此中断向量表可以直接具有C函数的地址。该产品系列的一个很好的功能。

使用工具链反汇编程序并检查从入口点到main()的代码...过去肯定会有一些工具链,当它专门看到main()并添加额外代码时会产生异常。所以有时你会看到一些其他的C函数名用作第一个C函数,以避免工具链在其他东西中链接。

虽然链接器只是寻找未解决的符号,一个是主要的,一个是gnu工具链,另一个是_start,Clifford在头上敲了一下钉子。它链接了它已经知道的东西或者你在命令行上提供的东西,直到所有标签都被解析。