Chinaunix首页 | 论坛 | 博客

分类: LINUX

2011-10-27 15:19:53

有时侯我们会想这么写一条汇编语句:
                  
                /* 错误写法 */
                asm volatile ("movl %0(%%esp), %x" : /* no outputs */ : "i" (4) );
         
           但这样子gcc会报错:
                  
                Error: junk `(%esp)' after expression
         
           原因是,%0是立即数(用"i"约束的),于是gcc会把这条汇编指令变成:
                  
                movl $4(%esp), x
         
           于是它就会报错,因为多了一个$符号 -- 这在GNU as的寄存器间接寻址中是不允许的。需要改成:
                  
                asm volatile ("movl (%%esp), %x" : /* no outputs */ : "i" (4) );
         

          例如下面的程序:
                  
             /* WARNING: WRONG! */
             $ cat -n dollar.c
             1  #include
             2
             3  int main()
             4  {
             5          long var = 0;
             6
             7          asm volatile
             8                  ("pushl $0x11223344\n\t"
             9                   "pushl $0x55667788\n\t"
            10                   "movl  %1(%%esp), %0\n\t"
            11                   "addl  $8, %%esp "     /* 消减前面两条push指令导致的esp值改变 */
            12
            13                   : "=r" (var)
            14                   : "i" (4)
            15                   : "cc", "esp"
            16                  );
            17
            18          printf("var is 0x%x\n", var);
            19          return 0;
            20  }

         
          使用gcc编译:
                 
                $ gcc dollar.c -m32 -S
         
          查看一下生成的dollar.s文件,我们看到inline asm被gcc转换成:
                 
                pushl   $0x11223344
                pushl   $0x55667788
                movl    $4(%esp), x
                addl    $8, %esp
      
         
          如上所说,"$4(%esp)"这种寻址方式是无法通过GNU as的,因为多了一个$符号。 GCC为我们提供了一些方法,
          允许我们消除那个无用的$符号:
                 
               
                         消除%0的$符号
                %n0         消除%0的$符号,并添加负号。 (例如%0是4,那么%n0就是-4)
                %l0         和%0相同,但是只用于跳转指令的目标
                         %0本来是一个地址,例如0x8048000,放在x中。 现在把它转换为寄存器间接寻址,亦即: (x)
                %P0         相同,消除%0的$符号
         
          让我们再回头来看上面的dollar.c程序。 我们把第10行改成:
                 
                "movl (%%esp), %0\n\t"
         
          然后再次用gcc dollar.c -m32 -S编译,查看dollar.s中的相关代码,发现这条汇编语句变成了:
                 
                movl    4(%esp), x
         
          这正是我们期待的结果。 至于%n0、%l0和%P0,留给读者自己试验一下。

          看一下的例子:

                $ cat a.c
                #include

                int main(void)
                {
                        char c = 0;

                        asm volatile ("movb , %0"
                                      : "=r" (c)
                                      /* GNU ld 为IA32连接程序时,默认在从0x8048001开始的地方保存'E'、'L'、'F'三个字母 */
                                      : "r" (0x8048001)
                                     );

                        printf("c is %c\n", c);
                        return 0;
                }
         
          编译运行:
                  $ gcc -m32 a.c
                $ ./a.out
                c is E
         
          成功的打印出了字母'E'。 倘若我们用-S生成*.s文件,再查看其中的相关汇编语句:
                 
                movb (x), %al
         
          这说明gcc为0x8048001接合了eax寄存器,这就是%1;而,则把它转换为间接寻址,也就是『(%x)』。
阅读(2731) | 评论(0) | 转发(0) |
0

上一篇:一些cpu指令

下一篇:glib 的安装

给主人留下些什么吧!~~