如何通过asm关键字嵌入汇编语言代码?

道哥分享

    一、基本 asm 格式
    1. 语法规则
    2. test1.c 插入空指令
    3. test2.c 操作全局变量
    4. test3.c 尝试操作局部变量
    二、扩展 asm 格式
    1. 指令格式
    2. 输出和输入操作数列表
    3. test4.c 通过寄存器操作局部变量
    4. test5.c 声明使用的寄存器
    三、使用占位符来代替寄存器名称
    1. test6.c 使用占位符代替寄存器名
    2. test7.c 给寄存器起别名
    四、使用内存地址
    1. test8.c 使用内存地址来操作数据
    五、总结
    在 Linux 代码中,经常可以看到在 C 代码中,嵌入部分汇编代码,这些代码要么是与硬件体系相关的,要么是对性能有关键影响的。
    在很久以前,我特别惧怕内嵌汇编代码,直到后来把汇编部分的短板补上之后,才彻底终结这种心理。
    也许你在工作中,几乎不会涉及到内嵌汇编代码的工作,但是一旦进入到系统的底层,或者需要对时间关键场景进行优化,这个时候你的知识储备就发挥重要作用了!
    这篇文章,我们就来详细聊一聊在 C 语言中,如何通过 asm 关键字来嵌入汇编语言代码,文中的 8 个示例代码从简单到复杂,逐步深入地介绍内联汇编的关键语法规则。
    希望这篇文章能够成为你进阶高手路上的垫脚石!
    PS:
    示例代码中使用的是 Linux 系统中 AT&T 汇编语法;
    一、基本 asm 格式
    gcc 编译器支持 2 种形式的内联 asm 代码:
    基本 asm 格式:不支持操作数;
    扩展 asm 格式:支持操作数;
    1. 语法规则
    asm [volatile] ("汇编指令")
    所有指令,必须用双引号包裹起来;
    超过一条指令,必须用分隔符进行分割,为了排版,一般会加上 ;
    多条汇编指令,可以写在一行,也可以写在多行;
    关键字 asm 可以使用 asm 来替换;
    volatile 是可选的,编译器有可能对汇编代码进行优化,使用 volatile 关键字之后,告诉编译器不要优化手写的内联汇编代码。
    2. test1.c 插入空指令#include <stdio.h>
    int main()
    {
        asm ("nop");
        printf("hello");
        asm ("nop nop "
     "nop");
        return 0;
    }
    注意:C语言中会自动把两个连续的字符串字面量拼接成一个,所以"nop nop " "nop" 这两个字符串会自动拼接成一个字符串。
    生成汇编代码指令:
    gcc -m32 -S -o test1.s test1.c
    test1.s 中内容如下(只贴出了内联汇编代码相关部分的代码):
    #APP
    # 5 "test1.c" 1
     nop
    # 0 "" 2
    #NO_APP
     // 这里是 printf 语句生成的代码。
    #APP
    # 7 "test1.c" 1
     nop
     nop
     nop
    # 0 "" 2
    #NO_APP
    可以看到,内联汇编代码被两个注释(#APP ... #NO_APP)包裹起来。在源码中嵌入了两个汇编代码,因此可以看到 gcc 编译器生成的汇编代码中包含了这两部分代码。
    这 2 部分嵌入的汇编代码都是空指令 nop,没有什么意义。
    3. test2.c 操作全局变量
    在 C 代码中嵌入汇编指令,目的是用来计算,或者执行一定的功能,下面我们就来看一下,如何在内联汇编指令中,操作全局变量。
    #include <stdio.h>
    int a = 1;
    int b = 2;
    int c;
    int main()
    {
        asm volatile ("movl a, %eax "
            "addl b, %eax "
            "movl %eax, c");
        printf("c = %d ", c);
        return 0;
    }
    关于汇编指令中编译器的基本知识:
    eax, ebx 都是 x86 平台中的寄存器(32位),在基本asm格式中,寄存器的前面必须加上百分号%。
    32 位的寄存器 eax 可以当做 16 位来使用(ax),或者当做 8 位来使用(ah, al),本文只会按照 32 位来使用。
    代码说明:
    movl a, %eax  // 把变量a的值复制到 %eax 寄存器中;
    addl b, %eax   // 把变量 b 的值 与 %eax 寄存器中的值(a)相加,结果放在 %eax 寄存器中;
    movl %eax, c   // 把 %eax 寄存器中的值复制到变量 c 中;
    
    生成汇编代码指令:
    gcc -m32 -S -o test2.s test2.c
    test2.s 内容如下(只贴出与内联汇编代码相关部分):
    #APP
    # 9 "test2.c" 1
     movl a, %eax
     addl b, %eax
     movl %eax, c
    # 0 "" 2
    #NO_APP
    可以看到,在内联汇编代码中,可以直接使用全局变量 a, b 的名称来操作。执行 test2,可以得到正确的结果。
    思考一个问题:为什么在汇编代码中,可以使用变量a, b, c?
    
    
    1  2  3  下一页>