Board logo

标题: ARM过程调用标准 更新 [打印本页]

作者: look_w    时间: 2017-9-24 17:23     标题: ARM过程调用标准 更新

为了更细致的理解,下面是代码是 Norcroft C v4.00 为上述代码生成的...        AREA |C$$code|, CODE, READONLY        IMPORT  |__main||x$codeseg|        B       |__main|        DCB     &6d,&61,&69,&6e        DCB     &00,&00,&00,&00        DCD     &ff000008        IMPORT  |x$stack_overflow|        EXPORT  one        EXPORT  mainmain        MOV     ip, sp        STMFD   sp!, {fp,ip,lr,pc}        SUB     fp, ip, #4        CMPS    sp, sl        BLLT    |x$stack_overflow|        BL      one        MOV     a1, #0        LDMEA   fp, {fp,sp,pc}^        DCB     &6f,&6e,&65,&00        DCD     &ff000004        EXPORT  zero        EXPORT  twoone        MOV     ip, sp        STMFD   sp!, {fp,ip,lr,pc}        SUB     fp, ip, #4        CMPS    sp, sl        BLLT    |x$stack_overflow|        BL      zero        LDMEA   fp, {fp,sp,lr}        B       two        IMPORT  |_printf|two        ADD     a1, pc, #L000060-.-8        B       |_printf|L000060        DCB     &6d,&61,&69,&6e        DCB     &2e,&2e,&2e,&6f        DCB     &6e,&65,&2e,&2e        DCB     &2e,&74,&77,&6f        DCB     &0a,&00,&00,&00zero        MOVS    pc, lr        AREA |C$$data||x$dataseg|        END这个例子不遵从 32 为体系。APCS-32 规定只是简单的说明了标志不需要被保存。所以删除 LDM 的‘^’后缀,并在函数 zero 中删除 MOVS 的‘S’后缀。则代码就与遵从 32-bit 的编译器生成的一样了。
保存代码指针包含这条设置回溯结构的指令(STMFD ...)的地址再加上 12 字节。记住,对于 26-bit 代码,你需要去除其中的 PSR 来得到实际的代码地址。
现在我们查看刚进入函数的时候:

实际参数APCS 没有定义记录、数组、和类似的格局。这样语言可以自由的定义如何进行这些活动。但是,如果你自己的实现实际上不符合 APCS 的精神,那么将不允许来自你的编译器的代码与来自其他编译器的代码连接在一起。典型的,使用 C 语言的惯例。

函数退出通过把返回连接值传送到程序计数器中来退出函数,并且:
在 32 位模式下,不需要对 PSR 标志进行跨越函数调用的保护。在 26 位模式下必须这样,并通过传送 lr 到 pc 中(MOVS、或 LDMFD xxx^)来暗中恢复。必须从 lr 重新装载 N、Z、C 和 V,跨越函数保护这些标志不是足够的。
建立栈回溯结构对于一个简单函数(固定个数的参数,不可重入),你可以用下列指令建立一个栈回溯结构:
function_name_label        MOV     ip, sp        STMFD   sp!, {fp,ip,lr,pc}        SUB     fp, ip, #4这个片段(来自上述编译后的程序)是最基本的形式。如果你要破坏其他不可破坏的寄存器,则你应该在这个 STMFD 指令中包含它们。
下一个任务是检查栈空间。如果不需要很多空间(小于 256 字节)则你可以使用:
        CMPS    sp, sl        BLLT    |x$stack_overflow|这是 C 版本 4.00 处理溢出的方式。在以后的版本中,你要调用 |__rt_stkovf_split_small|。接着做你自己的事情...
通过下面的指令完成退出:
        LDMEA   fp, {fp,sp,pc}^还有,如果你入栈了其他寄存器,则也在这里重新装载它们。选择这个简单的 LDM 退出机制的原因是它比分支到一个特殊的函数退出处理器(handler)更容易和更合理。
用在回溯中的对这个协议的一个扩展是把函数名字嵌入到代码中。紧靠在函数(和 MOV ip, sp)的前面的应该是:
        DCD     &ff0000xx这里的‘xx’是函数名字符串的长度(包括填充和终结符)。这个字符串是字对齐、尾部填充的,并且应当被直接放置在 DCD &ff....的前面。
所以一个完整的栈回溯代码应当是:
        DCB     "my_function_name", 0, 0, 0, 0        DCD     &ff000010my_function_name        MOV     ip, sp        STMFD   sp!, {fp, ip, lr, pc}        SUB     fp, ip, #4        CMPS    sp, sl                    ; 如果你不使用栈        BLLT    |x$stack_overflow|        ; 则可以省略        ...处理...        LDMEA   fp, {fp, sp, pc}^要使它遵从 32-bit 体系,只须简单的省略最后一个指令的‘^’。注意你不能在一个编译的 26-bit 代码中使用这个代码。实际上,你可以去除它,但这不是我愿意打赌的事情。
如果你不使用栈,并且你不需要保存任何寄存器,并且你不调用任何东西,则没有必要设置 APCS 块(但在调试阶段对跟踪问题仍是有用的)。在这种情况下你可以:
my_simple_function        ...处理...        MOVS    pc, lr(再次,对 32 位 APCS 使用 MOV 而不是 MOVS,但是不要冒险与 26 位代码连接)。

APCS 标准总的来说,有多个版本的 APCS (实际上是 16 个)。我们只关心在 RISC OS 上可能遇到的。
APCS-A
就是 APCS-Arthur;由早期的 Arthur 所定义。它已经被废弃,原因是它有不同的寄存器定义(对于熟练的 RISC OS 程序员它是某种异类)。它用于在 USR 模式下运行的 Arthur 应用程序。不应该使用它。

APCS-R
就是 APCS-RISC OS。用于 RISC OS 应用程序在 USR 模式下进行操作;或在 SVC 模式下的模块/处理程序。

APCS-U
就是 APCS-Unix,Acorn 的 RISCiX 使用它。它用于 RISCiX 应用程序(USR 模式)或内核(SVC 模式)。

APCS-32
它是 APCS-2(-R 和 -U)的一个扩展,允许 32-bit 程序计数器,并且从执行在 USR 模式下的一个函数中退出时,允许标志不被恢复。其他事情同于 APCS-R。
Acorn C 版本 5 支持生成 32-bit 代码;在用于广域调试的 32 位工具中,它是最完整的开发发行。一个简单的测试是要求你的编译器导出汇编源码(而不是制作目标代码)。你不应该找到:
MOVS PC, R14
或者
LDMFD R13!, {Rx-x, PC}^


对编码有用的东西首先要考虑的是该死的 26/32 位问题。 简单的说,不转弯抹角绝对没有方法为两个版本的 APCS 汇编同一个通用代码。但是幸运的这不是问题。APCS 标准不会突然改变。RISC OS 的 32 位版本也不会立刻变异。所以利用这些,我们可以设计一种支持两种版本的方案。这将远远超出 APCS,对于 RISC OS 的 32 位版本你需要使用 MSR 来处理状态和模式位,而不是使用 TEQP。许多现存的 API 实际上不需要保护标志位。所以在我们的 32 版本中可以通过把 MOVS PC,... 变成 MOV PC,...,和把 LDM {...}^ 变成 LDM {...},并重新建造来解决。objasm 汇编器(v3.00 和以后)有一个 {CONFIG} 变量可以是 26 或 32。可以使用它建造宏...
my_function_name        MOV     ip, sp        STMFD   sp!, {fp, ip, lr, pc}        SUB     fp, ip, #4        ...处理...        [ {CONFIG} = 26          LDMEA   fp, {fp, sp, pc}^        |          LDMEA   fp, {fp, sp, pc}        ]我未测试这个代码。它(或类似的东西)好象是保持与两个版本的 APCS 相兼容的最佳方式,也是对 RISC OS 的不同版本,26 位版本和将来的 32 位版本的最佳方法。
测试是否处于 32 位? 如果你要求你的代码有适应性,有一个最简单的方法来确定处理器的 PC 状态:
   TEQ     PC, PC     ; 对于 32 位是 EQ;对于 26 位是 NE使用它你可以确定:





欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0