English 中文(简体)
如何在GCC x86内联汇编中使用地址常量
原标题:
  • 时间:2009-02-14 16:43:38
  •  标签:

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

我会把这个问题标记为“社区协作”,如果你有任何有用的内容可以添加,可以自由地这么做。

最佳回答

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语法中,对于英特尔架构上的寄存器,前缀实际上并不是必需的,因为寄存器是有名称的。但是为了统一起见,它可以存在,因为一些其他架构(例如SPARC)具有编号的寄存器,此时仅指定低号码将不清楚是指内存地址还是寄存器。

问题回答

这是我的结果:

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




相关问题
热门标签