gcc 如何知道内联汇编中使用的寄存器大小?

Posted

技术标签:

【中文标题】gcc 如何知道内联汇编中使用的寄存器大小?【英文标题】:How does gcc know the register size to use in inline assembly? 【发布时间】:2014-09-19 17:17:53 【问题描述】:

我有内联汇编代码:

#define read_msr(index, buf)    asm volatile ("rdmsr" : "=d"(buf[1]), "=a"(buf[0]) : "c"(index))

使用此宏的代码:

u32 buf[2];

read_msr(0x173, buf);

我发现反汇编是(使用 gnu 工具链):

mov    eax,0x173
mov    ecx,eax
rdmsr  
mov    DWORD PTR [rbp-0xc],edx
mov    DWORD PTR [rbp-0x10],eax

问题是0x173小于0xffff,为什么gcc不使用mov cx, 0x173? gcc会解析下面的指令rdmsr吗? gcc 是否总是知道正确的寄存器大小?

【问题讨论】:

我相信是因为0x173的类型是int,所以gcc使用了32位的mov指令 mov cx, 0x173 不会将 ecx 设置为 0x173。高 16 位将包含早期计算中剩余的任何值。 @RaymondChen:我知道你说什么。我想知道为什么gcc知道必须使用“ecx”。 @RaymondChen 我相信问题是问 GCC 如何知道“c”约束是指 ecx 而不是 cxrcx。如果我是正确的,那么您的编辑会改变其含义。 【参考方案1】:

这取决于传递的值或变量的大小。

如果您传递一个“short int”,它将设置“cx”并从“ax”和“dx”读取数据(如果 buf 也是一个 short int)。

对于 char 它将访问 "cl" 等等。

所以“c”指的是“ecx”寄存器,但是根据访问的大小,可以使用“ecx”、“cx”或“cl”来访问,我认为这是有道理的。

为了测试你可以尝试通过(无符号短)0x173,它应该改变代码。

没有分析内联程序集(实际上是文本替换后直接复制到输出程序集,包括语法错误)。此外,没有默认寄存器大小,具体取决于您是 32 位还是 64 位目标。这将是一种限制方式。

【讨论】:

【参考方案2】:

我认为答案是因为当前的默认数据大小是 32 位。在 64 位长模式下,默认数据大小也是 32 位,除非您使用“rex.w”前缀。

【讨论】:

不,C 中的 0x173 具有类型 int,它是 x86-64 System V ABI 中的 32 位类型。 这就是为什么它为"c"(0x173) 选择了一个 32 位寄存器。 int 的大小与默认操作数大小相同并非巧合,但它们是不同的东西。 (例如,gcc -m16 针对 16 位模式仍然使用 32 位 int 和指针,即使这要求汇编器在大多数指令上使用操作数大小和地址大小前缀。在非 x86 上,例如 AVR,@987654326 @ 需要 2 个寄存器。)【参考方案3】:

英特尔将 RDMSR 指令指定为使用(全部)ECX 来确定型号特定寄存器。在这种情况下,显然正如您的宏所指定的那样,GCC 完全有理由将您的常量加载到完整的 ECX 中。

所以关于为什么它不加载 CX 的问题似乎完全不合适。看起来 GCC 正在生成正确的代码。

(你没有问为什么它通过使用 EAX 来低效地分阶段 ECX 的负载;我不知道答案)。

【讨论】:

GCC 不能以这种方式工作。唯一决定分配哪个寄存器的是约束和类型的大小。 GCC 本身并不分析指令。即使它想也做不到,因为可能有不止一个指令。 如前所述,他的宏建立了对 ECX 的约束。就 GCC 确实 知道 RDMSR 的注册要求而言(它可能不知道,我不是 GCC 专家),所以如果 OP 以某种方式指定了 CX,RDMSR 代码生成器会反对它没有得到完整的价值。 如果您承认您不知道 GCC 是否以这种方式行事,为什么要在您的答案中写下它确实如此?正如我所说,GCC 不可能以这种方式运行,因为在内联汇编中 rdmsr 之前可能还有其他指令,包括可能的分支指令,并且知道哪个指令是使用 ecx 的指令与解决停止问题一样困难问题。 如果只分配了cx,代码生成器不会像您声称的那样给出错误:它只会使用高位的先前内容。 其明确的 OP 对出于 RDMSR 的目的设置 CX 感到困惑。很明显,他指定了“C”寄存器来设置该值。我不具体知道 GCC 代码生成器对指令做了什么,但它非常复杂,我可以想象(可能是错误的)它理解每条指令的输入和输出要求。虽然它可能无法确定一些自定义汇编代码是否正确设置了寄存器,但它有可能确定寄存器 ECX 是否未设置,因此如果只设置了 CX 可能会报错.

以上是关于gcc 如何知道内联汇编中使用的寄存器大小?的主要内容,如果未能解决你的问题,请参考以下文章

GCC 内联汇编的副作用

GCC内联汇编常见陷阱

如何在没有扩展内联 asm 的情况下在 gcc 内联汇编中声明和初始化局部变量?

内联汇编:注册引用约定

gcc 内联汇编中的 min

gcc中的arm内联汇编