C语言ASM汇编内嵌语法详解

2020-01-18 19:38:27于丽

3. Operation Constraint

每一个Input和Output表达式都必须指定自己的操作约束Operation Constraint,我们这里来讨论在80386平台上所可能使用的操作约束。

1、寄存器约束

当你当前的输入或输入需要借助一个寄存器时,你需要为其指定一个寄存器约束。你可以直接指定一个寄存器的名字,比如:

__asm__ __volatile__("movl %0, %%cr0"::"eax" (cr0));

也可以指定一个缩写,比如:

__asm__ __volatile__("movl %0, %%cr0"::"a" (cr0));

如果你指定一个缩写,比如字母a,则GCC将会根据当前操作表达式中C/C++表达式的宽度决定使用%eax,还是%ax或%al。比如:


unsigned short __shrt;
__asm__ ("mov %0,%%bx" : : "a"(__shrt));

由于变量__shrt是16-bit short类型,则编译出来的汇编代码中,则会让此变量使用%ex寄存器。编译结果为:


movw -2(%ebp), %ax # %ax = __shrt
#APP
movl %ax, %bx
#NO_APP

无论是Input,还是Output操作表达式约束,都可以使用寄存器约束。

下表中列出了常用的寄存器约束的缩写。

约束 Input/Output 意义

r I,O 表示使用一个通用寄存器,由GCC在%eax/%ax/%al, %ebx/%bx/%bl, %ecx/%cx/%cl, %edx/%dx/%dl中选取一个GCC认为合适的。

q I,O 表示使用一个通用寄存器,和r的意义相同。 a I,O 表示使用%eax / %ax / %al b I,O 表示使用%ebx / %bx / %bl c I,O 表示使用%ecx / %cx / %cl d I,O 表示使用%edx / %dx / %dl D I,O 表示使用%edi / %di S I,O 表示使用%esi / %si f I,O 表示使用浮点寄存器 t I,O 表示使用第一个浮点寄存器 u I,O 表示使用第二个浮点寄存器

2、内存约束

如果一个Input/Output操作表达式的C/C++表达式表现为一个内存地址,不想借助于任何寄存器,则可以使用内存约束。

比如:

__asm__ ("lidt %0" : "=m"(__idt_addr)); 或 __asm__ ("lidt %0" : :"m"(__idt_addr));

我们看一下它们分别被放在一个C源文件中,然后被GCC编译后的结果:


$ cat example5.c

// 本例中,变量sh被作为一个内存输入

int main(int __argc, char* __argv[]) 
{ 
char* sh = (char*)&__argc; 
__asm__ __volatile__("lidt %0" : : "m" (sh)); 

return 0; 
} 
$ gcc -S example5.c

$ cat example5.s

main: 
pushl %ebp 
movl %esp, %ebp 
subl $4, %esp 
leal 8(%ebp), %eax 
movl %eax, -4(%ebp) # sh = (char*) &__argc
#APP 
lidt -4(%ebp) 
#NO_APP 
movl $0, %eax 
leave 
ret 
$ cat example6.c

// 本例中,变量sh被作为一个内存输出

int main(int __argc, char* __argv[]) 
{ 
char* sh = (char*)&__argc; 
__asm__ __volatile__("lidt %0" : "=m" (sh)); 

return 0; 
} 
$ gcc -S example6.c
$ cat example6.s
main:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
leal 8(%ebp), %eax
movl %eax, -4(%ebp) # sh = (char*) &__argc
#APP
lidt -4(%ebp)
#NO_APP
movl $0, %eax
leave
ret