关于AT&T汇编

时间:2022-04-01 13:39:31
小弟现在在学Linux源代码,但有很多AT&T格式的汇编,请问哪位大哥有教程一类的东东呀,给小弟发一份,谢了

6 个解决方案

#1


以下部分是从网上找的,希望对你有用。

三.AT&T的汇编语言语法格式 
我想我们大部分人对Intel格式的汇编语言都很了解了。但是,在 
Linux核心代码中,所有的汇编语言指令都是用AT&T格式的汇编语 
言书写的。这两种汇编语言在语法格式上有着很大的不同: 

1.在AT&T的汇编语言中,用'$'前缀表示一个立即操作数;而在Intel 
的格式中,立即操作数的表示不带任何前缀符。例如:下面两个语句 
是完全相同的: 
*AT&T: pushl $4 
*Intel: push 4 

2.AT&T和Intel的汇编语言格式中,源操作数和目标操作数的位置正 
好相反。Intel的汇编语言中,目标操作数在源操作数的左边;而在 
AT&T的汇编语言中,目标操作数则在源操作数的右边。例如: 
*AT&T : addl $4,%eax 
*Intel: add eax,4 

3.在AT&T的汇编语言中,操作数的字长是由操作码助记符的最后一个 
字母决定的,后缀'b'、'w'、'l'分别表示操作数的字长为8比特(字 
节,byte),16比特(字,word)和32比特(长字,long),而 
Intel格式中操作数的字长是用“word ptr”或者“byte ptr”等前 
缀来表示的。例如: 
*AT&T: movb FOO,%al 
*Intel: mov al,byte ptr FOO 

4.在AT&T汇编指令中,直接远跳转/调用的指令格式是“lcall/ljmp 
$SECTION,$OFFSET”,同样,远程返回的指令是“lret 
$STACK-ADJUST”;而在Intel格式中,相应的指令分别为“call/jmp 
far SECTION:OFFSET”和“ret far STACK-ADJUST”。 

①AT&T汇编指令操作助记符命名规则 
AT&T汇编语言中,操作码助记符的后缀字符指定了该指令中操作数的 
字长。后缀字母'b'、'w'、'l'分别表示字长为8比特(字节,byte), 
16比特(字,word)和32比特(长字,long)的操作数。如果助记符 
中没有指定字长后缀并且该指令中没有内存操作数,汇编程序'as'会 
根据指令中指定的寄存器操作数补上相应的后缀字符。所以,下面的 
两个指令具有相同的效果(这只是GNU的汇编程序as的一个特性,AT&T 
的Unix汇编程序将没有字长后缀的指令的操作数字长假设为32比特): 

mov %ax,%bx 

movw %ax,%bx 

AT&T中几乎所有的操作助记符与Intel格式中的助记符同名,仅有一 
小部分例外。操作数扩展指令就是例外之一。在AT&T汇编指令中,操 
作数扩展指令有两个后缀:一个指定源操作数的字长,另一个指定目 
标操作数的字长。AT&T的符号扩展指令的基本助记符为'movs',零扩 
展指令的基本助记符为'movz'(相应的Intel指令为'movsx'和 
'movzx')。因此,'movsbl %al,%edx'表示对寄存器al中的字节数据 
进行字节到长字的符号扩展,计算结果存放在寄存器edx中。下面是一 
些允许的操作数扩展后缀: 
*bl: 字节->长字 
*bw: 字节->字 
*wl: 字->长字 
还有一些其他的类型转换指令的对应关系: 

*Intel *AT&T 
⑴ cbw cbtw 
符号扩展:al->ax 
⑵ cwde cwtl 
符号扩展:ax->eax 
⑶ cwd cwtd 
符号扩展:ax->dx:ax 
⑷ cdq cltd 
符号扩展:eax->edx:eax 

还有一个不同名的助记符就是远程跳转/调用指令。在Intel格式中, 
远程跳转/调用指令的助记符为“call/jmp far”,而在AT&T的汇编 
语言中,相应的指令为“lcall”和“ljmp”。 

②AT&T中寄存器的命名 
在AT&T汇编语言中,寄存器操作数总是以'%'作为前缀。80386芯片的 
寄存器包括: 
⑴8个32位寄存器:'%eax','%ebx','%ecx','%edx','%edi','%esi', 
'%ebp','%esp' 
⑵8个16位寄存器:'%ax','%bx','%cx','%dx','%si','%di','%bp', 
'%sp' 
⑶8个8位寄存器:'%ah','%al','%bh','%bl','%ch','%cl','%dh', 
'%dl' 
⑷6个段寄存器:'%cs','%ds','%es','%ss','%fs','%gs' 
⑸3个控制寄存器:'%cr0','%cr1','%cr2' 
⑹6个调试寄存器:'%db0','%db1','%db2','%db3','%db6','%db7' 
⑺2个测试寄存器:'%tr6','%tr7' 
⑻8个浮点寄存器栈:'%st(0)','%st(1)','%st(2)','%st(3)', 
'%st(4)','%st(5)','%st(6)','%st(7)' 

*注:我对这些寄存器并不是都了解,这些资料只是摘自as.info文档。 
如果真的需要寄存器命名的资料,我想可以参考一下相应GNU工具的机 
器描述方面的源文件。 

③AT&T中的操作码前缀 
⑴段超越前缀'cs','ds','es','ss','fs','gs':当汇编程序中对内 
存操作数进行SECTION:MEMORY-OPERAND引用时,自动加上相应的段超 
越前缀。 
⑵操作数/地址尺寸前缀'data16','addr16':这些前缀将32位的操作 
数/地址转化为16位的操作数/地址。 
⑶总线锁定前缀'lock':总线锁定操作。'lock'前缀在Linux核心代码 
中使用很多,特别是SMP代码中。 
⑷协处理器等待前缀'wait':等待协处理器完成当前操作。 
⑸指令重复前缀'rep','repe','repne':在串操作中重复指令的执行。 

④AT&T中的内存操作数 
在Intel的汇编语言中,内存操作数引用的格式如下: 

SECTION:[BASE + INDEX*SCALE + DISP] 
而在AT&T的汇编语言中,内存操作数的应用格式则是这样的: 

SECTION:DISP(BASE,INDEX,SCALE) 

下面是一些内存操作数的例子: 

*AT&T *Intel 
⑴ -4(%ebp) [ebp-4] 
⑵ foo(,%eax,4) [foo+eax*4] 
⑶ foo(,1) [foo] 
⑷ %gs:foo gs:foo 

还有,绝对跳转/调用指令中的内存操作数必须以'*'最为前缀,否则 
as总是假设这是一个相对跳转/调用指令。 

⑤AT&T中的跳转指令 
as汇编程序自动对跳转指令进行优化,总是使用尽可能小的跳转偏移 
量。如果8比特的偏移量无法满足要求的话,as会使用一个32位的偏 
移量,as汇编程序暂时还不支持16位的跳转偏移量,所以对跳转指令 
使用'addr16'前缀是无效的。 

还有一些跳转指令只支持8位的跳转偏移量,这些指令包括:'jcxz', 
'jecxz','loop','loopz','loope','loopnz'和'loopne'。所以, 
在as的汇编源程序中使用这些指令可能会出错。(幸运的是,gcc并 
不使用这些指令) 

对AT&T汇编语言语法的简单介绍差不多了,其中有些特性是as特有的。 
在Linux核心代码中,并不涉及到所有上面这些提到的语法规则,其 
中有两点规则特别重要:第一,as中对寄存器引用时使用前缀'%';第 
二,AT&T汇编语言中源操作数和目标操作数的位置与我们熟悉的Intel 
的语法正好相反。 

#2


下面是关于gcc行内汇编的。

四.gcc的内嵌汇编语言语句asm 
利用gcc的asm语句,你可以在C语言代码中直接嵌入汇编语言指令, 
同时还可以使用C语言的表达式指定汇编指令所用到的操作数。这一 
特性提供了很大的方便。 

要使用这一特性,首先要写一个汇编指令的模板(这种模板有点类似 
于机器描述文件中的指令模板),然后要为每一个操作数指定一个限 
定字符串。例如: 
extern __inline__ void change_bit(int nr,volatile void *addr) 


__asm__ __volatile__( LOCK_PREFIX 

"btcl %1,%0" 

:"=m" (ADDR) 

:"ir" (nr)); 

上面的函数中: 

LOCK_PREFIX:这是一个宏,如果定义了__SMP__,扩展为"lock;", 
用于指定总线锁定前缀,否则扩展为""。 

ADDR:这也是一个宏,定义为(*(volatile struct __dummy *) addr) 

"btcl %1,%0":这就是嵌入的汇编语言指令,btcl为指令操作码,%1, 
%0是这条指令两个操作数的占位符。后面的两个限定字符串就用于描 
述这两个操作数。 

: "=m" (ADDR):第一个冒号后的限定字符串用于描述指令中的“输 
出”操作数。刮号中的ADDR将操作数与C语言的变量联系起来。这个 
限定字符串表示指令中的“%0”就是addr指针指向的内存操作数。这 
是一个“输出”类型的内存操作数。 

: "ir" (nr):第二个冒号后的限定字符串用于描述指令中的“输入” 
操作数。这条限定字符串表示指令中的“%1”就是变量nr,这个的操 
作数可以是一个立即操作数或者是一个寄存器操作数。 

*注:限定字符串与操作数占位符之间的对应关系是这样的:在所有 
限定字符串中(包括第一个冒号后的以及第二个冒号后的所有限定字 
符串),最先出现的字符串用于描述操作数“%0”,第二个出现的字 
符串描述操作数“%1”,以此类推。 

①汇编指令模板 
asm语句中的汇编指令模板主要由汇编指令序列和限定字符串组成。 
在一个asm语句中可以包括多条汇编指令。汇编指令序列中使用操作 
数占位符引用C语言中的变量。一条asm语句中最多可以包含十个操 
作数占位符:%0,%1,...,%9。汇编指令序列后面是操作数限定字 
符串,对指令序列中的占位符进行限定。限定的内容包括:该占位符 
与哪个C语言变量对应,可以是什么类型的操作数等等。限定字符串 
可以分为三个部分:输出操作数限定字符串(指令序列后第一个冒号 
后的限定字符串),输入操作数限定字符串(第一个冒号与第二个冒 
号之间),还有第三种类型的限定字符串在第二个冒号之后。同一种 
类型的限定字符串之间用逗号间隔。asm语句中出现的第一个限定字 
符串用于描述占位符“%0”,第二个用于描述占位符“%1”,以此类 
推(不管该限定字符串的类型)。如果指令序列中没有任何输出操作 
数,那么在语句中出现的第一个限定字符串(该字符串用于描述输入 
操作数)之前应该有两个冒号(这样,编译器就知道指令中没有输出 
操作数)。 

指令中的输出操作数对应的C语言变量应该具有左值类型,当然对于 
输出操作数没有这种左值限制。 

输出操作数必须是只写的,也就是说,asm对取出某个操作数,执行 
一定计算以后再将结果存回该操作数这种类型的汇编指令的支持不是 
直接的,而必须通过特定的格式的说明。如果汇编指令中包含了一个 
输入-输出类型的操作数,那么在模板中必须用两个占位符对该操作 
数的不同功能进行引用:一个负责输入,另一个负责输出。例如: 

asm ("addl %2,%0":"=r"(foo):"0"(foo),"g"(bar)); 
在上面这条指令中,“%0”是一个输入-输出类型的操作数,"=r"(foo) 
用于限定其输出功能,该指令的输出结果会存放到C语言变量foo中; 
指令中没有显式的出现“%1”操作数,但是针对它有一个限定字符串 
"0"(foo),事实上指令中隐式的“%1”操作数用于描述“%0”操作数 
的输入功能,它的限定字符串中的"0"限定了“%1”操作数与“%0” 
具有相同的地址。可以这样理解上述指令中的模板:该指令将“%1” 
和“%2”中的值相加,计算结果存放回“%0”中,指令中的“%1”与 
“%0”具有相同的地址。注意,用于描述“%1”的"0"限定字符足以 
保证“%1”与“%0”具有相同的地址。但是,如果用下面的指令完成 
这种输入-输出操作就不会正常工作: 

asm ("addl %2,%0":"=r"(foo):"r"(foo),"g"(bar)); 
虽然该指令中“%0”和“%1”同样引用了C语言变量foo,但是gcc并 
不保证在生成的汇编程序中它们具有相同的地址。 

还有一些汇编指令可能会改变某些寄存器的值,相应的汇编指令模板 
中必须将这种情况通知编译器。所以在模板中还有第三种类型的限定 
字符串,它们跟在输入操作数限定字符串的后面,之间用冒号间隔。 
这些字符串是某些寄存器的名称,代表该指令会改变这些寄存器中的 
内容。 

在内嵌的汇编指令中可能会直接引用某些硬件寄存器,我们已经知道 
AT&T格式的汇编语言中,寄存器名以“%”作为前缀,为了在生成的 
汇编程序中保留这个“%”号,在asm语句中对硬件寄存器的引用必须 
用“%%”作为寄存器名称的前缀。如果汇编指令改变了硬件寄存器的 
内容,不要忘记通知编译器(在第三种类型的限定串中添加相应的字 
符串)。还有一些指令可能会改变CPU标志寄存器EFLAG的内容,那么 
需要在第三种类型的限定字符串中加入"cc"。 

为了防止gcc在优化过程中对asm中的汇编指令进行改变,可以在"asm" 
关键字后加上"volatile"修饰符。 

可以在一条asm语句中描述多条汇编语言指令;各条汇编指令之间用 
“;”或者“\n”隔开。 

②操作数限定字符 
操作数限定字符串中利用规定的限定字符来描述相应的操作数,一些 
常用的限定字符有:(还有一些没有涉及的限定字符,参见gcc.info) 

1。"m":操作数是内存变量。 

2。"o":操作数是内存变量,但它的寻址方式必须是“偏移量”类型的, 
也就是基址寻址或者基址加变址寻址。 

3。"V":操作数是内存变量,其寻址方式非“偏移量”类型。 

4。" ":操作数是内存变量,其地址自动增量。 

6。"r":操作数是通用寄存器。 

7。"i":操作数是立即操作数。(其值可在汇编时确定) 

8。"n":操作数是立即操作数。有些系统不支持除字(双字节)以外的 
立即操作数,这些操作数要用"n"而不是"i"来描述。 

9。"g":操作数可以是立即数,内存变量或者寄存器,只要寄存器属 
于通用寄存器。 

10。"X":操作数允许是任何类型。 

11。"0","1",...,"9":操作数与某个指定的操作数匹配。也就是说, 
该操作数就是指定的那个操作数。例如,如果用"0"来描述"%1"操作 
数,那么"%1"引用的其实就是"%0"操作数。 

12。"p":操作数是一个合法的内存地址(指针)。 

13。"=":操作数在指令中是只写的(输出操作数)。 

14。"+":操作数在指令中是读-写类型的(输入-输出操作数)。 

15。"a":寄存器EAX。 

16。"b":寄存器EBX。 

17。"c":寄存器ECX。 

18。"d":寄存器EDX。 

19。"q":寄存器"a","b","c"或者"d"。 

20。"A":寄存器"a"或者"d"。 

21。"a":寄存器EAX。 

22。"f":浮点数寄存器。 

23。"t":第一个浮点数寄存器。 

24。"u":第二个浮点数寄存器。 

25。"D":寄存器di。 

26。"S":寄存器si。 

27。"I":0-31之间的立即数。(用于32位的移位指令) 

28。"J":0-63之间的立即数。(用于64位的移位指令) 

29。"N":0-255之间的立即数。(用于"out"指令) 

30。"G":标准的80387浮点常数。 

*注:还有一些不常见的限定字符并没有在此说明,另外有一些限定 
字符,例如"%","&"等由于我缺乏编译器方面的一些知识,所以我也 
不是很理解它们的含义,如果有高手愿意补充,不慎感激!不过在 
核心代码中出现的限定字符差不多就是上面这些了。 

#3


高手太多,!!!!!!!!

#4


以上这些知识呢,最好能在x86汇编的基础上再学习,因为只要是PC,用的是Intel的芯片,它们的指令系统都是一样的,不管是什么汇编,最终都要对应成机器语言,而且是一一对应。更重要的是masm的资料比较多!:))

#5


谢谢,我对UNIX实在知道得太少了

#6


我有gas的资料(E文的,不多,不知道你有没有兴趣),如果你要的话发信给我
butcherwjw@etang.com

#1


以下部分是从网上找的,希望对你有用。

三.AT&T的汇编语言语法格式 
我想我们大部分人对Intel格式的汇编语言都很了解了。但是,在 
Linux核心代码中,所有的汇编语言指令都是用AT&T格式的汇编语 
言书写的。这两种汇编语言在语法格式上有着很大的不同: 

1.在AT&T的汇编语言中,用'$'前缀表示一个立即操作数;而在Intel 
的格式中,立即操作数的表示不带任何前缀符。例如:下面两个语句 
是完全相同的: 
*AT&T: pushl $4 
*Intel: push 4 

2.AT&T和Intel的汇编语言格式中,源操作数和目标操作数的位置正 
好相反。Intel的汇编语言中,目标操作数在源操作数的左边;而在 
AT&T的汇编语言中,目标操作数则在源操作数的右边。例如: 
*AT&T : addl $4,%eax 
*Intel: add eax,4 

3.在AT&T的汇编语言中,操作数的字长是由操作码助记符的最后一个 
字母决定的,后缀'b'、'w'、'l'分别表示操作数的字长为8比特(字 
节,byte),16比特(字,word)和32比特(长字,long),而 
Intel格式中操作数的字长是用“word ptr”或者“byte ptr”等前 
缀来表示的。例如: 
*AT&T: movb FOO,%al 
*Intel: mov al,byte ptr FOO 

4.在AT&T汇编指令中,直接远跳转/调用的指令格式是“lcall/ljmp 
$SECTION,$OFFSET”,同样,远程返回的指令是“lret 
$STACK-ADJUST”;而在Intel格式中,相应的指令分别为“call/jmp 
far SECTION:OFFSET”和“ret far STACK-ADJUST”。 

①AT&T汇编指令操作助记符命名规则 
AT&T汇编语言中,操作码助记符的后缀字符指定了该指令中操作数的 
字长。后缀字母'b'、'w'、'l'分别表示字长为8比特(字节,byte), 
16比特(字,word)和32比特(长字,long)的操作数。如果助记符 
中没有指定字长后缀并且该指令中没有内存操作数,汇编程序'as'会 
根据指令中指定的寄存器操作数补上相应的后缀字符。所以,下面的 
两个指令具有相同的效果(这只是GNU的汇编程序as的一个特性,AT&T 
的Unix汇编程序将没有字长后缀的指令的操作数字长假设为32比特): 

mov %ax,%bx 

movw %ax,%bx 

AT&T中几乎所有的操作助记符与Intel格式中的助记符同名,仅有一 
小部分例外。操作数扩展指令就是例外之一。在AT&T汇编指令中,操 
作数扩展指令有两个后缀:一个指定源操作数的字长,另一个指定目 
标操作数的字长。AT&T的符号扩展指令的基本助记符为'movs',零扩 
展指令的基本助记符为'movz'(相应的Intel指令为'movsx'和 
'movzx')。因此,'movsbl %al,%edx'表示对寄存器al中的字节数据 
进行字节到长字的符号扩展,计算结果存放在寄存器edx中。下面是一 
些允许的操作数扩展后缀: 
*bl: 字节->长字 
*bw: 字节->字 
*wl: 字->长字 
还有一些其他的类型转换指令的对应关系: 

*Intel *AT&T 
⑴ cbw cbtw 
符号扩展:al->ax 
⑵ cwde cwtl 
符号扩展:ax->eax 
⑶ cwd cwtd 
符号扩展:ax->dx:ax 
⑷ cdq cltd 
符号扩展:eax->edx:eax 

还有一个不同名的助记符就是远程跳转/调用指令。在Intel格式中, 
远程跳转/调用指令的助记符为“call/jmp far”,而在AT&T的汇编 
语言中,相应的指令为“lcall”和“ljmp”。 

②AT&T中寄存器的命名 
在AT&T汇编语言中,寄存器操作数总是以'%'作为前缀。80386芯片的 
寄存器包括: 
⑴8个32位寄存器:'%eax','%ebx','%ecx','%edx','%edi','%esi', 
'%ebp','%esp' 
⑵8个16位寄存器:'%ax','%bx','%cx','%dx','%si','%di','%bp', 
'%sp' 
⑶8个8位寄存器:'%ah','%al','%bh','%bl','%ch','%cl','%dh', 
'%dl' 
⑷6个段寄存器:'%cs','%ds','%es','%ss','%fs','%gs' 
⑸3个控制寄存器:'%cr0','%cr1','%cr2' 
⑹6个调试寄存器:'%db0','%db1','%db2','%db3','%db6','%db7' 
⑺2个测试寄存器:'%tr6','%tr7' 
⑻8个浮点寄存器栈:'%st(0)','%st(1)','%st(2)','%st(3)', 
'%st(4)','%st(5)','%st(6)','%st(7)' 

*注:我对这些寄存器并不是都了解,这些资料只是摘自as.info文档。 
如果真的需要寄存器命名的资料,我想可以参考一下相应GNU工具的机 
器描述方面的源文件。 

③AT&T中的操作码前缀 
⑴段超越前缀'cs','ds','es','ss','fs','gs':当汇编程序中对内 
存操作数进行SECTION:MEMORY-OPERAND引用时,自动加上相应的段超 
越前缀。 
⑵操作数/地址尺寸前缀'data16','addr16':这些前缀将32位的操作 
数/地址转化为16位的操作数/地址。 
⑶总线锁定前缀'lock':总线锁定操作。'lock'前缀在Linux核心代码 
中使用很多,特别是SMP代码中。 
⑷协处理器等待前缀'wait':等待协处理器完成当前操作。 
⑸指令重复前缀'rep','repe','repne':在串操作中重复指令的执行。 

④AT&T中的内存操作数 
在Intel的汇编语言中,内存操作数引用的格式如下: 

SECTION:[BASE + INDEX*SCALE + DISP] 
而在AT&T的汇编语言中,内存操作数的应用格式则是这样的: 

SECTION:DISP(BASE,INDEX,SCALE) 

下面是一些内存操作数的例子: 

*AT&T *Intel 
⑴ -4(%ebp) [ebp-4] 
⑵ foo(,%eax,4) [foo+eax*4] 
⑶ foo(,1) [foo] 
⑷ %gs:foo gs:foo 

还有,绝对跳转/调用指令中的内存操作数必须以'*'最为前缀,否则 
as总是假设这是一个相对跳转/调用指令。 

⑤AT&T中的跳转指令 
as汇编程序自动对跳转指令进行优化,总是使用尽可能小的跳转偏移 
量。如果8比特的偏移量无法满足要求的话,as会使用一个32位的偏 
移量,as汇编程序暂时还不支持16位的跳转偏移量,所以对跳转指令 
使用'addr16'前缀是无效的。 

还有一些跳转指令只支持8位的跳转偏移量,这些指令包括:'jcxz', 
'jecxz','loop','loopz','loope','loopnz'和'loopne'。所以, 
在as的汇编源程序中使用这些指令可能会出错。(幸运的是,gcc并 
不使用这些指令) 

对AT&T汇编语言语法的简单介绍差不多了,其中有些特性是as特有的。 
在Linux核心代码中,并不涉及到所有上面这些提到的语法规则,其 
中有两点规则特别重要:第一,as中对寄存器引用时使用前缀'%';第 
二,AT&T汇编语言中源操作数和目标操作数的位置与我们熟悉的Intel 
的语法正好相反。 

#2


下面是关于gcc行内汇编的。

四.gcc的内嵌汇编语言语句asm 
利用gcc的asm语句,你可以在C语言代码中直接嵌入汇编语言指令, 
同时还可以使用C语言的表达式指定汇编指令所用到的操作数。这一 
特性提供了很大的方便。 

要使用这一特性,首先要写一个汇编指令的模板(这种模板有点类似 
于机器描述文件中的指令模板),然后要为每一个操作数指定一个限 
定字符串。例如: 
extern __inline__ void change_bit(int nr,volatile void *addr) 


__asm__ __volatile__( LOCK_PREFIX 

"btcl %1,%0" 

:"=m" (ADDR) 

:"ir" (nr)); 

上面的函数中: 

LOCK_PREFIX:这是一个宏,如果定义了__SMP__,扩展为"lock;", 
用于指定总线锁定前缀,否则扩展为""。 

ADDR:这也是一个宏,定义为(*(volatile struct __dummy *) addr) 

"btcl %1,%0":这就是嵌入的汇编语言指令,btcl为指令操作码,%1, 
%0是这条指令两个操作数的占位符。后面的两个限定字符串就用于描 
述这两个操作数。 

: "=m" (ADDR):第一个冒号后的限定字符串用于描述指令中的“输 
出”操作数。刮号中的ADDR将操作数与C语言的变量联系起来。这个 
限定字符串表示指令中的“%0”就是addr指针指向的内存操作数。这 
是一个“输出”类型的内存操作数。 

: "ir" (nr):第二个冒号后的限定字符串用于描述指令中的“输入” 
操作数。这条限定字符串表示指令中的“%1”就是变量nr,这个的操 
作数可以是一个立即操作数或者是一个寄存器操作数。 

*注:限定字符串与操作数占位符之间的对应关系是这样的:在所有 
限定字符串中(包括第一个冒号后的以及第二个冒号后的所有限定字 
符串),最先出现的字符串用于描述操作数“%0”,第二个出现的字 
符串描述操作数“%1”,以此类推。 

①汇编指令模板 
asm语句中的汇编指令模板主要由汇编指令序列和限定字符串组成。 
在一个asm语句中可以包括多条汇编指令。汇编指令序列中使用操作 
数占位符引用C语言中的变量。一条asm语句中最多可以包含十个操 
作数占位符:%0,%1,...,%9。汇编指令序列后面是操作数限定字 
符串,对指令序列中的占位符进行限定。限定的内容包括:该占位符 
与哪个C语言变量对应,可以是什么类型的操作数等等。限定字符串 
可以分为三个部分:输出操作数限定字符串(指令序列后第一个冒号 
后的限定字符串),输入操作数限定字符串(第一个冒号与第二个冒 
号之间),还有第三种类型的限定字符串在第二个冒号之后。同一种 
类型的限定字符串之间用逗号间隔。asm语句中出现的第一个限定字 
符串用于描述占位符“%0”,第二个用于描述占位符“%1”,以此类 
推(不管该限定字符串的类型)。如果指令序列中没有任何输出操作 
数,那么在语句中出现的第一个限定字符串(该字符串用于描述输入 
操作数)之前应该有两个冒号(这样,编译器就知道指令中没有输出 
操作数)。 

指令中的输出操作数对应的C语言变量应该具有左值类型,当然对于 
输出操作数没有这种左值限制。 

输出操作数必须是只写的,也就是说,asm对取出某个操作数,执行 
一定计算以后再将结果存回该操作数这种类型的汇编指令的支持不是 
直接的,而必须通过特定的格式的说明。如果汇编指令中包含了一个 
输入-输出类型的操作数,那么在模板中必须用两个占位符对该操作 
数的不同功能进行引用:一个负责输入,另一个负责输出。例如: 

asm ("addl %2,%0":"=r"(foo):"0"(foo),"g"(bar)); 
在上面这条指令中,“%0”是一个输入-输出类型的操作数,"=r"(foo) 
用于限定其输出功能,该指令的输出结果会存放到C语言变量foo中; 
指令中没有显式的出现“%1”操作数,但是针对它有一个限定字符串 
"0"(foo),事实上指令中隐式的“%1”操作数用于描述“%0”操作数 
的输入功能,它的限定字符串中的"0"限定了“%1”操作数与“%0” 
具有相同的地址。可以这样理解上述指令中的模板:该指令将“%1” 
和“%2”中的值相加,计算结果存放回“%0”中,指令中的“%1”与 
“%0”具有相同的地址。注意,用于描述“%1”的"0"限定字符足以 
保证“%1”与“%0”具有相同的地址。但是,如果用下面的指令完成 
这种输入-输出操作就不会正常工作: 

asm ("addl %2,%0":"=r"(foo):"r"(foo),"g"(bar)); 
虽然该指令中“%0”和“%1”同样引用了C语言变量foo,但是gcc并 
不保证在生成的汇编程序中它们具有相同的地址。 

还有一些汇编指令可能会改变某些寄存器的值,相应的汇编指令模板 
中必须将这种情况通知编译器。所以在模板中还有第三种类型的限定 
字符串,它们跟在输入操作数限定字符串的后面,之间用冒号间隔。 
这些字符串是某些寄存器的名称,代表该指令会改变这些寄存器中的 
内容。 

在内嵌的汇编指令中可能会直接引用某些硬件寄存器,我们已经知道 
AT&T格式的汇编语言中,寄存器名以“%”作为前缀,为了在生成的 
汇编程序中保留这个“%”号,在asm语句中对硬件寄存器的引用必须 
用“%%”作为寄存器名称的前缀。如果汇编指令改变了硬件寄存器的 
内容,不要忘记通知编译器(在第三种类型的限定串中添加相应的字 
符串)。还有一些指令可能会改变CPU标志寄存器EFLAG的内容,那么 
需要在第三种类型的限定字符串中加入"cc"。 

为了防止gcc在优化过程中对asm中的汇编指令进行改变,可以在"asm" 
关键字后加上"volatile"修饰符。 

可以在一条asm语句中描述多条汇编语言指令;各条汇编指令之间用 
“;”或者“\n”隔开。 

②操作数限定字符 
操作数限定字符串中利用规定的限定字符来描述相应的操作数,一些 
常用的限定字符有:(还有一些没有涉及的限定字符,参见gcc.info) 

1。"m":操作数是内存变量。 

2。"o":操作数是内存变量,但它的寻址方式必须是“偏移量”类型的, 
也就是基址寻址或者基址加变址寻址。 

3。"V":操作数是内存变量,其寻址方式非“偏移量”类型。 

4。" ":操作数是内存变量,其地址自动增量。 

6。"r":操作数是通用寄存器。 

7。"i":操作数是立即操作数。(其值可在汇编时确定) 

8。"n":操作数是立即操作数。有些系统不支持除字(双字节)以外的 
立即操作数,这些操作数要用"n"而不是"i"来描述。 

9。"g":操作数可以是立即数,内存变量或者寄存器,只要寄存器属 
于通用寄存器。 

10。"X":操作数允许是任何类型。 

11。"0","1",...,"9":操作数与某个指定的操作数匹配。也就是说, 
该操作数就是指定的那个操作数。例如,如果用"0"来描述"%1"操作 
数,那么"%1"引用的其实就是"%0"操作数。 

12。"p":操作数是一个合法的内存地址(指针)。 

13。"=":操作数在指令中是只写的(输出操作数)。 

14。"+":操作数在指令中是读-写类型的(输入-输出操作数)。 

15。"a":寄存器EAX。 

16。"b":寄存器EBX。 

17。"c":寄存器ECX。 

18。"d":寄存器EDX。 

19。"q":寄存器"a","b","c"或者"d"。 

20。"A":寄存器"a"或者"d"。 

21。"a":寄存器EAX。 

22。"f":浮点数寄存器。 

23。"t":第一个浮点数寄存器。 

24。"u":第二个浮点数寄存器。 

25。"D":寄存器di。 

26。"S":寄存器si。 

27。"I":0-31之间的立即数。(用于32位的移位指令) 

28。"J":0-63之间的立即数。(用于64位的移位指令) 

29。"N":0-255之间的立即数。(用于"out"指令) 

30。"G":标准的80387浮点常数。 

*注:还有一些不常见的限定字符并没有在此说明,另外有一些限定 
字符,例如"%","&"等由于我缺乏编译器方面的一些知识,所以我也 
不是很理解它们的含义,如果有高手愿意补充,不慎感激!不过在 
核心代码中出现的限定字符差不多就是上面这些了。 

#3


高手太多,!!!!!!!!

#4


以上这些知识呢,最好能在x86汇编的基础上再学习,因为只要是PC,用的是Intel的芯片,它们的指令系统都是一样的,不管是什么汇编,最终都要对应成机器语言,而且是一一对应。更重要的是masm的资料比较多!:))

#5


谢谢,我对UNIX实在知道得太少了

#6


我有gas的资料(E文的,不多,不知道你有没有兴趣),如果你要的话发信给我
butcherwjw@etang.com