联系我们:010-58426662

GCC Inline ASM

2015-04-28 11:57:29

©娜迦 charme版权所有

版权声明:未经许可,请勿转载

内联汇编的两种语法


内联汇编支持两种语法,一种是intel style,一种是 AT&T 格式,具体的语法这里不做介绍。

特别需要指出的是,在gcc inline asm的编写中支持两个语法格式的切换,使得两种语法的汇编代码都得以正确的编译。


简单示例:

asm(
    //“.globl _charme\n\t"
    //“_charme:\n\t"
    “.intel_syntax noprefix\n\t”
    //“.code64\n\t"
    "mov eax,0x7\n\t"
    ".att_syntax\n\t”
    "mov $5, %eax\n\t”
    "nop\n\t”
    "nop\n\t”
    //“.code32\n\t”
    //“.globl _charme_end\n\t"
    //“_charme_end:\n\t”
    );


说明:

  1.   .intel_syntax或者.intel_syntax noprefix表明后续的代码是intel格式的汇编,直到出现.att_syntax时,汇编代码会切换为AT&T格式。 如果使用gcc编译intel格式的汇编,需要在命令行指定-masm=intel。详细说明及其他一些需要注意的细节,可以参考这里


  2.  .globl 允许用户将插入的汇编代码作为单独的一个段出现。

       注释掉.globl 后: 

     1.png

       开启.globl后:

      2.png

       两种写法运行结果一样,选择使用。


3.   .code64 .code32 指定其后编译的汇编指令位数,选择使用。



gcc 内联汇编支持的两种格式


两种格式:

基本内联汇编 - __asm__ [__volatile__] ("instruction list"); 

带有c/c++表达式内联汇编 - __asm__ [__volatile__]("instruction list”:Output:Input:Clobber/Modify);


说明:

   1.  __asm__ 是asm的宏形式,即#define __asm__ asm。如果要编写符合ANSI C标准的代码(即与ANSI C兼容)使用__asm__。

   2.  __volatile__是volatile的宏形式,使用volatile即表示instruction list中的所有指令要保留,不受优化选项的影响。

   3.  instruction list可以为空。内部书写的指令在一对引号内一行全部书写完毕。也没有在多行中通过多个引号书写。但是任意两条指令之间必须使用;或者\n或者\n\t来分隔。推荐使用\n\t。

   4.  Output,Input,Clobber/Modify都可以为空,如果同时为空则自动转换为第一种格式。

   5.  Output,Input,Clobber/Modify三部分,如果后面的部分不为空,前面部分为空,此时前面的:也不能省略。

asm(“”::);
asm("movl %%eax,%%ebx”::"a"(var));

      上述两种书写方式都是正确的。

   6.  对于 AT&T 语法,基本格式中寄存器前面只能有一个%,带c/c++表达式的格式中,必须有两个%。

asm("movl %%eax,%%ebx”);
asm("movl %eax,%ebx”::);

       上述两种写法都是错误的。


      

还有很多的细节,下面会详细的提到。




高级内联汇编细节

操作约束


基本约束

p1.png


修饰符

p2.png


(I表示输入操作式,O表示输出操作式)


p3.png



通用操作约束如下:

p4.png



针对x86还有一些特殊的操作约束可用:

p5.png



Clobber/Modify


一般发生在一个寄存器出现在"Instruction List",但却不是由Input/Output操作表达式所指定的,也不是在一些Input/Output操作表达式使用"r","g"约束时由GCC 为其选择的,同时此寄存器被"Instruction List”中的指令修改,而这个寄存器只是供当前内联汇编临时使用的情况.


p6.png



备注:

   1.  如果你在Clobber/Modify域声明了一个寄存器,那么这个寄存器将不能再被用做当前内联汇编语句的Input/Output操作表达式的寄存器约束,如果Input/Output操作表达式的寄存器约束被指定为"r"或"g",GCC也不会选择已经被声明在 Clobber/Modify中的寄存器。

   2.  指定修改的寄存器时,指定”ax”,”bx”,”cx","dx","si","di"即可,其它字串与此六种表示等价。

   3.  当一个内联汇编中包含影响标志寄存器eflags的条件,需要在Clobber/Modify部分中使用”cc"来向GCC声明这一点。


有趣的用法

1.  修改编译后的例程名称

extern int test(void) asm ("CHARME");
int test(){
    return 0;
}

3.png



2.  指定变量使用指定的寄存器

int test(){
    register unsigned char counter asm("ecx");
    counter = 19;
    asm volatile(
                 ".intel_syntax\n\t"
                 "xor ecx, ecx\n\t"
                 : "=l" (counter)
                 ::"cx");
    return counter;
}

counter最终的值为0



3.  单纯的通知编译器

int main(int __argc, char* __argv[])
{
    int* __p = (int*)__argc;
    (*__p) = 9999;
    
    __asm__("":::"memory");
    
    if((*__p) == 9999)
        return 5;
    
    return (*__p); 
}

如果没有那一句内联汇编语句,编译器会直接忽略比较语句,直接返回5。



参考文献:

AT&T Syntax versus Intel Syntax - http://www.sourceware.org/binutils/docs/as/i386_002dVariations.html

GCC Inline ASM - http://locklessinc.com/articles/gcc_asm/

标签: asm inlineasm 内联汇编 gcc

热度  13718 回应  2 收藏  0

  • charme
    @stoner 高级格式的内联汇编还是很不错的
    回应 2015-05-04 16:57:48
GCC Inline ASM

作者信息

  • charme
  • 2015-03-30日入驻

收藏 0

文章 7

进入个人主页

娜迦,守护10亿移动用户安全! 免费体验