GCC 工具链默认使用 AT&T 汇编语法,但可以通过 .intel_syntax
指令来支持 Intel 语法。
此外,AT&T和Intel语法都提供有前缀和无前缀版本,在前缀版本中需要使用 %
符号添加寄存器名称。
根据存在的指令,地址常数的格式会改变。
让我们考虑以下的 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)");
会起作用。
将符文添加到普通地址常数时,代码将无法编译。
__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版本的Intel双核笔记本电脑上),默认使用ds寄存器。
把常量放在方括号里是可选的。如果段寄存器被省略但方括号存在,地址常量也会被正确识别。然而,在我的系统中,省略寄存器会发出警告。
在 前缀
模式下,段寄存器必须添加%
前缀,但仅使用括号仍然有效。这些是生成正确指令的不同方法:
__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
我会把这个问题标记为“社区协作”,如果你有任何有用的内容可以添加,可以自由地这么做。