如何在GCC x86内联汇编中使用地址常量

GCC工具链默认使用AT&T汇编语法,但可通过.intel_syntax指令获得对Intel语法的支持。

此外,AT&T和Intel语法都有prefixnoprefix版本,不同之处在于它们是否需要使用% sigil为寄存器名称添加前缀。

根据存在的指令,地址常量的格式会发生变化。

我们考虑以下C代码

 *(int *)0xdeadbeef = 0x1234; 

使用objdump -d ,我们发现它被编译为以下汇编程序指令

 movl $0x1234,0xdeadbeef 

由于没有涉及寄存器,这是.att_syntax prefix.att_syntax noprefix的正确语法,即。 嵌入在C代码中,它们看起来像这样

 __asm__(".att_syntax prefix"); __asm__("movl $0x1234,0xdeadbeef"); __asm__(".att_syntax noprefix"); __asm__("movl $0x1234,0xdeadbeef"); 

您可以选择用括号括起地址常量,即。

 __asm__("movl $0x1234,(0xdeadbeef)"); 

也会奏效。

将sigil添加到普通地址常量时,代码将无法复制

 __asm__("movl $0x1234,$0xdeadbeef"); // won't compile 

当用paranthesis围绕这个表达式时,编译器会在没有警告的情况下发出错误的代码,即

 __asm__("movl $0x1234,($0xdeadbeef)"); // doesn't warn, but doesn't work! 

这将错误地发出指令

 movl $0x1234,0x0 

在Intel模式下,如果可能存在歧义,则地址常量必须以段寄存器为前缀,并且必须以操作数大小和PTR标志为前缀。 在我的机器上(采用Windows XP和当前MinGW和Cygwin GCC版本的英特尔双核笔记本电脑),默认情况下使用寄存器ds

常量周围的方括号是可选的。 如果省略段寄存器但是括号存在,则也可以正确识别地址常量。 但是,忽略寄存器会在我的系统上发出警告。

prefix模式中,段寄存器必须以%为前缀,但仅使用括号仍然有效。 这些是生成正确指令的不同方法:

 __asm__(".intel_syntax noprefix"); __asm__("mov DWORD PTR ds:0xdeadbeef,0x1234"); __asm__("mov DWORD PTR ds:[0xdeadbeef],0x1234"); __asm__("mov DWORD PTR [0xdeadbeef],0x1234"); // works, but warns! __asm__(".intel_syntax prefix"); __asm__("mov DWORD PTR %ds:0xdeadbeef,0x1234"); __asm__("mov DWORD PTR %ds:[0xdeadbeef],0x1234"); __asm__("mov DWORD PTR [0xdeadbeef],0x1234"); // works, but warns! 

省略段寄存器和括号将无法编译

 __asm__("mov DWORD PTR 0xdeadbeef,0x1234"); // won't compile 

我将此问题标记为社区维基 ,所以如果你有任何有用的东西可以添加,请随意这样做。

noprefix / prefix指令仅控制寄存器是否需要%前缀(*)(至少看起来如此,这是文档提到的唯一区别)。 值文字在AT&T语法中始终需要$前缀,而在Intel语法中永远不需要。 以下是有效的:

 __asm__(".intel_syntax prefix"); __asm__("MOV [DWORD PTR 0xDEADBEEF], 0x1234"); 

如果您真的倾向于在使用GCC编译的C代码中使用Intel语法内联汇编并与GAS汇编,请不要忘记在它之后添加以下内容,以便汇编器可以查看由(AT&T语法)生成的其余部分。 GCC:

 __asm__(".att_syntax prefix"); 

我看到前缀/ noprefix区别的原因是,对于AT&T语法,英特尔架构上的寄存器并不真正需要%前缀,因为寄存器是命名的。 但是为了统一,它可以存在,因为一些其他架构(即SPARC)已经编号注册,在这种情况下,单独指定一个低数字对于是否意味着存储器地址或寄存器是不明确的。

这是我自己的结果:

 *(int *)0xdeadbeaf = 0x1234; // reference implementation // AT&T: addresses without sigil; parentheses are optional __asm__(".att_syntax prefix"); __asm__("movl $0x1234,0xdeadbeaf"); // works __asm__("movl $0x1234,(0xdeadbeaf)"); // works __asm__("movl $0x1234,($0xdeadbeaf)"); // doesn't work, doesn't warn! //__asm__("movl $0x1234,$0xdeadbeaf"); // doesn't compile //__asm__("movl 0x1234,0xdeadbeaf"); // doesn't compile //__asm__("movl 0x1234,(0xdeadbeaf)"); // doesn't compile __asm__(".att_syntax noprefix"); // same as above: no registers used! // Intel: addresses with square brackets or segment register prefix // brackets without prefix will warn __asm__(".intel_syntax noprefix"); __asm__("mov DWORD PTR ds:0xdeadbeaf,0x1234"); // works __asm__("mov DWORD PTR ds:[0xdeadbeaf],0x1234"); // works __asm__("mov DWORD PTR [0xdeadbeaf],0x1234"); // works, but warns! //__asm__("mov DWORD PTR 0xdeadbeaf,0x1234"); // doesn't compile // `prefix` will add % to register names __asm__(".intel_syntax prefix"); __asm__("mov DWORD PTR %ds:0xdeadbeaf,0x1234"); // works __asm__("mov DWORD PTR %ds:[0xdeadbeaf],0x1234"); // works __asm__("mov DWORD PTR [0xdeadbeaf],0x1234"); // works, but warns! //__asm__("mov DWORD PTR 0xdeadbeaf,0x1234"); // doesn't compile __asm__(".att_syntax prefix");