在C项目中,.s文件的作用是什么?

25

我正在使用一款ARM Cortex M3芯片(STM32F2),ST公司提供了一份“标准外设库”。它包含了一些有用的 .c 和 .h 文件以及 .s 文件。

在一个C语言项目中,这些 .s 文件的目的是什么?如何让编译器/链接器等使用它们?

4个回答

36

.s扩展名是GNU和许多其他工具链用于汇编文件的约定。

据我所知,STM32标准外设库本身不包含任何汇编文件,但CMSIS库包含各种STM32部件的启动代码,例如startup_stm32f2xx.s是所有STM32F2xx系列设备的启动代码。针对不同的工具链有不同的实现,您需要构建和链接与您特定部件和工具链相关联的文件。如果您正在使用可以构建和运行示例项目或者创建特定于部件的项目的IDE,则可能已经完成了这个步骤 - 如果您有可以运行的代码,那么肯定已经完成了。

如何构建和链接代码取决于您使用的工具链。大多数基于IDE的工具将自动识别扩展名并调用汇编器生成对象文件,该文件将像其他文件一样进行链接。工具链版本之间的确切内容略有不同,但主要创建C运行时环境(堆栈和堆),初始化处理器,定义初始中断/异常向量表,初始化静态数据并跳转到main()。

例如Keil/ARM RealView版本的核心文件如下:

; Reset handler
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
        IMPORT  SystemInit
        IMPORT  __main
                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP
Reset_Handler是处理器重置后程序计数器(PC)寄存器所设置的地址。 SystemInit是一个外部的C代码函数,用于执行大部分的初始化 - 这可能需要根据你的硬件进行自定义。Cortex-M不同寻常的地方在于它可以在复位后立即开始运行C代码,因为向量表包括了复位地址和初始堆栈指针地址,这些会在复位时自动加载到SP寄存器中。因此,你不需要太多的汇编知识来让它运行起来。 __main()是编译器提供的C代码入口点。它不是你编写的main()函数,而是在调用你的`main()'函数之前要执行标准库、静态数据、堆的初始化。
GCC版本有些复杂,因为它执行了Keil/ARM RealView版本中__main()完成的大部分工作,但本质上它执行相同的功能。
请注意,在CMSIS中,SystemInit()在system_stm32f2xx.c中定义,并且可能需要根据你的板子进行自定义(正确的晶振频率、PLL设置、外部SRAM配置等)。由于这是C代码,并且有良好的注释,你可能会更容易理解它。

除了我刚刚注意到你指定了STM32F2xx。答案仍然适用,只是在你的情况下,相应的文件名为startup_stm32f2xx.s和system_stm32f2xx.c。我修改了答案,使其更具体地针对STM32F2。 - Clifford
Clifford - 在ARM网站的文档中提到,startup_xxx.s中的另一个例程__user_initial_stack_heap不应使用超过88字节的堆栈。你知道这个限制是从哪里来的吗?请参见http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.kui0099a/armlib_cihhdahf.htm - NickHalden
@NickHalden:你觉得把这个问题单独发布成一个问题,是不是更合适呢?这个问题已经过去两年了,而且这不是你的问题。这也不是评论区的用途 - SO不是讨论论坛。而且,通过这种方式你会得到更大的受众。 - Clifford
@Clifford,你当时在解释我所引用的确切文件,所以我认为这并不是太过牵强,但我承认我没有注意到这篇文章有多久了。我在这里提出了一个新问题:http://stackoverflow.com/questions/26643465/arm-cortex-m3-startup-code。如果可能,请在那里回答,谢谢。 - NickHalden

8

它们通常包含汇编代码。汇编器将其转换为目标文件,稍后由链接器与主要内容一起链接。但我想这取决于编译器、工具链等。


3

.s文件通常包含向量表。它定义了系统在中断发生时应该执行什么操作。这个表(代码)被放置在链接器文件中由您定义的内存地址中。例如,每当重置发生时,处理器应该从哪里开始运行,应该运行什么代码。同样地,还有其他处理程序(中断向量)。在STM32中,通常控制器会循环执行特定的处理程序。

如下面的例子所示:请参见此链接以获取详细说明

    .section INTERRUPT_VECTOR, "x"
    .global _Reset
    _Reset:
      B Reset_Handler /* Reset */
      B . /* Undefined */
      B . /* SWI */
      B . /* Prefetch Abort */
      B . /* Data Abort */
      B . /* reserved */
      B . /* IRQ */
      B . /* FIQ */

    Reset_Handler:
      LDR sp, =stack_top
      BL c_entry
      B .

这个汇编代码稍后将转换为目标文件,并与您的.c文件和.ld链接,以创建.elf或.bin文件。


1
您可能已经为ST套件准备了基于Keil的开发环境。根据编译器的版本,项目文件应该有不同的C、C++和汇编代码部分。在IDE中,打开您的项目并查找“项目属性”或类似的选项。
您可以导入和导出符号到汇编代码中,以便它和C/C++代码进行链接。使用Keil,所有这些都可以很好地集成。
EXPORT指令告诉汇编器将指定的符号公开,以便您的C/C++代码可以链接到它。
IMPORT指令告诉汇编器指定的符号在其他地方定义,并将在链接时解析。

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