📄 preparing-asm_4.htm
字号:
: : "ebx" )。</FONT></LI></UL>
<P><FONT
face=System>从上面的规则可以看到另外一个事实,区分一个内联汇编是基本格式的还是带有C/C++表达式格式的,其规则在于在"Instruction
List"后是否有冒号(:)的存在,如果没有则是基本格式的,否则,则是带有C/C++表达式格式的。</FONT></P>
<P><FONT
face=System>两种格式对寄存器语法的要求不同:基本格式要求寄存器前只能使用一个百分号(%),这一点和非内联汇编相同;而带有C/C++表达式格式则要求寄存器前必须使用两个百分号(%%),其原因我们会在后面讨论。</FONT></P>
<P><STRONG><FONT face="Times New Roman TUR" size=4>1. Output</FONT></STRONG></P>
<P><FONT
face=System><STRONG>Output</STRONG>用来指定当前内联汇编语句的输出。我们看一看这个例子:</FONT></P>
<P><FONT face=System>__asm__("movl %%cr0, %0": "=a" (cr0));</FONT></P>
<P><FONT
face=System>这个内联汇编语句的输出部分为"=r"(cr0),它是一个“操作表达式”,指定了一个输出操作。我们可以很清楚得看到这个输出操作由两部分组成:括号括住的部分(cr0)和引号引住的部分"=a"。这两部分都是每一个输出操作必不可少的。括号括住的部分是一个C/C++表达式,用来保存内联汇编的一个输出值,其操作就等于C/C++的相等赋值cr0
=
output_value,因此,括号中的输出表达式只能是C/C++的左值表达式,也就是说它只能是一个可以合法的放在C/C++赋值操作中等号(=)左边的表达式。那么右值output_value从何而来呢?</FONT></P>
<P><FONT face=System>答案是引号中的内容,被称作“操作约束”(Operation
Constraint),在这个例子中操作约束为"=a",它包含两个约束:等号(=)和字母a,其中等号(=)说明括号中左值表达式cr0是一个Write-Only的,只能够被作为当前内联汇编的输入,而不能作为输入。而字母a是寄存器EAX
/ AX / AL的简写,说明cr0的值要从eax寄存器中获取,也就是说cr0 = eax,最终这一点被转化成汇编指令就是movl %eax,
address_of_cr0。现在你应该清楚了吧,操作约束中会给出:到底从哪个寄存器传递值给cr0。</FONT></P>
<P><FONT
face=System>另外,需要特别说明的是,很多文档都声明,所有输出操作的操作约束必须包含一个等号(=),但GCC的文档中却很清楚的声明,并非如此。因为等号(=)约束说明当前的表达式是一个Write-Only的,但另外还有一个符号——加号(+)用来说明当前表达式是一个Read-Write的,如果一个操作约束中没有给出这两个符号中的任何一个,则说明当前表达式是Read-Only的。因为对于输出操作来说,肯定是必须是可写的,而等号(=)和加号(+)都表示可写,只不过加号(+)同时也表示是可读的。所以对于一个输出操作来说,其操作约束只需要有等号(=)或加号(+)中的任意一个就可以了。</FONT></P>
<P><FONT
face=System>二者的区别是:等号(=)表示当前操作表达式指定了一个纯粹的输出操作,而加号(+)则表示当前操作表达式不仅仅只是一个输出操作还是一个输入操作。但无论是等号(=)约束还是加号(+)约束所约束的操作表达式都只能放在Output域中,而不能被用在Input域中。</FONT></P>
<P><FONT face=System>另外,有些文档声明:尽管GCC文档中提供了加号(+)约束,但在实际的编译中通不过;我不知道老版本会怎么样,我在GCC
2.96中对加号(+)约束的使用非常正常。</FONT></P>
<P><FONT face=System>我们通过一个例子看一下,在一个输出操作中使用等号(=)约束和加号(+)约束的不同。</FONT></P>
<P><FONT face=System>$ cat example2.c</FONT></P>
<P><FONT face=System>int main(int __argc, char*
__argv[])
<BR>{
<BR> int cr0 =
5;
<BR>
<BR> __asm__ __volatile__("movl %%cr0,
%0":"=a" (cr0));
<BR>
<BR> return
0;
<BR>}</FONT></P>
<P><FONT face=System>$ gcc -S example2.c</FONT></P>
<P><FONT face=System>$ cat example2.s</FONT></P>
<P><FONT
face=System>main:
<BR> pushl
%ebp
<BR> movl %esp,
%ebp <BR>
subl $4, %esp
<BR> movl $5,
-4(%ebp) # cr0 =
5<BR>#APP
<BR> movl %cr0,
%eax
<BR>#NO_APP
<BR> movl %eax,
%eax <BR>
movl %eax, -4(%ebp) # cr0 =
%eax<BR> movl $0,
%eax
<BR>
leave
<BR>
ret
<BR></FONT></P>
<P><FONT face=System>这个例子是使用等号(=)约束的情况,变量cr0被放在内存-4(%ebp)的位置,所以指令mov %eax,
-4(%ebp)即表示将%eax的内容输出到变量cr0中。</FONT></P>
<P><FONT face=System>下面是使用加号(+)约束的情况:</FONT></P>
<P><FONT face=System>$ cat example3.c</FONT></P>
<P><FONT face=System>int main(int __argc, char*
__argv[])
<BR>{
<BR> int cr0 =
5;
<BR>
<BR> __asm__ __volatile__("movl %%cr0,
%0" : "+a" (cr0));
<BR>
<BR> return
0;
<BR>}</FONT></P>
<P><FONT face=System>$ gcc -S example3.c</FONT></P>
<P><FONT face=System>$ cat example3.s</FONT></P>
<P><FONT
face=System>main:
<BR> pushl
%ebp
<BR> movl %esp,
%ebp
<BR> subl $4,
%esp
<BR> movl $5,
-4(%ebp) # cr0
= 5<BR> movl
-4(%ebp), %eax # input ( %eax = cr0
)<BR>#APP
<BR> movl %cr0,
%eax<BR>#NO_APP<BR>
movl %eax, -4(%ebp) # output (cr0 = %eax
)<BR> movl $0,
%eax<BR>
leave<BR> ret</FONT><BR></P>
<P>从编译的结果可以看出,当使用加号(+)约束的时候,cr0不仅作为输出,还作为输入,所使用寄存器都是寄存器约束(字母a,表示使用eax寄存器)指定的。关于寄存器约束我们后面讨论。</P>
<P><FONT face=System>在Output域中可以有多个输出操作表达式,多个操作表达式中间必须用逗号(,)分开。例如:</FONT></P>
<P><FONT
face=System> __asm__(
<BR> "movl %%eax, %0
\n\t"
<BR> "pushl %%ebx
\n\t"
<BR> "popl %1
\n\t"
<BR> "movl %1,
%2"
<BR> : "+a"(cr0), "=b"(cr1),
"=c"(cr2));</FONT><BR></P>
<P><FONT face="Times New Roman Baltic"
size=4><STRONG>2、Input</STRONG></FONT></P><FONT face=System>
<P><FONT
face=System><STRONG>Input域的内容</STRONG>用来指定当前内联汇编语句的输入。我们看一看这个例子:</FONT></P>
<P><FONT face=System>__asm__("movl %0, %%db7" : : "a"
(cpu->db7));</FONT></P>
<P>例中Input域的内容为一个表达式"a"[cpu->db7),被称作“输入表达式”,用来表示一个对当前内联汇编的输入。</P>
<P>像输出表达式一样,一个输入表达式也分为两部分:带括号的部分(cpu->db7)和带引号的部分"a"。这两部分对于一个内联汇编输入表达式来说也是必不可少的。</P>
<P>括号中的表达式cpu->db7是一个C/C++语言的表达式,它不必是一个左值表达式,也就是说它不仅可以是放在C/C++赋值操作左边的表达式,还可以是放在C/C++赋值操作右边的表达式。所以它可以是一个变量,一个数字,还可以是一个复杂的表达式(比如a+b/c*d)。比如上例可以改为:__asm__("movl
%0, %%db7" : : "a" (foo)),__asm__("movl %0, %%db7" : : "a"
(0x1000))或__asm__("movl %0, %%db7" : : "a" (va*vb/vc))。</P>
<P>引号号中的部分是约束部分,和输出表达式约束不同的是,它不允许指定加号(+)约束和等号(=)约束,也就是说它只能是默认的Read-Only的。约束中必须指定一个寄存器约束,例中的字母a表示当前输入变量cpu->db7要通过寄存器eax输入到当前内联汇编中。</P>
<P>我们看一个例子:</P>
<P>$ cat example4.c</P>
<P>int main(int __argc, char*
__argv[])
<BR>{
<BR> int cr0 =
5;
<BR>
<BR> __asm__ __volatile__("movl %0,
%%cr0"::"a" (cr0));
<BR>
<BR> return
0;
<BR>}</P>
<P>$ gcc -S example4.c</P>
<P>$ cat example4.s</P>
<P>main:
<BR> pushl
%ebp
<BR> movl %esp,
%ebp
<BR> subl $4,
%esp
<BR> movl $5,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -