att汇编格式

调用约定

1

cdecl调用方式:

  1. 参数从右往左入栈
  2. 调用者保存eax,ecx,edx寄存器,其余寄存器由被调用者(函数)来保存
  3. 函数返回值保存在eax寄存器中

内联汇编

Intel和At&t的风格对比:

区别 Intel At&t 说明
寄存器 寄存器前有前缀%
操作数顺序 a<-b a->b 恰好相反
操作数字体指定大小 +数据类型用来修饰:byte,word,dword,如:mov byte[0x1234],eax 指令的最后一个字母表示操作树的大小,b,w,l,如:movl %eax,var att语法,内存地址是第一位,数字默认是地址,所以操作数字就是操作内存,Intel语法数字就是数字,加上[]才是操作内存,
立即数 6 有前缀$,$6
远转跳 jmp far segment:offset ljmp $segment:$offset
远调用 call far segment:offset lcall $segment:$offset
远返回 ret far n lret $n

att内存寻址通用格式:

segreg(段基址):base_address(offset_address,index,size)

对应的值是: base_address+offset_address+index*size

等价与Intel格式: segreg:[base+offset+index*size]

base_address表示基地址,可正负

offset_address表示偏移地址,index表示索引值(这2个必须是8个通用寄存器)

size:表示长度,值只能取: 1,2,4,8

att寻址方式:

  1. 直接寻址(只有base_address项),如: movl $255,0xc00008f0

  2. 寄存器间接寻址(只有offset_address项,所以格式为(offset_address)): 如: movl (%eax),%ebx

  3. 寄存器相对址(有offset_address和base_address项,格式为base_address(offset_address) ),如: movb -4(%ebx),%al,将地址(ebx-4)的1 byte字节复制到al寄存器中

  4. 变址寻址(含有index,既然有index则有size,base_address和offset_address可有可无,):

    1. base_address,无offset_address

      movl %eax,(,%esi,2):将eax的值写入地址esi*2处

    2. base_address,有offset_address

      movl %eax,(%ebx,%esi,2):将eax的值写入ebx+esi*2处

    3. base_address,无offset_address

      movl %eax,base_value(,%esi,2):将eax的值写入base_value+esi*2处

    4. base_address,有offset_address

      movl %eax,base_value(%ebx,%esi,2):将eax的值写入base_value+ebx+esi*2处


基本内联汇编

#define __asm__ asm#define __volatile__ volatile,所以直接用asm和volatile就行

基本格式asm [volatile] ("assembly code");

  1. 指令用""括起来,
  2. 指令之间用;或者\n\n\t

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
char *str="hello,world\n";
int count=0;

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

没什么需要注意的,此时基本内敛汇编引用C变量只能引用全局变量,在扩展中可以随意使用

x64下编译成32bit 可执行:gcc -m32 test.s

扩展内联汇编

问题:

  1. C和内联汇编对寄存器的使用冲突
  2. 汇编如何访问C栈上的变量

按照格式提供汇编要访问的变量给编译器看(gcc制订的规则)

asm [volatile] ("assembly code":output:input:modify)

  1. output:output指定汇编代码执行后如何将数据传递给C代码使用,格式:"操作数修饰符 约束名"(c变量名)
  2. input:input,指定C中的变量如何传递给汇编代码使用.[操作数修饰符] 约束名(C变量名)
  3. modify:通知编译器那些寄存器可能被破坏
  • 寄存器约束

    将input或output中的变量约束在某个寄存器中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    a: eax/ax/al
    b: ebx/bx/bl
    c: ecx/cx/cl
    d: edx/dx/dl
    D: edi/di
    S: esi/si
    q: 任意4个通用寄存器:eax/ebx/ecx/edx
    r: 任意6个通用寄存器:eax/ebc/ecx/edx/esi/edi
    g: q+内存
    A: eax和edx组成的64bit整数
    f: 浮点寄存器
    t: 第一个浮点寄存器
    u: 第2个浮点寄存器

    例(对比基本内联汇编):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #include <stdio.h>
    #define ps(str) printf("%s",str);

    char *str="hello,world\n";
    int count=0;

    int in_a=1,in_b=2,out_sum;
    int main()
    {
    asm("pusha;\
    movl in_a,%eax;\
    movl in_b,%ebx;\
    addl %ebx,%eax;\
    movl %eax,out_sum;\
    popa");
    printf("sum is %d",out_sum);

    in_a=16;
    in_b=22;
    asm("addl %%ebx,%%eax":"=a"(out_sum):"a"(in_a),"b"(in_b));
    printf("sum is %d",out_sum);
    return 0;
    }

    方便,并且由gcc完成将input赋值到寄存器中和寄存器赋值到output中

    注意: %在扩展内联汇编中留给占位符用,寄存器使用2个%,既%%ebx

  • 内存约束

    gcc将input和output中的C变量的地址作为操作数,直接进行内存读写,

    1
    2
    m: 表示操作数可以使用任意一种内存形式
    o: 操作数为内存变量,但是访问是通过偏移量的形式(包含offset_address的格式),

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include <stdio.h>
    #define ps(str) printf("%s",str);

    int main()
    {
    int in_a=1,in_b=2;
    printf("in_b is %d\n",in_b);
    asm("movb %b0,%1;"::"a"(in_a),"m"(in_b));
    printf("in_b is %d\n",in_b);
    return 0;
    }
  • 立即数约束

    gcc在传值的时候不通过内存和寄存器,直接将立即数传递给汇编代码,

    1
    2
    3
    4
    5
    6
    7
    i: 表示操作数为整数立即数
    F: 浮点立即数
    I: 0-31之间的立即数
    J: 0-63之间的立即数
    N: 0-255之间的立即数
    O: 0-32之间的立即数
    X: 操作数为任何类型的立即数
  • 通用约束


  1. 序号占位符
  1. 名称占位符

    [名称] "[操作类型]约束名" (C变量)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <stdio.h>
    int main()
    {
    int in_a=1;
    int in_b=2;
    int out;
    asm("addl %%ebx,%%eax;"
    "movl %%eax,%[result]":"+a"(in_a):"b"(in_b),[result]"m"(out));
    // in_a和out都是结果
    // +表示既作为输入又作为输出结果
    printf("in_a is %d\nout is %d\n",in_a,out);
    }

操作数类型修饰

output中:

  1. =:表示操作数只写,如: =a(var),等价与var=eax
  2. +:表示操作数是可读写的,寄存器或内存前被读入,然后被写入
  3. &:表示output的操作数要独占被约束的寄存器, input中约束的寄存器不能与此相同

input:

  1. %:此操作数可以和下一个操作数互换

+例子:

1
2
3
4
5
6
7
#include <stdio.h>
int main()
{
int in_a=1,in_b=2;
asm("addl %%ebx,%%eax;":"+a"(in_a):"b"(in_b));
printf("in_a is %d",in_a);
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!