as语法:
——————宏
像C语言一样,as也支持宏。但是,汇编程序中所包含的头文件不能包含C语言中的函数,数据结构等内容,而只能定义常量,汇编指令等汇编器认识的内容。
宏的存在说明as在编译时存在预处理这一步骤,但是需要注意,如果所写的汇编代码中包含宏指令的话,则一定要将源文件的后缀名使用大写的S,否则as会报告说它不认识#这条指令。
——————汇编命令
汇编命令都是以“.”开头的,用于指示as如何处理我们所写的汇编代码。比如.globl命令使得跟在其后的符号在文件之外可见,或则从C语言的及哦啊杜来看,类似于定义全局函数。.align命令时告诉as后面的代码的存放地址必须满足4字节边界对齐要求。
as支持大量的汇编命令,这些命令都可以从官方手册《Using as》中找到。在这些汇编命令中有几个值得在此一提。第一批包括.text和.data命令,它们指示将其后的内容放入指定段;第二批是.lcomm和.comm,它们指示将其后的内容放入程序的.bss段中。
——————符号和标签
符号或许是程序中非常核心的概念,我们通过符号去表达变量和命令函数。
as的符号可以由字母,“.”和“_”组成,对于有的处理器还可以使用“$”
符号后面如果加上一个“:”就成了标签,标签中的符号(即去掉:)其实代表程序运行时再内存中的一个具体地址。从这一点来看,符号是有值的。标签与C语言中的函数是对等的。(其中的符号对应于C语言中的函数名)。
——————汇编指令
不同的处理器具有不同的指令集。不论是AT&T语法还是Intel语法,as都支持。可以通过.att_syntax和.intel_syntax汇编命令来切换指令格式
——————————————————————————————————————————————————
差异点 AT&T语法格式 Intel语法格式
寄存器操作数的表达 总是在寄存器名前加一个“%”
常数和立即数的表达 pushl $4 push 4
操作数的源于目的的表达 addl $4, %eax add eax, 4
操作数内存大小的表达 movb foo, %al mov al, byte ptr foo
内存引用方式 mov -4(%esp), %ecx mov ecx, [esp-4]
mov 4(%esp), %ecx mov ecx, [esp+4]
________________________________________________________________________________________
AT&T格式中操作数内存大小是通过在汇编指令后面加上一个字母来表示的。
jmp *%edx jmp中使用*表示绝对跳转,如果没有这个符号则表示相对跳转。
嵌入汇编的 语法:
例子:
static void root_frame_init (root_frame_t *_p_frame)
{
asm volatile{
//save old ESP into root_frame_t
"movl %%esp, 0x14(%0)
"
"movl %0, %%esp
"
"popl %%ebx
"
"popl %%esi
"
"popl %%edi
"
"call _root_context_init
"
"movl 8(%%esp), %%esp
"
::"r"(_p_frame):"ebx","esi","edi"
}
}
嵌入汇编的格式是:
asm volatile("汇编指令"
:输出寄存器(列表)
:输入寄存器(列表)
:保留寄存器(列表));
其中volatile用于防止编译器对嵌入式的汇编指令进行优化,它是可选的。
从例子中看的出,寄存器前面得加上"%%"前缀,这一点与非嵌入汇编是不一样的。另外,还可以看到”%0“这样奇怪的符号,后面再说。出了这两点特殊之外,嵌入的汇编指令与非嵌入的汇编指令是完全一样的。由于所有的汇编指令是写在一起的,所以必须用” “对每一条汇编指令进行分割,这一点需注意。
嵌入的汇编代码大多数情况下需要使用到C程序中定义的变量和函数参数,因此在嵌入的汇编内需要完成寄存器到 变量和参数的映射,这正是嵌入汇编格式内”输入寄存器“和”输出寄存器“的作用。
”输入寄存器“的格式必须是:
“限制”(C变量或参数 名)
其中的”限制“是一些字母,这些字母用于指明处理器的一个寄存器,这个寄存器将用于指代括号内的内容。如果有多个输入变量需要指定,则各部分需要“,”加以分割。
“输出寄存器”的格式必须是:
“=限制”(C变量或参数 名)
除了限制中多了个等号外,与输入格式是一样的。另外有与输出寄存器的值将放入其后括号内的C程序符号中,因此它必须是左值。
对于x86处理器的限制可以如下:
————————————————————————————————————————————
限制 寄存器分配含义
a 指定eax
b 指定ebx
c 指定ecx
d 指定edx
S 指定esi
D 指定edi
I 从0~31的常量
q 从eax,ebx,ecx,edx中动态分配一个
r 从esi和edi中动态分配一个
g 从eax,ebx,ecx,edx或内存变量中动态分配一个
A 指定eax和edx合成一个用于存储64位的数(long long)
————————————————————————————————————————————
当使用q,r和g这些限制时,表示有编译器自动完成寄存器的分配。那如何在嵌入的汇编中引用被编译器自动分配的寄存器呢?这得通过“%N”这样的格式加以引用的,其中N为0~9,N指示了寄存器在“输出寄存器”和“输入寄存器”列表中的索引。举个例子,如果输入和输出寄存器都有两个,%0代表第一个输出寄存器,%1代表第二个输出寄存器。
%2代表第一个输入寄存器,%3代表第二个输入寄存器。当没有输出寄存器时,%0代表第一个输入寄存器。回头最开始的例子,%0其实知道_p_frame参数的寄存器,且这个寄存器时从esi和edi中分配而来的。
“保留寄存器”用于告诉编译器不要为“输入寄存器”和“输出寄存器”分配这些寄存器。