GCC工具链默认使用AT&T汇编语法,但可通过该.intel_syntax
指令获得对Intel语法的支持.
此外,AT和T以及英特尔语法都有a prefix
和a noprefix
版本,不同之处在于它们是否需要使用%
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编译并通过GAS编译的C代码中使用Intel语法内联汇编,请不要忘记在其后添加以下内容,以便汇编程序可以使用由以下代码生成的其余(AT&T语法)汇编: GCC:
__asm__(".att_syntax prefix");
我看到的前缀/无前缀区别的原因是,对于AT&T语法,%
英特尔架构上的寄存器实际上并不需要前缀,因为寄存器是命名的。但是为了统一,它可以存在,因为其他一些体系结构(即SPARC)已对寄存器编号,在这种情况下,仅指定一个低编号就意味着要使用内存地址还是寄存器是模棱两可的。