GNU汇编与内联汇编

以前看过一段时间,3天大鱼,2天晒网,现在也忘差不多了,但基本上也都知道什么意思,关于细节在这里写一下.
GNU汇编寻址:
+------------------------------+------------------------------------+
|       Intel Code             |      AT&T Code                     |
+------------------------------+------------------------------------+
| mov     eax,1                |  movl    $1,%eax                   |   
| mov     ebx,0ffh             |  movl    $0xff,%ebx                |   
| int     80h                  |  int     $0x80                     |   
| mov     ebx, eax             |  movl    %eax, %ebx                |
| mov     eax,[ecx]            |  movl    (%ecx),%eax               |
| mov     eax,[ebx+3]     |  movl    3(%ebx),%eax              | 
| mov     eax,[ebx+20h]        |  movl    0x20(%ebx),%eax           |
| add     eax,[ebx+ecx*2h]     |  addl    (%ebx,%ecx,0x2),%eax      |
| lea     eax,[ebx+ecx]        |  leal    (%ebx,%ecx),%eax          |
| sub     eax,[ebx+ecx*4h-20h] |  subl    -0x20(%ebx,%ecx,0x4),%eax |
+------------------------------+------------------------------------+
GCC内联汇编:
   内联汇编约束的意义,最主要在于帮助编译器来确定操作数的寻址方式.即asm应该在哪里去取这些操作数,或者把结果放到什么地方去.
   常用操作数约束:
        
    r: 任意寄存器          若对输入或者输出指定了这个约束条件,那么编译器就会把输入变量存储在一个寄存器中,或者会把输出保存到一个寄存器中去.第3个约束条件表示asm要用到%eax,编译器不应该用eax来存储有用的数据,否则eax里面的数据会被破坏.这里指定了b为输出操作数,但存在寄存器里面.所以,在asm程序段里面,会将b存在寄存器里面,但是在asm之外,编译器会将栈空间里面的b用寄存器里面的值更新.可以看到,在这里,输入和输出编译器使用了想同的寄存器,如果要强制使用不同的寄存器,可以将输出约束写为"=&r",加一个&.
      {
        int a=10, b;
       __asm__ ("movl %1, %%eax\n\t" \
                "movl %%eax, %0\n\t" \
                :"=r" (b) \                      /*output*/
                    :"r"(a)   \                      /*input*/
                    :"%eax"   \                      /*寄存器修饰*/
               );
      }
      利用gcc -S 编译得到汇编程序如下,可以看的比较清楚:
        pushl   %ebp                    #保存ebp
        movl    %esp, %ebp              #新的栈帧
        subl    $8, %esp                #为a,b在栈里面分配8个字节的空间
        movl    $10, -4(%ebp)           #a = 10
        movl    -4(%ebp), %edx          #把a放到寄存器edx里面,r操作数约束的结果
#APP
        movl %edx %eax                  
        movl %eax %edx                  #可见编译器把输出也放在了edx里面
#NO_APP
        movl    %edx, %eax              #多了一句废话          
        movl    %eax, -8(%ebp)          #在asm外更新b的值  
        leave
        ret
    

                
                
                
                
                q(a,b,c,d分别指定):表示寄存器为eax,ebx,ecx,edx之一
m:相应的操作数放在内存单元中
    当操作数在内存中时,任何对操作数的操作都直接在内存中生效
S,D:要求使用寄存器esi或者edi 
    通常在字符串函数中会用到,因为字符串函数的使用频繁,所以在lib中都是使用汇编进行实现.
    void strncpy(char *dst,char *src,int count)  \
    __asm__("cld\n\t"  \             /*DF=0, 地址增加*/
              "rep\n\t"  \                /*将下一条指令执行%ecx次*/
              "movsb"     \
            :          \
              :"S"(src), \
             "D"(dst), \
                "c"(count)\
编译后结果:
    可以看到这里有对edi,esi的保护,到底是调用者保护寄存器,还是被调用者保护寄存器,这个对不同的寄存器有明确的说明,具体件上一部分.
strncpy:
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %edi
        pushl   %esi
        movl    12(%ebp), %esi
        movl    8(%ebp), %edi
        movl    16(%ebp), %ecx
#APP
        cld
        rep
        movsb
#NO_APP
        popl    %esi
        popl    %edi
        popl    %ebp
        ret
            );
匹配约束:    在某些情况下,有些变量既要充当输入操作叔,又要充当输出操作数字.
    __asm__("incl %1":"=a"(val):"0"(val));
    在这个例子中,%eax既用做输入也用做输出,注意在输入位置的"0"表示:与第0个输出变量想同的约束,也就是说,将输出值val也存放在%eax中.
修饰寄存器的使用:
    首先需要明确,ESI和EDI是不需要修饰的,因为他们本来就在asm的使用列表中,编译器假定asm一定会使用esi和edi,所以编译器不会把esi和edi用做其他用途.而如果我们使用了一个寄存器,而这个寄存器既不出现在输入列表中,也不出现在输出列表中,我们最好还是修饰一下.
例如:
#define  memcpy(dst,src,count) \
__asm__("movl %0 %%ecx"
        "up: lodsl\r\n"    \   /*将esi里面的值放到eax里面去*/
        "stosl\r\n"        \
        "loop up"           \
        :                   \
         :"m"(count),      \
         "S"(src),        \ 
         "D"(dst),         \
         :"eax","ecx"       \
        );
编译后结果:
memcpy:
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %edi
        pushl   %esi
        movl    12(%ebp), %esi
        movl    8(%ebp), %edi
#APP
        movl 16(%ebp) %ecx
        up: lodsl
        stosl
        loop up
#NO_APP
        popl    %esi
        popl    %edi
        popl    %ebp
        ret
注意:%0,%1,%2与输入输出的匹配,按照最后约束条件里面出现的顺序,分别为0,1,2...,比如下程序,如果想
把a,b,c的值付给d,e,f,要这样写:
int main()
{
        int a=10,b=12,c=33;
        int d=1,e=2,f=3;
        __asm__("movl %3, %%eax\n\t"   \
                "movl %4, %%ebx\n\t"   \
                "movl %5, %%ecx\n\t"   \
                "molv %%eax ,%0\n\t"   \
                "molv %%ebx ,%1\n\t"   \
                "molv %%ebx ,%2\n\t"   \
                :"=m"(d),"=m"(e),"=m"(f) \
                :"m"(a),"m"(b),"m"(c) \
                :"%eax","%ebx","%ecx" \
                );
        printf("a=%d\n, b=%d\n, c=%d\n, d=%d\n, e=%d\n, f=%d\n",a,b,c,d,e,f);
}

                

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/59615/showart_465060.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值