| 关键词: nbsp output eax input asm movl volatile mov 80495 结果 |
转自: http://blog.chinaunix.net/u2/86590/showart.php?id=1869827关于linux内嵌汇编语言,内核之旅的第六讲:Linux中的汇编语言已经讲得很清楚了。这里结合几个实例主要讲解几个需要留意的地方。实例一:变量修饰符"+"asm.c #include <stdio.h>unsigned long input = 0x12345678;unsigned long output = 0x10000000;int main(void){ asm volatile ("addl %1,%0 \n\t" :"=r"(output) :"r"(input)); printf("output:0x%x\n",output); return 0;}编译、运行:gcc -o asm asm.c -g./asm结果为:output:0x2468acf0怎么回事呢?我们的意图是让input+output,结果为0x22345678才对啊。没关系,objdump分析一下:objdump -j .text -S asm我们只关心内嵌汇编语言的反汇编的结果: asm volatile ("addl %1,%0 \n\t"8048385: a1 ac 95 04 08 mov 0x80495ac,%eax804838a: 01 c0 add %eax,%eax804838c: a3 b0 95 04 08 mov %eax,0x80495b0 :"=r"(output) :"r"(input));很明显可以猜到,0x80495ac是input的地址,0x80495b0是output的地址,那么反汇编的结果就是将input+input的结果送给output,所以结果才为0x12345678+0x12345678 = 0x2468acf0;原因是这样的,老版的gcc将操作符严格的分为输入和输出两种,分别放在输入和输出部分,那么我们知道这个output是一个先读后写型操作数,也就是说,要先将output读出来,和input相加,然后再写回去,所以output既属于输入部分也属于输出部分。那么问题的症结也就在此。我们在输入部分也加上output试试。 #include <stdio.h>unsigned long input = 0x12345678;unsigned long output = 0x10000000;int main(void){ asm volatile ("addl %1,%0 \n\t" :"=r"(output) :"r"(input), "0"(output));//新添加上去的 printf("output:0x%x\n",output); return 0;}编译、运行:gcc -o asm asm.c -g./asm结果为:output:0x22345678反汇编:objdump -j .text -S asm对应的汇编指令为; asm volatile ("addl %1,%0 \n\t"8048385: 8b 15 bc 95 04 08 mov 0x80495bc,%edx804838b: a1 c0 95 04 08 mov 0x80495c0,%eax8048390: 01 d0 add %edx,%eax8048392: a3 c0 95 04 08 mov %eax,0x80495c0 :"=r"(output) :"r"(input)), "0"(output));先将input(0x80495bc)送给edx,然后再把output(0x80495c0)送给eax,edx+eax->eax,eax->output其实有一个输出变量修饰符"+",就是专门干这个事情的。我们上面的例子中用的输出变量修饰符"="(这一句:"=r"(output)中的=号)只是表示这是一个输出操作数,而"+"则表示这是一个先读后写型操作数。所以将这里的=改为+,一样可以解决问题,读者可以自己尝试。实例二:变量修饰符"&"上个实例讲解了变量修饰符+,这个实例主要说明变量修饰符&。先看两段代码: asm volatile ("movl %1,%0 \n\t" :"=r"(output) :"r"(input)); asm volatile ("movl %1,%0 \n\t" :"=&r"(output) :"r"(input));这两段代码主要的区别就在于这个&。变量修饰符&表示:输出变量和输入变量不能共用同一个寄存器,这样据说可以避免很多麻烦。反汇编这两段代码看看是不是这样: asm volatile ("movl %1,%0 \n\t"8048385: a1 ac 95 04 08 mov 0x80495ac,%eax804838a: 89 c0 mov %eax,%eax804838c: a3 b0 95 04 08 mov %eax,0x80495b0 :"=r"(output) :"r"(input)); asm volatile ("movl %1,%0 \n\t"8048385: a1 ac 95 04 08 mov 0x80495ac,%eax804838a: 89 c2 mov %eax,%edx804838c: 89 d0 mov %edx,%eax804838e: a3 b0 95 04 08 mov %eax,0x80495b0 :"=&r"(output) :"r"(input));实例三: 破坏描述什么是破坏描述呢?当我们用纯C编写代码时,寄存器的分配和使用是完全由编译器来控制的。因为编译器对C语法和语义相当的了解,所以在编译器自己优化的时候,会协调好寄存器的使用。但是当C中有内嵌的汇编语言时,寄存器也可以由程序员来使用和控制,所以就得有一种机制通知编译器内嵌汇编都修改了哪些寄存器,编译器对这些寄存器再次使用可能会导致错误。这种机制就是破坏描述。先看一段代码: #include <stdio.h>unsigned long input = 0x12345678;unsigned long output,zero;int main(void){ asm volatile ("movl $0,%%eax \n\t" "movl %%eax,%1 \n\t" "movl %2,%%eax \n\t" "movl %%eax,%0 \n\t" :"=m"(output),"=m"(zero) :"r"(input)); printf("output:0x%x\t zero:0x%x\t\n",output,zero); return 0;}这段代码想把zero置为0,然后再把input赋值给output。那么结果是不是这样的呢?编译、运行:gcc -o asm asm.c -g./asm 结果为:output:0x0 zero:0x0同样看看反汇编的结果吧:objdump -j .text -S asm asm volatile ("movl $0,%%eax \n\t"8048385: a1 bc 95 04 08 mov 0x80495bc,%eax804838a: b8 00 00 00 00 mov $0x0,%eax804838f: a3 c8 95 04 08 mov %eax,0x80495c88048394: 89 c0 mov %eax,%eax8048396: a3 c4 95 04 08 mov %eax,0x80495c4 "movl %%eax,%1 \n\t" "movl %2,%%eax \n\t" "movl %%eax,%0 \n\t" :"=m"(output),"=m"(zero) :"r"(input));先将input(0x80495bc)送给eax,然后将eax清零再将eax送给zero(x80495c8)将eax送给output(0x80495c4)这就是编译器在优化的时候造成的麻烦。那么我们可以用破坏描述,告诉编译器eax已经使用,不能再对它进行更改了。代码修改如下: #include <stdio.h>unsigned long input = 0x12345678;unsigned long output,zero;int main(void){ asm volatile ("movl $0,%%eax \n\t" "movl %%eax,%1 \n\t" "movl %2,%%eax \n\t" "movl %%eax,%0 \n\t" :"=m"(output),"=m"(zero) :"r"(input) :"eax");//新加的破坏描述 printf("output:0x%x\t zero:0x%x\t\n",output,zero); return 0;}结果为:./asmoutput:0x12345678 zero:0x0反汇编的结果为:objdump -j .text -S asm asm volatile ("movl $0,%%eax \n\t"8048385: 8b 15 bc 95 04 08 mov 0x80495bc,%edx804838b: b8 00 00 00 00 mov $0x0,%eax8048390: a3 c8 95 04 08 mov %eax,0x80495c88048395: 89 d0 mov %edx,%eax8048397: a3 c4 95 04 08 mov %eax,0x80495c4 "movl %2,%%eax \n\t" "movl %%eax,%0 \n\t" :"=m"(output),"=m"(zero) :"r"(input) :"eax");另外还有内存破坏描述:"memory",同样的道理,这里就不再多举例了.参考资料:《Linux2.6内核标准教程》人民邮电出版社 作者:华清远见嵌入式培训中心 河秦 王洪涛 |
|
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|