内联汇编_AT&T语法

AT&T汇编基本语法

对比intel格式的汇编

* 寄存器命名原则
    AT&T: %eax                      Intel: eax
* 源/目的操作数顺序 
    AT&T: movl %eax, %ebx           Intel: mov ebx, eax
* 常数/立即数的格式 
    AT&T: movl $_value, %ebx        Intel: mov eax, _value
  把value的地址放入eax寄存器
    AT&T: movl $0xd00d, %ebx        Intel: mov ebx, 0xd00d
* 操作数长度标识 
    AT&T: movw %ax, %bx             Intel: mov bx, ax
* 寻址方式 
    AT&T:   immed32(basepointer, indexpointer, indexscale)
    Intel:  [basepointer + indexpointer × indexscale + imm32)

实例说明

* 直接寻址 
        AT&T:  foo                         Intel: [foo]
        boo是一个全局变量。注意加上$是表示地址引用,不加是表示值引用。对于局部变量,可以通过堆栈指针引用。

* 寄存器间接寻址 
        AT&T: (%eax)                        Intel: [eax]

* 变址寻址 
        AT&T: _variable(%eax)               Intel: [eax + _variable]
        AT&T: _array( ,%eax, 4)             Intel: [eax × 4 + _array]
        AT&T: _array(%ebx, %eax,8)          Intel: [ebx + eax × 8 + _array]

GCC扩展内联汇编

基本格式

asm [volatile] ( Assembler Template
: Output Operands
[ : Input Operands
[ : Clobbers ] ])

格式说明

  • __asm__表示汇编代码的开始,其后可以跟__volatile__(这是可选项),其含义是避免 “asm” 指令被删除、移动或组合,在执行代码时,如果不希望汇编语句被 gcc 优化而改变位置,就需要在 asm 符号后添加 volatile 关键词:asm volatile(…),或者更详细地说明为:__asm__ __volatile__(...)

  • 小括弧(),括弧中的内容是具体的内联汇编指令代码。 "" 为汇编指令部分,例如,“movl %%cr0,%0\n\t”。

  • 数字前加前缀 ““,如%1,%2等表示使用寄存器的样板操作数。可以使用的操作数总数取决于具体CPU中通用寄存器的数 量,如Intel可以有8个。指令中有几个操作数,就说明有几个变量需要与寄存器结合,由gcc在编译时根据后面输出部分输入部分约束条件进行相应的处理。 由于这些样板操作数的前缀使用了”%“,因此,在用到具体的寄存器时就在前面加两个“%”,如%%cr0(相当多一个表示转义功能)。

  • 输出部分(output operand list),用以规定对输出变量(目标操作数)如何与寄存器结合的约束(constraint),输出部分可以有多个约束,互相以逗号分开。每个约束以“=”开头,接着用一个字母来表示操作数的类型,然后是关于变量结合的约束。

  • 输入部分(input operand list):输入部分与输出部分相似,但没有“=”。如果输入部分一个操作数所要求使用的寄存器,与前面输出部分某个约束所要求的是同一个寄存器,那就把对应操作数的编号(如“1”,“2”等)放在约束条件中。 在后面的例子中,可看到这种情况。

  • 修改部分(clobber list,也称 乱码列表):这部分常常以“memory”为约束条件,以表示操作完成后内存中的内容已有改变,如果原来某个寄存器的内容来自内存,那么现在内存中这个单元的内容已经改变。乱码列表通知编译器,有些寄存器或内存因内联汇编块造成乱码,可隐式地破坏了条件寄存器的某些位(字段)。

注意: 指令部分为必选项,而输入部分、输出部分及修改部分为可选项,当输入部分存在,而输出部分不存在时,冒号“:”要保留,当“memory”存在时,三个冒号都要保留,例如

#define __cli() __asm__ __volatile__("cli": : :"memory")

实例一说明

	#define read_cr0() ({ \
	unsigned int __dummy; \
	__asm__( \
    "movl %%cr0,%0\n\t" \
    :"=r" (__dummy)); \
    __dummy; \
    })
 :"=r" (__dummy)

“=r”表示相应的目标操作数(指令部分的%0)可以使用任何一个通用寄存器,并且变量__dummy 存放在这个寄存器中,但如果是:

:“=m”(__dummy)

“=m”就表示相应的目标操作数是存放在内存单元__dummy中。表示约束条件的字母很多,下表给出几个主要的约束字母及其含义:

字母含义
m, v, o内存单元
R任何通用寄存器
Q寄存器eax, ebx, ecx,edx之一
I, h直接操作数
E, F浮点数
G任意
a, b, c, d寄存器eax/ax/al, ebx/bx/bl, ecx/cx/cl或edx/dx/dl
S, D寄存器esi或edi
I常数(0~31)

实例二说明

源文件test.cpp

 int main(int argc, char* argv[])
      {
              long __res, arg1 = 2,arg2 = 3, arg3 = 4, arg4 = 5;
      __asm__ volatile("int $0x80"
                      : "=a"(__res)
                      : "0"(11), "b"(arg1), "c"(arg2), "d"(arg3), "S"(arg4));
      
      return 0;
      }

完成编译执行下面指令

gcc test.cpp -o test.exe -m32

查看反汇编代码
objdump -d test.exe > test.s
less test.s查看得到main函数对应代码如下
在这里插入图片描述

实例详解注释

uint16_t reg1, reg2, reg3, reg4;
asm volatile ( 
"mov %%cs, %0;" 
"mov %%ds, %1;" "mov %%es, %2;" "mov %%ss, %3;" : "=m"(reg1), "=m"(reg2), "=m"(reg3), "=m"(reg4));

注解上述代码完成功能:将cs ds es ss 四个段寄存器的值赋值给reg1, reg2,reg3, reg4.
并建议编译器将这四个变量放到内存(栈)中。

关于CFI directives系列

CFI directives

补充

[1] 使用q指示编译器从eax, ebx, ecx, edx分配寄存器。 使用r指示编译器从eax, ebx, ecx, edx, esi, edi分配寄存器。
[2] 不必把编译器分配的寄存器放入改变的寄存器列表,因为寄存器已经记住了它们。
[3] "="是标示输出寄存器,必须这样用。
[4] 数字%n的用法:数字表示的寄存器是按照出现和从左到右的顺序映射到用"r"或"q"请求的寄存器.如果要重用"r"或"q"请求的寄存器的话,就可以使用它们。
[5] 如果强制使用固定的寄存器的话,如不用%1,而用ebx,则:

asm("leal (%%ebx,%%ebx,4),%0"
: "=r" (x)
: "0" (x) 
);

注:要使用两个%,因为一个%的语法已经被%n用掉了(注:n-number,多一个%是为了区分具体寄存器和数字表示)。

关于(=a)用法

在嵌入汇编的文档中找”=a“的含义,就可以明白了。它有两个含义,一是调用前把输入的系统调用号放到eax中,二是调用后把返回值放从eax中读出来,其他类推。

实例:

static int kernel_execve(const char *name, unsigned char *binary, size_t size) {
    int ret, len = strlen(name);
    asm volatile (
        "int %1;"
        : "=a" (ret)
        : "i" (T_SYSCALL), "0" (SYS_exec), "d" (name), "c" (len), "b" (binary), "D" (size)
        : "memory");
    return ret;
}

我们看到kernel_execve通过内联汇编 int SYS_exec的形式进行了系统调用(=a再此处即两种作用),同时传入了四个参数:

name:即将执行的用户进程名
len:用户进程名的长度
binary:用户程序的二进制代码起始地址
size:用户程序的二进制代码的大小

资料手册

GCC-Inline-Assembly-HOWTO

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值