gnu汇编语言使用内联汇编 扩展asm

Extended ASM format 扩展的asm格式:html

asm关键字的扩展格式以下:数组

asm ("assembly code" : output operands : input operands : changed registers);

上面的扩展格式由四个部分组成,每一个部分之间使用冒号隔开:函数

Assembly code: The inline assembly code using the same syntax used for the basic asm format
assembly code:即汇编代码部分,能够在该部分编写所需的内联汇编代码,和基本asm格式相比,扩展格式里的汇编代码除了在百分号的使用上有所不一样外(下面会介绍),其余的汇编指令的书写格式都是差很少的
Output operands: A list of registers and memory locations that will contain the output values
from the inline assembly code
输出操做数:用于指定内联汇编代码的执行结果须要输出到哪一个寄存器,以及须要输出到哪一个C变量里
Input operands: A list of registers and memory locations that contain input values for the inline
assembly code
输入操做数:能够将C变量的值输入到指定的寄存器,这样就能够在内联汇编代码里,经过这些寄存器来使用到C变量的值了,这里的C变量既能够是全局变量,也能够是局部变量
Changed registers: A list of any additional registers that are changed by the inline code
被修改的寄存器:用于指明在内联汇编代码里修改了哪些寄存器,编译器会根据实际状况,对这些修改的寄存器进行一些push、pop之类的操做,这样,汇编代码修改的寄存器就不会影响到外面C代码的正常执行
并不是每一个部分都必须进行设置,假设你不须要第二个输出操做数,那么输出操做数部分就能够留空,不过须要注意的是,即使输出操做数留空,开头的两个冒号也必须保留,如:asm (“assembly code” : : input operands : changed registers);的格式,另外,若是不须要设置被修改的寄存器部分,那么最后一个冒号能够省略,如:asm (“assembly code” : output locations : input operands);的格式。









.net

下面就介绍下扩展asm格式里输入和输出操做数的具体格式。

Specifying input and output values 输入和输出操做数:code

输入和输出操做数的格式以下:

“constraint”(variable)
其中,括号里的variable表示C代码里定义的全局变量或局部变量,而引号里的constraint(约束)则表示用于存储variable变量值的寄存器或内存位置。对于输入操做数,constraint就是将variable变量的值读取到指定的寄存器,或者直接使用变量的内存位置。对于输出操做数,constraint就是先将结果存储到指定的寄存器,再将寄存器里的值设置到variable变量里,某些constraint(约束)也能够直接将结果写入到变量所在的内存里。
orm

constraint(约束)是一个单一的字符,可用的约束符以下表所示:

Constraint
约束符 Description
描述
a Use the %eax, %ax, or %al registers.
使用%eax, %ax或%al寄存器
b Use the %ebx, %bx, or %bl registers.
使用%ebx, %bx或%bl寄存器
c Use the %ecx, %cx, or %cl registers.
使用%ecx, %cx或%cl寄存器
d Use the %edx, %dx, or $dl registers.
使用%edx, %dx或%dl寄存器
S Use the %esi or %si registers.
使用%esi或%si寄存器
D Use the %edi or %di registers.
使用%edi或%di寄存器
r Use any available general-purpose register.
使用任何可用的通用寄存器
q Use either the %eax, %ebx, %ecx, or %edx register.
使用%eax, %ebx, %ecx或%edx寄存器
A Use the %eax and the %edx registers
for a 64-bit value.
使用%eax和%edx寄存器来存储一个64位的值
f Use a floating-point register.
使用一个浮点寄存器
t Use the first (top) floating-point register.
使用第一个ST0浮点寄存器
u Use the second floating-point register.
使用第二个ST1浮点寄存器
m Use the variable’s memory location.
使用变量的内存位置
o Use an offset memory location.
使用一个带有偏移值的内存位置,例如:使用4(%edi)来表示EDI加偏移值4的内存地址,多用于数组元素的访问
V Use only a direct memory location.
使用不带偏移值的内存位置
i Use an immediate integer value.
表示操做数是一个当即数(常量整数值)
n Use an immediate integer value with a known value.
表示操做数是一个已知数值的当即数,在许多系统里,当操做数小于1个word(字)宽时,就不能使用i,而应使用n来表示当即数
g Use any register or memory location available.
能够使用任一通用寄存器或内存位置,或者操做数是一个当即数
除了上面的这些约束外,对于操做数还包含一个constraint modifier(约束修饰符),用于表示操做数是否可读写之类的,可用的约束修饰符以下表所示:







































htm

Constraint modifier
约束修饰符 Description
描述

blog

  • The operand can be both read from
    and written to.
    说明操做数是可读写的,例如"+r",指的是在内联汇编指令开始前,必须先将变量的值赋值给对应的寄存器(也就是读的过程),在执行完汇编指令后,再将寄存器的值写入变量(即写的过程)
    = The operand can only be written to.
    说明操做数是只写的,例如"=r",就是说,只需将结果写入变量,至于内联汇编指令执行前,对应寄存器里的值则能够是任意值,无需在执行前读取变量的值。
    % The operand can be switched with the
    next operand if necessary.
    在必要时,操做数能够和下一个操做数进行交换,下面会举例说明
    & use ‘&’ for each output operand that may
    not overlap an input
    &修饰符是用来强制编译器为输入操做数与输出操做数分配不一样的寄存器
    能够参考
    http://blog.csdn.net/bokee/article/details/7029353
    该连接里的文章
    上面的约束修饰符里"+"、"="、"&“这三个只能用于修饰输出操做数,不能用于修饰输入操做数,而”%“则恰好相反,只能用于修饰输入操做数,”%"的用法能够参考下面的代码:













int main(int __argc, char* __argv[])
{
int __in1 = 8, __in2 = 4, __out = 3;
asm (“addl %1, %0\n\t”
: “=r”(__out)
: “%r” (__in1), “0” (__in2));
return 0;
}
在此例中,因为指令是一个加法运算,至关于等式__out = __in1 + __in2,而它与等式__out = __in2 + __in1没有什么不一样。因此使用百分号修饰,让GCC知道__in1和__in2能够互换,也就是说GCC能够自动将本例的内联汇编改变为:







ip

asm (“addl %1, %0\n\t”

“=r”(__out)内存

“%r” (__in2), “0” (__in1));
上面的代码例子来源于:http://tieba.baidu.com/p/310375553 ,另外上面例子里的%1和%0等是占位符,占位符的含义会在下面的介绍里进行说明。

用的较多的是第一个"+“和第二个”="修饰符,例以下面的代码模板:

asm (“assembly code” : “=a”(result) : “d”(data1), “c”(data2));
上面的代码模板,表示将data1变量的值读取到EDX寄存器,将data2变量的值读取到ECX寄存器,内联汇编代码在执行完后,会将结果保存到EAX寄存器,最后,EAX里的值会写入到result变量里,这里必需要在"a"约束前面添加"="修饰符,表示result操做数能够写入数据,不然,因为在没有修饰符的状况,该操做数表示只读,GCC编译器就会报"output operand constraint lacks ‘=’“的错误,即输出操做数的约束缺乏”=“修饰符。固然也能够使用”+"修饰符来表示输出操做数可读写。

Using registers 在扩展asm格式里使用寄存器:

在上一篇文章里介绍的基本asm内联汇编格式里,寄存器的使用方式和在单独的汇编文件里的使用方式同样,只须要在寄存器名称前加一个百分号便可,可是在本章介绍的扩展asm格式里,内联汇编代码里要使用寄存器时,必须在寄存器的名称前加两个百分号,例如:%%eax 这样的形式,第一个百分号用于对第二个百分号进行转义,这样两个百分号就能够表示一个普通的百分号来修饰eax,之因此要这么作,是由于%在扩展asm格式里有特殊的用途,就像C语言字符串里的"\n"字符里的"\"字符用于和"n"字符一块儿来表示特殊的换行符同样,单个百分号用于和数字一块儿来表示下面会提到的占位符。

下面的regtest1.c程式就演示了如何在扩展的asm格式里使用寄存器:
/* regtest1.c – An example of using registers */
#include <stdio.h>

int main()
{
	int data1 = 10;
	int data2 = 20;
	int result;
	asm ("imull %%edx, %%ecx\n\t"
	    "movl %%ecx, %%eax"
	    : "=a"(result)
	    : "d"(data1), "c"(data2));
	printf("The result is %d\n", result);
	return 0;
}

上面的代码里,在将data1和data2局部变量的值分别加载到EDX和ECX后,在asm的汇编代码里,就会经过imull %%edx, %%ecx指令将EDX和ECX寄存器的值相乘,计算结果存储到ECX里,而后经过movl %%ecx, %%eax指令将ECX里的值输出到EAX里,最后EAX里的值会写入到result局部变量里,这样在随后的C代码里就能够使用printf函数来将result的值给显示出来。这里须要注意寄存器的写法,如:%%ecx等都有两个百分号,缘由在上面已经解释了。

参考文章: https://www.zengl.com/a/201404/146.html

相关文章
相关标签/搜索