ARM GCC内联汇编

ARM GCC内联汇编

通用的内嵌汇编模板

assembly 复制代码
__asm volatile (
    code
    :output operand list
    :input operand list
    :clobber list
);

Example

c 复制代码
C内联汇编:
// __attribute__((naked))
__attribute__((noinline))
uint8_t asm_test(uint8_t a, uint8_t b)
{
    uint8_t c;

    __asm volatile (
        "adds %[res], %[src0], %[src1]\n\t"
        :[res] "=r" (c)
        :[src0] "r" (a), [src1] "r" (b)
        :
    );

    return c;
}

反汇编代码:
0040a350 <asm_test>:
  40a350:	1840      	adds	r0, r0, r1
  40a352:	b2c0      	uxtb	r0, r0
  40a354:	4770      	bx	lr
  • 使用__attribute__((naked)可以使函数开头和结尾没有保存和恢复的指令(由编译器自动添加),使用__attribute__((noinline))可以使函数不被优化为嵌入某个函数的汇编指令,而是保持一个函数的形式。
  • 使用operand list与C函数的变量和入参进行交互,格式为[alias] "修饰符" (C variable),使用[]和()绑定变量的语法很像超链接,比如[Good](https://google.com)。
  • operand list中的各项以逗号,分隔
  • 对于早期gcc,不支持使用alias标识变量,而是使用%0,%1...这种根据变量出现的顺序编号,这样的缺陷在于一旦中间加一个操作符,所有的编号都变了
  • 使用volatile可以禁止编译器优化,用于将咱写的汇编指令都编译出来,比如mov r0, r0就不会再被优化

Operand List中修饰符

  • 定义数据类型
  • 输入输出类型
Modifier 含义
= write-only,仅用于output operand list
+ read-write,定义操作数可读可写,仅用于output operand list
& 指示编译器将变量绑定到特定的寄存器,避免与其它寄存器冲突,引发输出值被改变的问题,&只能放在+/=之后

没有=,+修饰的操作符都是read-only的,input operand list都是read-only,所以想对同一变量又读又写,那只能将它放在output operand list并用+修饰。

Example

  1. 立即数搭配宏进行使用,因为内联汇编里的东西没法被预处理
c 复制代码
#define A 20

void demo(void)
{
    __asm volatile (
    "mov r0, %[imm]\n\t"
    :
    :[imm] "I" (A)
    :
    );
}

/* -------------------对应汇编----------------------- */
0040a364 <demo>:
  40a364:	f04f 000a 	mov.w	r0, #10
  40a368:	4770      	bx	lr
  1. &的作用
c 复制代码
/* ---------------------正确写法带&----------------------- */
uint32_t tab[2];
uint8_t asm_test(uint8_t a, uint8_t b)
{
    uint32_t rdv, wdv=1;

    __asm volatile (
        "ldr %[rd], [%[tab]]\n\t"
        "str %[wr], [%[tab], #4]\n\t"
        :[rd] "=&r" (rdv)
        :[tab] "r" (tab),[wr] "r" (wdv)
        :
    );

    return 1;
}
/* ---------------------对应汇编----------------------- */
0040a350 <asm_test>:
  40a350:	2001      	movs	r0, #1
  40a352:	4b02      	ldr	r3, [pc, #8]	@ (40a35c <asm_test+0xc>)
  40a354:	681a      	ldr	r2, [r3, #0]
  40a356:	6058      	str	r0, [r3, #4]
  40a358:	4770      	bx	lr
  40a35a:	bf00      	nop
  40a35c:	200023fc 	.word	0x200023fc
      
      
      
/* ---------------------错误写法无&----------------------- */
uint32_t tab[2];
uint8_t asm_test(uint8_t a, uint8_t b)
{
    uint32_t rdv, wdv=1;

    __asm volatile (
        "ldr %[rd], [%[tab]]\n\t"
        "str %[wr], [%[tab], #4]\n\t"
        :[rd] "=r" (rdv)
        :[tab] "r" (tab),[wr] "r" (wdv)
        :
    );

    return 1;
}
/* ---------------------对应汇编----------------------- */
0040a350 <asm_test>:
  40a350:	2001      	movs	r0, #1
  40a352:	4b02      	ldr	r3, [pc, #8]	@ (40a35c <asm_test+0xc>)
  40a354:	681b      	ldr	r3, [r3, #0]
  40a356:	6058      	str	r0, [r3, #4]
  40a358:	4770      	bx	lr
  40a35a:	bf00      	nop
  40a35c:	200023fc 	.word	0x200023fc

在错误的写法中,由于不带&,造成rd和tab共用了寄存器r3;加上&后,rd用的r2,没有受到干扰,这些行为和编译器类型及版本有关系,为了避免出错,直接将output operand list都加上&修饰

Clobber List

破坏列表,告诉编译器我在这段汇编代码中改变了什么,比如改变了寄存器,内存等等,这样编译器会在执行这段汇编代码之前保存一下将被改变的数据。

类型 含义
r0, r1... 表示寄存器将被改变,在执行汇编之前将push对应的寄存器,并在执行完汇编之后pop出来
memory 告诉编译器内存将会被改变,在执行汇编之前,将那些还未写入内存的值赶紧写入
cc 告诉编译器condition code状态寄存器将改变

Example

对于memory和cc没构建出好的例子,只有关于寄存器的了。

c 复制代码
/*--------------------------C--------------------------*/
uint8_t asm_test(uint8_t a, uint8_t b)
{

    __asm volatile (
        "mov r0, r0\n\t"
        :
        :
        :"r4"
    );

    return 0;
}

/*--------------------------汇编--------------------------*/
0040a350 <asm_test>:
  40a350:	b510      	push	{r4, lr}
  40a352:	4600      	mov	r0, r0
  40a354:	2000      	movs	r0, #0
  40a356:	bd10      	pop	{r4, pc}

果然编译器对咱指定的"r4"做了保存恢复的操作。

相关推荐
iCxhust4 小时前
8088汇编测试程序 (MASM/TASM) — 显示 “HELLO 8088!“ + “LCD1602 OK“
汇编·单片机·嵌入式硬件·51单片机·微机原理
lhbian21 小时前
AI编程革命:Codex让脚本开发提速10倍
开发语言·汇编·jvm·c#
誰能久伴不乏1 天前
SPI总线通信协议基础与ICM20607传感器驱动开发指南
arm开发·c++·驱动开发·嵌入式硬件·arm
Mike_detailing1 天前
资源分享:VMware-Fusion-25H2u1 和 arm版本win11
macos·arm·虚拟机
wechatbot8882 天前
企业微信全场景消息发送接口开发实现及接口调用
java·汇编·微信·企业微信
wechatbot8882 天前
企业微信 iPad 协议接口全功能开发实战
汇编·人工智能·ios·微信·企业微信·ipad
techdashen3 天前
Rust 1.88 终于稳定了裸函数:写汇编不再需要那堆样板代码
汇编·rust
So_shine4 天前
stm32f103汇编-1:LED点灯
汇编·stm32·单片机·led
北漂Zachary5 天前
四大编程语言终极对决:汇编/C#/Go/Java谁更强
汇编·golang·c#
独小乐6 天前
019.ADC转换和子中断|千篇笔记实现嵌入式全栈/裸机篇
linux·c语言·驱动开发·笔记·嵌入式硬件·mcu·arm