GCC 工具链默认使用 AT&T 汇编器语法,但可以通过.intel_syntax
指令获得对 Intel 语法的支持。
此外,AT&T 和 Intel 语法在 aprefix
和 anoprefix
版本中都可用,它们的不同之处在于它们是否需要在寄存器名称前加上%
sigil。
根据存在的指令,地址常量的格式会发生变化。
让我们考虑以下 C 代码
*(int *)0xdeadbeef = 0x1234;
使用objdump -d
,我们发现它被编译成下面的汇编指令
movl $0x1234,0xdeadbeef
由于不涉及任何寄存器,因此这是 and 的正确语法.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)");
也会起作用。
将印记添加到普通地址常量时,代码将无法复制
__asm__("movl $0x1234,$0xdeadbeef"); // won't compile
当用括号括起来这个表达式时,编译器将发出错误代码而不发出警告,即
__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
我会将此问题标记为社区 wiki,因此如果您有任何有用的内容要添加,请随时添加。