Chinaunix首页 | 论坛 | 博客
  • 博客访问: 773417
  • 博文数量: 196
  • 博客积分: 115
  • 博客等级: 民兵
  • 技术积分: 354
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-13 23:19
文章分类

全部博文(196)

文章存档

2021年(1)

2019年(5)

2018年(11)

2017年(15)

2016年(13)

2015年(46)

2014年(81)

2013年(22)

2012年(2)

分类: C/C++

2014-06-12 14:15:00

C语言内联AT&T汇编概要

1 AT&T 语法概要

1.1 寄存器
    8位:  %ah %al
    16位:%ax
    32位:%eax
    64位:%rax
 
1.2 操作符

    8位:  b
    16位:w
    32位:l
    64位:q

1.3 操作数方向

    源 -> 目的

1.4 立即数

    $64

1.5 内存引用

    Intel格式:section:[base+index*scale+displacement]  
        e.g:mov  [ebp+eax*2-0x10], 0x41
    AT&T:    section:displacement(base,index,scale)     
        e.g:movb $0x41, -0x10(%ebp,%eax,2)


2 内联格式 

  1. __asm__ __volatile__ (
  2.     "movl $1, %%eax ;\n"
  3.     : output
  4.     : imput
  5.     : modify
  6.     );
2.1 __volatile__ 不解释。
2.2 每句汇编在 " " 以内,且以 ;\n 结束。
2.3 在内联格式中,寄存器要写为%%rax %%eax格式;%0 %1为占位符使用。
2.4 output/imput 关联到 C 变量/常量。
2.5 modify 向编译器指明不需要优化的部份,如 "memory" "ax" "bx"。


3 output/imput 的详细分析

    output/imput 可以理解为与C语言的接口,几乎所有的内联都会用到这两个域,所以本文会详细描述这两者的区别。

3.1 一个内联的例程
  1. void example( )
  2. {
  3.     char c = 'a';

  4.     __asm__ __volatile__ (
  5.             "movb $84, %0 ; \n"
  6.             : "=r" (c) //output
  7.             :          //input
  8.             : "memory"
  9.     );

  10.     printf("[%c]\n", c);
  11. }

3.2 常用操作约束

    在上面例子中的output域引号中的内容,就是“操作约束”(Operation Constraint);操作约束后括号内,就是C语言的变量或者常量。
    操作约束分为两部分:操作约束  "="  和 存储约束 "r" 。
    操作约束:“=” Write-Only
                    “+” Read-Write。
    存储约束:r  根据操作符使用一个通用寄存器,由GCC自行选取
                    a  根据操作符使用 %rax %eax %ax %al
                    b  根据操作符使 %rbx %ebx %bx %bl
                    c  根据操作符使用 %rcx %ecx  %cx  %cl
                    d  根据操作符使 %rdx %edx %dx %dl
                    m 直接使用内存
    注:1. output/input可以指定多个接口,之间以","分隔。但最多不超过10个。
          2. 操作约束并没有一一列举,还有一些不太常用的如"=&a"()可以在自行google。

    
3.3 指定约束条件后,对C变量/常量的真实操作

    本节会详细分析三种操作约束后,真实的操作情况。其它约束可以以此类推。
        
3.3.1为了方便对比,我们先看output域中,操作约束为"+"(Read-Write)的情况
    先看将例程中的output域修改,并编译为汇编的结果:
  1. : "+r" (c) //output

    这里可以看出,output域在"+"的情况下:
    1. 程序先将变量 c 的值载入寄存器 %bl(虽然是先载入 %eax, 而后传到 %ebx)
    2. 执行汇编语句
    3. 将 %bl 的值写回 c 的内存地址

3.3.2 output域中,操作约束为"="(Write-Only)的情况
    先看将例程中的output域修改,并编译为汇编的结果:
  1. : "=r" (c) //output

    output域,在 "=" (Write-Only)时,程序不会将变量c的值载入("r")指定的寄存器 %bl:
    1. 执行汇编语句
    2. 将 %bl 的值写回 c 的内存地址

3.3.3 input域的情况
    同样,修改例程中的input域,并编译为汇编的结果:
  1. : "r" (c) //input

    input域就没有必要再指定操作约束是 "+"还是"=",因为作为输入域,本来就是会载入变量值的;但是因为同样的原因,输入域也不会再把该寄存器的值写回变量所在的内存;也就是说,如果不作其它的处理,该寄存器中的值会被丢掉,程序流程如下
    1. 程序先将变量 c 的值载入寄存器 %al(整体装入到 %eax)
    2. 执行汇编语句

    对比了以上三种情况,我们可以在内嵌汇编时,灵活使用操作约束来操作 c的变量。


4 顺带说下占位符

    在前面第二节说过,寄存器要写为%%rax %%eax格式;%0 %1为占位符使用。
    占位符可以理解为:output/input域指定的,从头开始数,第一个为 %0,第二个为 %1,以此类推。
    例如:
  1. : "+r" (c)
  2. : "a" (c1), "b" (c2)
    这时候,c c1 c2三个变量对应寄存器的占位符分别对应为:%0 %1 %2    
    占位符的用处:
    1)比如 c 存放于一个GCC选择的通用寄存器,我们并不知道是哪个寄存器,所以只能通过 %0来调用
    2)对c1的使用,可以使用 %%eax 也可以 %1。但是当程序需要改动时,%1明显会减少很多工作量
    另外:占位符最多只能有10个,从 %0 到 %9


END.

阅读(1026) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~