WinCE 和 Linux 上的 ARM 调用约定
介绍
由于ARM处理器的低功耗、高性能和低成本,其架构在嵌入式系统和移动设备中越来越受欢迎。它广泛用于Windows CE和Linux等操作系统的开发。在本文中,我们将讨论Windows CE和Linux上的ARM调用约定。
什么是调用约定?
调用约定是一组规则,规定了程序中函数调用的方式。这些规则用于定义如何传递参数、如何处理返回值以及在函数调用期间如何管理堆栈。ARM架构有几种不同的调用约定,它们因使用的操作系统而异。
Windows CE上的调用约定
Windows CE是为小型设备(如手机和PDA)设计的实时操作系统。Windows CE中使用的ARM调用约定称为Windows CE操作系统的ARM过程调用标准(APCS-CE)。
APCS-CE定义了函数调用的几个规则。首先,函数的前四个参数通过寄存器r0到r3传递。如果参数超过四个,则在堆栈上传递。其次,函数的返回值通过寄存器r0传递。如果返回值大于32位,则通过寄存器r0和r1传递。
第三,堆栈以特定方式管理。当调用函数时,调用者负责将返回地址压入堆栈。被调用者负责在其可以使用它们之前将其余寄存器压入堆栈。最后,堆栈与8字节对齐。
示例
这是一个使用APCS-CE调用约定添加两个数字的函数示例:
int add(int a, int b) { int c; __asm { mov r0, a mov r1, b bl sum mov c, r0 } return c; } int sum(int a, int b) { return a + b; }
在这个例子中,add()函数接收两个参数(a和b),并返回它们的和。参数通过寄存器r0和r1传递,函数调用使用bl指令进行。sum()函数单独定义,并接收两个参数(a和b),将它们相加并返回。
Linux上的调用约定
Linux是一个流行的开源操作系统,运行在各种设备上,包括服务器、桌面和嵌入式系统。Linux中使用的ARM调用约定称为ARM过程调用标准(APCS)。
APCS定义了函数调用的几个规则。首先,函数的前四个参数通过寄存器r0到r3传递。如果参数超过四个,则在堆栈上传递。其次,函数的返回值通过寄存器r0传递。如果返回值大于32位,则通过寄存器r0和r1传递。
第三,堆栈以特定方式管理。当调用函数时,调用者负责将返回地址压入堆栈。被调用者负责在其可以使用它们之前将其余寄存器压入堆栈。最后,堆栈与8字节对齐。
示例
这是一个使用APCS调用约定添加两个数字的函数示例:
int add(int a, int b) { int c; asm volatile ( "mov r0, %1
\t" "mov r1, %2
\t" "bl sum
\t" "mov %0, r0
\t" : "= ) }
在这个例子中,add()函数接收两个参数(a和b),并返回它们的和。参数通过寄存器r0和r1传递,函数调用使用bl指令进行。sum()函数单独定义,并接收两个参数(a和b),将它们相加并返回。
APCS-CE和APCS之间的区别
APCS-CE和APCS之间的主要区别在于堆栈的管理方式。APCS-CE要求堆栈与8字节对齐,而APCS没有此要求。此外,APCS-CE要求调用者在调用函数之前将其余寄存器压入堆栈,而APCS没有此要求。
另一个区别是返回值的处理方式。APCS-CE要求返回值通过寄存器r0传递,而APCS允许返回值通过任何寄存器传递。
此外,重要的是要注意,调用约定可能因使用的编译器而异。例如,不同版本的GCC可能对同一操作系统使用不同的调用约定。必须查阅所用特定编译器的文档,以确保遵循正确的调用约定。
另一个重要的考虑因素是ARM架构支持32位和64位模式,并且调用约定可能因使用的模式而异。例如,在64位模式下,参数通过寄存器x0到x7传递,而不是r0到r7。
最后,值得注意的是,ARM架构除了APCS和APCS-CE之外,还有许多其他调用约定。例如,有一个由Microsoft Visual C++编译器生成的ARM代码的调用约定,还有一个由LLVM编译器生成的ARM代码的调用约定。
结论
在本文中,我们讨论了Windows CE和Linux上的ARM调用约定。我们看到这两个操作系统都使用ARM过程调用标准,但在堆栈管理和返回值处理方式上存在一些差异。了解这些调用约定对于在ARM架构上编写高效且可移植的代码非常重要。