C语言内嵌汇编

119 阅读1分钟

实现val3 = val1 + val2函数

#include <stdio.h>
void myAdd(int val1, int val2){
    int val3 = 0;
    printf("val1=%d, val2=%d, val3=%d\n", val1, val2, val3);
    asm volatile(
        "movl $0, %%eax\n\t"
        "addl %1, %%eax\n\t"
        "addl %2, %%eax\n\t"
        "movl %%eax, %0\n\t"
        :"=m"(val3)
        :"c"(val1), "d"(val2)
    );
    printf("val1=%d, val2=%d, val3=%d\n", val1, val2, val3);
}
int main(){
    myAdd(1, 2);
    return 0;
}
  • __asm__asm等价,__volatile__volatile等价。
  • 寄存器前要加一个%转义符号。
  • %0,%1..是指输入输出部分,第一个编号为%0
  • "a""c""d"是将变量放到eax,ecxedx寄存器中。
  • volatile`用于关闭编译器优化

asm内嵌汇编代码语法

asm volatile (
    "asm code\n\t"
    :output
    :input
    :changed(clobber list)
);

output表示的是从ASM到C语言输出,简单理解就是寄存器到变量的操作;而input相反,指的是C变量到ASM寄存器赋值的过程。

input和output格式为[asm代码中用到的符号]"内容限定"(C代码中的变量)

  • []符号可以省略,改成%0,%1等代替。
  • 内容限定
简单约束符
r 使用任何可用的寄存器
m 使用变量的内存地址
i 使用立即数
​
约束修饰符:在上面的简单约束符开头可以增加下面的约束修饰符来决定它们的可读写特性。
'=': 表示只写
'+': 表示读写
对于既没有指定'=',也没有指定'+'的操作数,会被认为是只读的;
=a: 表示先嫁给你结果输出值rax/eax寄存器,再由rax/eax寄存器更新相应的输出变量

changed(clobber list),内联汇编告知GCC,需要GCC“照顾”好这些被 影响的寄存器或者内存,比如必要时需要在执行内联汇编指令前保存好寄存器,而在执行内联汇编指令后恢复寄存器的值。

因此上面代码的一个等效代码如下

#include <stdio.h>
void myAdd(int val1, int val2){
    int val3 = 0;
    printf("val1=%d, val2=%d, val3=%d\n", val1, val2, val3);
    asm volatile(
        "movl $0, %%eax\n\t"
        "addl %[input1], %%eax\n\t"
        "addl %[input2], %%eax\n\t"
        "movl %%eax, %[output]\n\t"
        :[output]"=m"(val3)
        :[input1]"c"(val1), [input2]"d"(val2)
    );
    printf("val1=%d, val2=%d, val3=%d\n", val1, val2, val3);
}
int main(){
    myAdd(1, 2);
    return 0;
}