C语言的内嵌汇编

398 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

在程序需要在嵌入式平台上运行时,如果需要代码占用内存更小、程序运行的效率更高或需要准确地操作寄存器时,嵌入汇编会是不错的选择。

内嵌汇编的语法格式

asm volatile( /* volatile : 可选,禁止编译器对汇编代码进行优化 */
  "汇编指令"   /* 汇编指令间使用'\n'分隔 */
  :"=限制符"(输出参数)
  :"限制符"(输入参数)
  :保留列表
)

常用的限制符

限制符说明
r通用寄存器
aeax, ax, al
bebx, bx, bl
cecx, cx, cl
dedx, dx, dl
Sesi, si
Dedi, di
q寄存器:a、b、c、d
m使用合法内存代表参数
g任意寄存器、内存、立即数

示例代码

下面示例演示的使用汇编对一个变量赋值:

#include <stdio.h>

/* 赋值 */
static int value_assignment(int input) {
  int ret = 0;

  asm volatile(
    /* ret = input */
    "movl %1, %0\n" /* 通过占位符指定交互的变量 : %0:ret %1:input*/
    :"=r"(ret) 
    :"r"(input)
  );

  return ret;
}

int main() {
  int input = 1;
  int ret = value_assignment(input);

  printf("input = %d\n", input);
  printf("ret = %d\n", ret);

  return 0;
}

打印结果:

input = 1
ret = 1

对于上述示例,编译器做了如下工作:

  1. ret 通过限定符建议关联到某个合适的寄存器(可以不关联)。
  2. input 限定符建议关联到另一个合适的寄存器(可以不关联)。
  3. 通过 通用寄存器 间接操作变量。

下面是 value_assignment() 函数使用 objdump 反汇编的结果:

static int value_assignment(int input) {
    1149:	f3 0f 1e fa          	endbr64 
    114d:	55                   	push   %rbp
    114e:	48 89 e5             	mov    %rsp,%rbp
    1151:	89 7d ec             	mov    %edi,-0x14(%rbp) /* input */
  int ret = 0;
    1154:	c7 45 fc 00 00 00 00 	movl   $0x0,-0x4(%rbp)  /* ret = 0 */

  asm volatile(
    115b:	8b 45 ec             	mov    -0x14(%rbp),%eax	/* 将栈上的 input 变量传递到 eax 寄存器 */
    115e:	89 c0                	mov    %eax,%eax              
    1160:	89 45 fc             	mov    %eax,-0x4(%rbp)  /* eax 寄存器 的值传递到 栈上的 ret 变量 */
    "movl %1, %0\n" 
    :"=r"(ret) 
    :"r"(input)
  );

  return ret;
    1163:	8b 45 fc             	mov    -0x4(%rbp),%eax
}
    1166:	5d                   	pop    %rbp
    1167:	c3                   	retq   

可以看出编译做了优化,没有将 retinput 根据限定符的建议关联到寄存器中,而是直接通过 通用寄存器 与 内存 完成赋值操作。

小结

  • C程序中支持直接嵌入汇编语言进行编程。
  • 通过寄存器到变量的关联完成汇编到C语言的交互。
  • 内嵌汇编代码时,通过占位符指定交互的变量
  • 限制符指示编译器将合适的寄存器关联到变量。(非强制)