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

11
GCC工具链默认使用AT&T汇编语法,但是可以通过.intel_syntax指令支持Intel语法。
此外,AT&T和Intel语法都有一个带有前缀prefix和不带前缀noprefix的版本,它们之间的区别在于是否需要在寄存器名称前加上%符号。
根据存在哪些指令,地址常量的格式会发生变化。
让我们考虑以下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)");

加上符号后,普通地址常量的代码将无法编译。

同样适用于will work as well。

__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

我将把这个问题标记为社区维基,因此如果你有任何有用的补充,请随意添加。


这显然不仅仅涉及工具链中的 AS;涉及哪个版本的 GCC?您是否检查了 gcc -S 的输出以验证输入到 GNU as 中的内容是否符合预期? - Charles Duffy
好的,是我搞砸了 - 至少部分地;用 0xdeadbeaf 替换 ($0xdeadbeaf) 似乎可以解决问题;测试后我会编辑问题... - Christoph
@Charles:是的,gcc -S 产生了预期的结果。 - Christoph
关于前缀的问题,我已经回答了。但是我并没有看到你在使用noprefix时遇到的警告。你使用的是哪个版本的gas/GCC? - Tom Alsberg
@Tom:gcc/as版本:cygwin:3.4.4/2.18.50.20080625,mingw32:3.4.5,4.3.0.20080305/2.17.50.20060824。 - Christoph
2个回答

5
< p > noprefix/prefix 指令只控制是否需要在寄存器前加上 % 前缀(至少文档是这么说的,而这也是文档提到的唯一区别)。 在 AT&T 语法中,值字面量始终需要添加 $ 前缀,而在 Intel 语法中则永远不需要。 因此,以下内容是正确的:

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

如果你真的倾向于在使用GCC编译器和GAS汇编器的C代码中嵌入Intel语法的汇编代码,请不要忘记在其后添加以下内容,以便汇编器可以理解GCC生成的其余(AT&T语法)汇编代码:
__asm__(".att_syntax prefix");

我看到前缀/无前缀区别的原因是,在AT&T语法中,对于Intel架构上的寄存器,实际上并不需要使用%前缀,因为寄存器已经有了名称。但是为了保持统一性,可以添加该前缀,因为一些其他架构(例如SPARC)具有编号的寄存器,此时仅指定低编号可能会产生歧义,无法确定是指内存地址还是寄存器。

1

以下是我的测试结果:

*(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");

正如我在我的答案中所指出的,我认为在这方面noprefix和prefix没有任何区别。文档只是说它影响寄存器名称前面的%。因此,在noprefix下相同的块也应该在prefix下工作。 - Tom Alsberg
@Tom:不幸的是,情况并非如此 - noprefix 版本在与 prefix 一起使用时会发出“错误:意外的令牌 :”。 - Christoph
这不是因为另外两个无前缀的例子包括一个寄存器(ds),所以在使用前缀时需要向其添加%吗? - Tom Alsberg
@Tom:不错 - 所以语法实际上是有意义的!你想总结一下吗?还是我更新问题,包括这些结果? - Christoph
你总结得比我好。现在无法尝试,但当我尝试并与MASM/TASM传统接受的英特尔语法进行概括或比较时,我可能会编辑我的答案。对于你将问题详细编辑成如此好的解释表示赞赏! - Tom Alsberg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接