|
开发Arm程序的时候,大多数时候使用C/C++语言就可以了,但汇编语言在某些情况下能够实现一些C语言无法实现的功能,这时候就要调用一些汇编语言的程序.我们需要大概了解一下在C语言中如何嵌入汇编语言. 内嵌汇编语法如下: __asm__ __volatile__ (汇编语句模板: 输出部分: 输入部分: 破坏描述部分) 共四个部分:汇编语句模板,输出部分,输入部分,破坏描述部分,各部分使用“:”格开,汇编语句模板必不可少,其他三部分可选,如果使用了后面的部分,而前面部分为空,也需要用“:”格开,相应部分内容为空。例如: __asm__ __volatile__("cli": : :"memory") 1、汇编语句模板 汇编语句模板由汇编语句序列组成,语句之间使用“;”、“\n”或“\n\t”分开。指令中的操作数可以使用占位符引用C语言变量,操作数占位符最多10个,名称如下:%0,%1,…,%9。指令中使用占位符表示的操作数,总被视为long型(4个字节),但对其施加的操作根据指令可以是字或者字节,当把操作数当作字或者字节使用时,默认为低字或者低字节。对字节操作可以显式的指明是低字节还是次字节。方法是在%和序号之间插入一个字母,“b”代表低字节,“h”代表高字节,例如:%h1。 2、输出部分 输出部分描述输出操作数,不同的操作数描述符之间用逗号格开,每个操作数描述符由限定字符串和C 语言变量组成。每个输出操作数的限定字符串必须包含“=”表示他是一个输出操作数。 例: __asm__ __volatile__("pushfl ; popl %0 ; cli":"=g" (x) ) 描述符字符串表示对该变量的限制条件,这样GCC 就可以根据这些条件决定如何分配寄存器,如何产生必要的代码处理指令操作数与C表达式或C变量之间的联系。 3、输入部分 输入部分描述输入操作数,不同的操作数描述符之间使用逗号格开,每个操作数描述符由限定字符串和C语言表达式或者C语言变量组成。 例1 : __asm__ __volatile__ ("lidt %0" : : "m" (real_mode_idt)); 先小试牛刀从最简单的的delay函数开始分析: static inline void delay (unsigned long loops) { __asm__ volatile ("1:\n" "subs %0, %1, #1\n" "bne 1b":"=r" (loops):"0" (loops)); } 先看下上述代码反汇编出来的结果是啥样的: static inline void delay (unsigned long loops) { __asm__ volatile ("1:\n" "subs %0, %1, #1\n" "bne 1b":"=r" (loops):"r" (loops)); } void main(void) { delay(2000); } 000103e8 <delay>: 103e8: e52db004 push {fp} ; (str fp, [sp, #-4]!) 103ec: e28db000 add fp, sp, #0 /* fp指向当前栈的栈顶 */ 103f0: e24dd00c sub sp, sp, #12 103f4: e50b0008 str r0, [fp, #-8] /* loops 入栈 */ 103f8: e51b3008 ldr r3, [fp, #-8] /* loops值保存至r3寄存器 */ 103fc: e2533001 subs r3, r3, #1 10400: 1afffffd bne 103fc <delay+0x14> 10404: e50b3008 str r3, [fp, #-8] /* 将loops 值写回r0,此函数无返回值这么做个人认为意义不大*/ 10408: e1a00000 nop ; (mov r0, r0) 1040c: e24bd000 sub sp, fp, #0 /* 恢复sp寄存器的值 */ 10410: e49db004 pop {fp} ; (ldr fp, [sp], #4) 10414: e12fff1e bx lr 00010418 <main>: 10418: e92d4800 push {fp, lr} 1041c: e28db004 add fp, sp, #4 10420: e3a00e7d mov r0, #2000 ; 0x7d0 10424: ebffffef bl 103e8 <delay> 10428: e1a00000 nop ; (mov r0, r0) 1042c: e8bd8800 pop {fp, pc} 不知道大家对上述的输入部和输出部的概念是否清晰,不过个人刚接触的时候确实是迷糊的,在网上找到如下解释,感觉还是很清晰的。 fsinx:汇编指令名 %1, %0:汇编指令操作数 “=f”(result):操作数%0是一个浮点寄存器,与变量result关联(对输出操作数,“关联”的意思就是说gcc执行完这条汇编指令后会把寄存器%0的内容送到变量result中) “f”(angle):操作数%1是一个浮点寄存器,与变量angle关联(对输入操作数,“关联”的意思是就是说gcc执行这条汇编指令前会先将变量angle的值读取到寄存器%1中) stack frame stack我们都知道,每一个进程都有自己的栈。考虑进程执行时发生函数调用的场景,母函数和子函数使用的是同一个栈,在通常的情况下,我们并不需要区分母函数和子函数分别使用了栈的哪个部分。但是,当我们需要在执行过程中对函数调用进行backtrace的时候,这一信息就很重要了。 简单的说,stack frame就是一个函数所使用的stack的一部分,所有函数的stack frame串起来就组成了一个完整的栈。stack frame的两个边界分别由FP和SP来限定。
上述的简单的示例,输入部和输出部都对应传入的loops变量,根据反汇编的结果期待的结果也一直,暂时先写个最简单的内嵌汇编,后续慢慢研究更复杂的使用场景。 以下代码是armv7-m 屏蔽中断的的内嵌汇编的代码
输入部:"i"(SEGGER_RTT_MAX_INTERRUPT_PRIORITY) 声明1%代表的是个立即数。 损坏部:"r1" 说明该段内嵌汇编使用到r1寄存器 我们反汇编输出下上述代码转化为真正的汇编代码如下: 发现:LockState c变量分配了R3寄存器进行保存,最终通过str r3, [r7, #4] 更新了变量LockState 的值,达到了读取basepri寄存器并保存至LockState 变量的目的,mov.w r1, #32 代码保存立即数至r1寄存器,反汇编的代码也使用了r1寄存器,跟之前的分析也是一致的。
|
微信公众号
手机版