康健计划——内联汇编

内联汇编只能使用AT&T汇编,以前先学的是Intel汇编,更熟悉的也是这个,AT&T汇编也是忘的差不多了。。

Intel汇编与AT&T汇编的区别

区别 intel AT&T
寄存器 寄存器前无前缀 寄存器前有前缀 %
操作数顺序 目的操作数在左,源操作数在右 目的操作数在右,源操作数在左
操作数指定大小 操作数前加修饰符,byte表示8位,word表示16位,dword表示32位 在指令后加字母表示,b表示8位,w表示16位,l表示32位
立即数 无前缀,直接用数字表示 有前缀$
远跳转 jmp far segment:offset ljmp %segment: %offset
远调用 call far segment:offset lcall %segment: %offset
远返回 ret far n lret $n

一些个人理解:
因为本人先学的Intel汇编,所以站在其角度上讲一下AT&T的不同理念。

  • 数字在AT&T汇编的表示的更高优先级应该是内存地址,所以立即数退而求次
  • 比起Intel的大量修饰符,AT&T使用的是在指令后增加简单的字符表示

基本内联汇编

基本内联汇编的格式如下:

asm [volatile] ("assembly code")

其中asm是必加的,这个就不讨论。
volatile和普通C一致,就是禁止编译器优化
再是汇编代码的注意事项

  • 双引号是必须的
  • 跨行需要加\
  • 指令之间用;, \n等分割,统一一下还是用;

还有需要注意的是,基本内联汇编只能使用C的全局变量,局部变量无法使用。

示例程序:

const char* str = "hello world";
int count = 0;

int main()
{
    asm ("pusha; \
        movl $4, %eax;   \
        movl $1, %ebx;   \
        movl str, %ecx;  \
        movl $12, %edx;  \
        int $0x80;       \
        mov %eax, count; \
        popa");
    return 0;
}

扩展内联汇编

扩展内联汇编增加的功能有

  • 能使用局部变量
  • 指定变量输入到寄存器,指定寄存器输出到变量
  • 约束
  • 占位符等

在扩展内联汇编中出现了占位符 %,这与AT&T汇编中的寄存器表示有所冲突,所以在扩展内联汇编中只能退而求次,表示寄存器时只能使用%%前缀

格式:

asm [volatile] ("assemble code": output : input : clobber/modify)

clobber/modify 用于告知编译器,该内联汇编修改了哪些寄存器或者内存,让编译器做好准备。

input 和 output是为了在汇编代码里使用C变量,在其前面都可以增加约束,其基本格式为

"各种约束"(变量名)

约束很多很繁杂,但也以此实现了其强大的功能。
以下简单介绍一下所有约束:

寄存器约束

寄存器约束表示让编译器使用那个寄存器,在input中表示,这个C变量存到哪个寄存器上,在output中表示将哪个寄存器的值存到这个C变量中。

约束如下:

  • a: 表示寄存器 eax/ax/al
  • b: ebx/bx/bl
  • c: ecx/cx/cl
  • d: edx/dx/dl
  • D: edi/di
  • S: esi/si
  • q: eax/ebx/ecx/edx
  • r: eax/ebx/ecx/edx/esi/edi
  • g: 任意位置(寄存器或者内存)
  • A: eax & edx 组合成的64位
  • f: 浮点寄存器
  • t: 第一个浮点寄存器
  • u: 第二个浮点寄存器

示例程序:

#include <stdio.h>

int main()
{
    int a=1, b=2, sum;
    asm("addl %%ebx, %%eax":"=a"(sum):"a"(a),"b"(b));
    printf("%d\n",sum);
    return 0;
}

内存约束

内存约束是指要求编译器将C的变量地址作为操作数直接给汇编代码,而不是用寄存器作为中转。

内存约束使用 m即可,还有个o,不太理解

立即数约束

要求编译器在传值的时候不通过内存和寄存器,而是直接作为立即数传给汇编代码

其约束有:

  • i:整数立即数
  • F:浮点数立即数
  • I: 0~31
  • J:0~63
  • N:0~255
  • O:0~32
  • X:任意立即数

(感觉用处不大

占位符

%数字 表示,最多支持10个 0~9
表示的是output和input中依次出现的变量

asm("addl %2, %1":"=a"(sum): "a"(a),"b"(b))

%0 表示 sum,%1表示a,%2表示b

名称占位符

普通占位符数量是有限制的,为了突破其限制才有了名称占位符,(麻烦,还不如不要普通占位符

eg.

#include <stdio.h>

int main()
{
    int a = 18, b = 3, out = 0;
    asm("divb %[divisor];movb %%al, %[result]" \
            :[result]"=m"(out)             \
            :"a"(a), [divisor]"m"(b));
    printf("%d\n",out);
    return 0;
}