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

时间:2009-02-14 16:43:39

标签: c gcc assembly inline-assembly

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

我会将此问题标记为社区维基,因此如果您有任何有用的内容,请随时添加。

2 个答案:

答案 0 :(得分:4)

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

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

如果您真的倾向于在使用GCC编译的C代码中使用英特尔语法内联汇编并与GAS汇编,请不要忘记在它之后添加以下内容,以便汇编程序能够理解其余部分(AT& T)语法)汇编由GCC生成:

__asm__(".att_syntax prefix");

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

答案 1 :(得分: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");