[英]GCC/x86 inline asm: How do you tell gcc that inline assembly section will modify %esp?
[英]How to use address constants in GCC x86 inline assembly
GCC工具链默认使用AT&T汇编语法,但可通过.intel_syntax
指令获得对Intel语法的支持。
此外,AT&T和Intel语法都有prefix
和noprefix
版本,不同之处在于它们是否需要使用%
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
我将此问题标记为社区维基 ,所以如果你有任何有用的东西可以添加,请随意这样做。
noprefix
/ prefix
指令仅控制寄存器是否需要%
前缀(*)(至少看起来如此,这是文档提到的唯一区别)。 值文字在AT&T语法中始终需要$
前缀,而在Intel语法中从不需要。 以下是有效的:
__asm__(".intel_syntax prefix");
__asm__("MOV [DWORD PTR 0xDEADBEEF], 0x1234");
如果您真的倾向于在使用GCC编译的C代码中使用英特尔语法内联汇编并与GAS汇编,请不要忘记在它之后添加以下内容,以便汇编器可以查看由其生成的其余(AT&T语法)汇编。 GCC:
__asm__(".att_syntax prefix");
我看到前缀/ noprefix区别的原因是,对于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");
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.