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

标签 c gcc assembly inline-assembly

GCC 工具链默认使用 AT&T 汇编器语法,但可通过 .intel_syntax 指令支持 Intel 语法。

此外,AT&T 和 Intel 语法在 prefixnoprefix 版本中均可用,它们的区别在于它们是否需要在寄存器名称前加上 >% 印记。

根据存在的指令,地址常量的格式会发生变化。

让我们考虑以下 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 版本的英特尔双核笔记本电脑)上,默认使用寄存器 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 编译并用 GAS 汇编的 C 代码中使用 Intel 语法内联汇编,请不要忘记在它后面添加以下内容,以便汇编器可以理解其余的(AT&T 语法) GCC 生成的程序集:

__asm__(".att_syntax prefix");

我看到的前缀/无前缀区别的原因是,对于 AT&T 语法,% 前缀对于 Intel 体系结构上的寄存器来说并不是真正需要的,因为寄存器已命名。但为了统一起见,它可以存在,因为一些其他架构(即 SPARC)已经注册了编号,在这种情况下,单独指定一个低编号对于内存地址或寄存器是指的意思是不明确的。

关于c - 如何在 GCC x86 内联汇编中使用地址常量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/549347/

相关文章:

c - 升级到Mojave后,无法在Mac上编译C程序

c - 学习汇编——全部注释掉,需要生成伪代码

c - 当用户按 "Enter"而不是一个空格字符时如何结束该程序?

c - 如何测试C中函数调用的顺序?

c - Tinyxml - 如何导出多个文件

c - 我可以使用 gdb 的 x 命令打印内存,但是如果我使用 printf,则会出现段错误

c - gcc:从硬件寄存器读取时 '-fno-strict-aliasing' 的奇怪行为

c - 如何使编译器不显示为 void pointer cast 警告

c - C中Thread函数返回值的查询

gcc - GCC中汇编文件的for循环宏/预处理器