浅析ARM汇编语言子例程设计方法

2019-12-04来源: eefocus关键字:ARM  汇编语言  设计方法

引言

在嵌入式软件系统开发过程中,大量使用C语言进行应用程序开发以提高开发效率。同时,系统中经常包含一些决定整个系统性能的关键模块,此时为了获得最佳性能,经常使用汇编语言编写它们,或者某些特殊情况下,例如操作硬件等,也必须使用汇编语言。


函数是C语言中一个重要的概念,在汇编语言中经常使用子例程或过程(subroutine or procedure)表达同样的概念,本文使用术语子例程。本文首先介绍ARM汇编语言子例程设计的一般方法,并以此为基础提出一种新的基于堆栈帧的设计方法,同时介绍与C语言交互技术。

1 一般方法

在ARM汇编语言中一般使用BL(Branch and Link)指令调用某个子例程,BL指令首先将返回地址保存在链接寄存器R14(也称为LR)中,然后跳转到目标地址。子例程执行完毕后,通过将R14的内容复制到PC中实现从子例程返回。

BL subr              ;调用subr

…                     ;返回到这里

subr

…                     ;子例程体

MOV PC, LR     ;从subr返回

上面这种方法对于叶子例程(即不调用其它子例程的例程)来说已经足够了,但是它并不能处理嵌套或递归调用。假设subr内部又使用BL调用了另一个子例程,那么LR将被后一次调用的返回地址所改写,导致死循环无法从subr返回。为了解决这个问题,subr必须在调用第二个子例程之前保存LR。更进一步,为了使子例程能够以任意深度调用另外一个子例程,必须采取某种方法以保存任意数目的返回地址。最常用的方法是将返回地址保存在堆栈中,如下面的例子所示:

subr

STMFD SP!, {R4-R12, LR}      ;保存所有的工作寄存器和返回地址,并更新堆栈指针

…                                           ;子例程体

LDMFD SP! { R4-R12, PC}       ;恢复所有的工作寄存器,使用保存的返回地址装载PC,

;更新堆栈指针

在子例程入口点可以把subr中需要使用的任何工作寄存器和LR保存到堆栈上,在出口点将它们弹出,这样就可以安全的进行子例程调用,而不必担心返回地址被改写导致无法从子例程正常返回。注意在出口点直接使用返回地址装载PC,它等价于下面的两条指令:

LDMFD SP! { R4-R12, LR}

MOV PC, LR

2 基于堆栈帧的子例程

前面介绍的子例程设计方法虽然已经能够满足设计需要,但是对于熟悉x86汇编语言的程序员来说还是不太适应。众所周知,x86汇编语言子例程存在一个标准的堆栈结构,如图1所示。它的一个显著特点是EBP寄存器作为参考点用来引用参数和局部变量,例如第一个参数位于地址[EBP+8]处。堆栈帧的优点在于它统一了汇编子例程的编程风格,参数、返回地址、工作寄存器或者局部变量都有固定的位置,这样不仅能够提高代码的可读性也有利于代码的维护。基于上面的考虑,特将堆栈帧的概念引入ARM汇编语言子例程的设计之中,如下面的例子所示。为了简便,假设subr的原型为int subr(int a, int b, int c, int d, int e, int f);,很明显根据APCS(ARM过程调用标准),参数a-d通过寄存器R0-R3进行传递,剩下的两个参数e和f通过堆栈传递。最终形成的堆栈帧结构如图2所示,与图1中的x86帧结构相比,唯一的不同之处在于局部变量和工作寄存器的位置相反,而出现这种差异的原因是为了充分利用ARM中多寄存器load-store指令的优势。

caller

 …                                   ;省略了参数a-d的传递代码

MOV R4, #2

STR R4, [SP, #-4]!        ;1)将参数f推入堆栈

MOV R4, #1

STR  R4, [SP, #-4]!        ;将参数e推入堆栈

BL    subr                      ;2)调用子例程subr

ADD SP, SP, #8                ;8)平衡堆栈。subr返回到这里,返回值保存在R0中

 

subr

STMFD     SP!, {R4-R7, FP,LR}    ;3)保存工作寄存器、FP和LR

ADD  FP, SP, #16            ;4)计算帧指针

SUB    SP, SP, #8              ;5)为局部变量分配空间

LDR  R4, [FP, #8]           ;载入参数e

LDR  R5, [FP, #12]        ;载入参数f

…                                   ;subr子例程体

ADD   SP, SP, #8              ;6)释放局部变量空间

LDMFD SP!, {R4-R7, FP, PC}     ;7)恢复寄存器并返回

 

               图1 x86堆栈帧结构                            

       图2 ARM中的堆栈帧结构

下面详细的说明如何一步步构建堆栈帧,其中序号与示例代码注释中的序号是一一对应的:

1)        通常,使用STR Rn, [SP, #-4]!指令将子例程需要的参数推入堆栈。注意根据APCS,首先考虑通过寄存器R0-R3传递参数,剩下的参数以相反的顺序推入堆栈。如果通过寄存器R0-R3就可以传递所有的参数,那么可以省略这个步骤。

2)        BL指令将返回地址推入堆栈,然后跳转到指定的子例程继续执行。自此开始所有修改堆栈的工作转交给子例程。

3)        如果子例程需要使用R4-R11工作寄存器,必须将它们推入堆栈;同时将旧的帧指针寄存器FP和链接寄存器LR推入堆栈,这些工作在一条指令中即可高效的完成。

4)        调整帧指针FP,以便随后使用它引用堆栈参数和变量。在本例中,可以使用LDR R0, [FP, #8]引用参数e,LDR R0, [FP, #-20]引用第一个局部变量。

5)        分配8个字节的堆栈空间存储子例程的局部变量。但是如果不需要使用局部变量,那么可以省略这个步骤。与CISC架构的x86处理器不同,RISC架构的ARM处理器拥有大量的通用寄存器,例如本例中的R0-R7、LR等,因此大多数情况下并不需要为局部变量分配堆栈空间。

6)        如果先前为局部变量分配了堆栈空间,那么为了保持堆栈平衡需要释放它们。

7)        恢复第三步保存到堆栈的各个寄存器,这里也是通过直接装载PC寄存器从子例程返回。

8)        子例程subr执行完成后返回到这里。这一步非常重要,由于caller在调用subr前将参数e和f推入堆栈,因此从subr返回后caller必须将这两个参数弹出堆栈,以保持堆栈的平衡。当然如果是从C语言中调用子例程,那么编译器会负责完成堆栈平衡工作。

3 汇编语言与C语言交互

在完成汇编子例程的编写之后,下一个问题就是如何在C语言中调用它们。本质上,不管使用何种语言编写代码,交叉调用其它模块的例程必须遵循一个通用的参数和结果传递约定。对于ARM来说,这个约定称为ARM过程调用标准,其定义了:

l         通用寄存器的特定用途

l         使用何种类型的堆栈

l         参数和结果的传递机制

l         ARM共享库机制支持

由于编译器生成的代码总是严格遵循APCS,因此只需保证手动编写的汇编代码符合APCS即可。下面的示例展示了如何从C语言中调用汇编语言编写的实现内存拷贝功能的子例程,开发环境为RealView MDK3.22a。

;定义和导出mymemcpy的mymemcpy.s文件

; R0目的地址,R1指向源地址,R2拷贝长度

       AREA Demo, CODE, READONLY

       EXPORT mymemcpy

mymemcpy

       STMFD SP!, {R4,LR}

       MOV R3,R0   ;取出目的地址

       MOV R12,R1 ;取出源地址

copy

       CMP R2, #0    ;如果长度小于等于0则退出

BLE e

[1] [2]
关键字:ARM  汇编语言  设计方法 编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic481972.html 本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:iOS逆向工程之Hopper中的ARM指令
下一篇:arm交叉编译器gnueabi、none-eabi、arm-eabi、gnueabihf的区别

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

ARM小记
ARM何物  ARM(Advanced RISC Machines,进阶精简指令集机器),如果你一直把这个单词读成“A”-“R”-“M”,那么将有一个好消息和一个坏消息要告诉你。好消息是:你 这是初次接触到ARM技术相关的文章,而本文内容正是专为你量身打造的,无论你是嵌入式系统的门外汉还是单片机技术的初学者,都可以通过本文轻松地了解ARM技术。 请相信我,这并不困难!坏消息是:我很抱歉的告诉你, 它的正确读法应该是“arm”,是手臂、胳膊的英文发音。 很出丑是不是?但除非你是在大声朗读,不然没人会知道 的。你又多了一个知识和一个不可告人的秘密。调整一下心态,我们的故事就从这里开始吧!  
发表于 2019-12-02
ARM小记
用ARM点亮一个led并闪烁
;       *//*               led.c:  用ARM点亮一个led并闪烁                    *//*                                        
发表于 2019-11-30
基于ARM倾角和加速度的检测装置的设计
C语言进行编程,完全采用面向对象的开发思想,具有很大的个人发挥空间,完成同一种功能可以从不同方面下手解决。MDK源自德国的KEIL公司,是RealView MDK的简称。在全球MDK被超过10万的嵌入式开发工程师使用。目前最新版本为:MDK5.14,该版本为 uVision5 IDE集成开发环境,是目前针对ARM处理器,尤其是Cortex M内核处理器的最佳开发工具。设计规划:针对此次课程设计,设计如下规划,如图2.1所示。图2.1 课程设计规划本章小结本章主要对课程设计的背景进行了介绍并对设计进行规划,制定出合理的工作流程。3  硬件系统设计  总体设计方案倾角加速度检测系统,主要要完成对温度、加速度
发表于 2019-11-29
基于ARM倾角和加速度的检测装置的设计
arm cortex m0 lpc1114的引脚图与综述
我们以LQFP48封装为例进行介绍。 从图中引脚上的描述可以看出,它的几乎每一个引脚上都复用了若干个功能。例如,第9脚:PIO1_8/CT16B1_CAP0,代表,第9脚既可以作为通用的输入输出引脚P1.8,也可以作为16位定时器1的捕获引脚。(关于什么是捕获引脚,请看Ration的《RATION LPC1114基础篇手册》)。引脚作为什么功能,需要通过IOCON模块来配置。现在,让我们把所有的引脚描述都看一遍吧!看完了引脚描述,你就会对它有一个基本的认识了。GPIO模块引脚:PIO0_0~PIO0~11PIO1_0~PIO1~11PIO2_0~PIO2_11PIO3_0~PIO3_5P0口,P1口,P2口各有12个引脚,
发表于 2019-11-29
arm cortex m0 lpc1114的引脚图与综述
ARM处理器主要有哪些优势?
对于如今大量出现的32位嵌入式应用,以笔者的看法,arm处理器的优势主要有以下几个方面。1.高性能、低功耗、低价格把ARM处理器的性能拿来和一些着名的通用处理器(如Pentium)相比是不合适的,因为他们各自针对的应用需求是不同的。Pentium处理器采用多条指令流水线的超标量结构,追求通用应用目标下的超强性能,功耗大,可以用散热器加风扇散热。ARM针对嵌入式应用,在满足性能要求的前提下,力求最低的功率消耗。ARM结构的优点是能兼顾到性能、功耗、代码密度、价格等几个方面,而且做得比较均衡。在性能/功耗比(MIPS/W)方面,ARM处理器具有业界领先的性能。基于ARM核的芯片价格也很低,目前arm CortexM的芯片价格可低至10
发表于 2019-11-29
一种基于ARM和智能手机的蓝牙CAN分析仪设计
  针对CAN总线通信质量、测试和验证的需要,以及传统CAN分析仪的复杂性,且必须选择PC机作为显示终端的不足,论证了一种基于ARM单片机和智能手机的CAN总线分析仪设计。该分析仪采用以ARM单片机为核心的硬件电路完成对CAN网络的实时数据收集和监控;采用蓝牙通信方式实现分析仪与智能手机的通信并以智能手机为终端完成数据分析。文章对分析仪硬件、软件和智能手机页面进行了具体设计,提出了一种新的波特率自动检测方法,最后给出了所设计的 CAN总线分析仪的实际试验结果,可实现CAN总线波特率自动检测、正常监测以及CAN总线状态分析的功能。  CAN(controller area network)控制器局域网络是一种实时性强、灵活性好
发表于 2019-11-27
一种基于ARM和智能手机的蓝牙CAN分析仪设计
小广播
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2019 EEWORLD.com.cn, Inc. All rights reserved