你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

基于ARM的gcc内嵌汇编初探

[复制链接]
andeyqi 发布时间:2019-8-14 11:29
开发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来限定。
fp.png

上述的简单的示例,输入部和输出部都对应传入的loops变量,根据反汇编的结果期待的结果也一直,暂时先写个最简单的内嵌汇编,后续慢慢研究更复杂的使用场景。

以下代码是armv7-m 屏蔽中断的的内嵌汇编的代码

  1. #ifndef   SEGGER_RTT_MAX_INTERRUPT_PRIORITY
  2.       #define SEGGER_RTT_MAX_INTERRUPT_PRIORITY   (0x20)
  3.     #endif
  4.     #define SEGGER_RTT_LOCK()   {                                                                   \
  5.                                     unsigned int LockState;                                         \
  6.                                   __asm volatile ("mrs   %0, basepri  \n\t"                         \
  7.                                                   "mov   r1, %1       \n\t"                         \
  8.                                                   "msr   basepri, r1  \n\t"                         \
  9.                                                   : "=r" (LockState)                                \
  10.                                                   : "i"(SEGGER_RTT_MAX_INTERRUPT_PRIORITY)          \
  11.                                                   : "r1"                                            \
  12.                                                   );

  13.     #define SEGGER_RTT_UNLOCK()   __asm volatile ("msr   basepri, %0  \n\t"                         \
  14.                                                   :                                                 \
  15.                                                   : "r" (LockState)                                 \
  16.                                                   :                                                 \
  17.                                                   );                                                \
  18.                                 }
复制代码
根据之前的理解,我们分析下SEGGER_RTT_LOCK 函数。输出部:LockState 代表0%,修饰符号为"=r"说明这个c语言变量LockState需要分配个寄存器并且该变量的值会被更新。
输入部:"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寄存器,跟之前的分析也是一致的。
  1. SEGGER_RTT_LOCK();
  2.     93e2:        f3ef 8311         mrs        r3, BASEPRI
  3.     93e6:        f04f 0120         mov.w        r1, #32
  4.     93ea:        f381 8811         msr        BASEPRI, r1
  5.     93ee:        607b              str        r3, [r7, #4]
复制代码
SEGGER_RTT_UNLOCK() 的代码只有一个输入部,将 c变量LockState 的值分配寄存去存储,根据如下的代码读取该变量的值至r3寄存器,之后通过r3恢复BASEPRI寄存器的内容。

  1.   SEGGER_RTT_UNLOCK()
  2.     93f6:        687b              ldr        r3, [r7, #4]
  3.     93f8:        f383 8811         msr        BASEPRI, r3
复制代码



收藏 1 评论0 发布时间:2019-8-14 11:29

举报

0个回答

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版