C - 读取和设置汇编器寄存器
C - Read and set assembler registers
我需要能够从我的 C 代码访问汇编寄存器。我有一种简单的方法可以将寄存器的值设置为变量的值并将寄存器的值作为变量获取?希望这是有道理的...
如果您正在使用 VC(Microsoft 的 C 编译器)
int regVal;
__asm {
mov [regVal], eax
}
这会将 eax
持有的任何内容加载到 regVal
如果您使用的是 GCC:
int regVal;
asm("movl %%eax, %0" : "=r"(regVal) :);
希望我做对了,我实际上并没有在 GCC 中进行内联汇编。
如果您真的想在执行某些代码时知道寄存器的值,使用调试器 更有意义。 (就像带有 layout asm; layout reg
的 gdb)。只看寄存器值而不看 asm 代码毫无意义。
请参阅 x86 wiki 中的链接和指南。
然而,使用 GNU 内联汇编,你可以做类似的事情
int eax_snapshot;
asm volatile (
""
: "=a" (eax_snapshot)
// no inputs, no clobbers
);
// any amount of intervening code
printf("eax was %x\n", eax_snapshot); // print whatever eax had at the point where the inline asm appeared in program order
The a
constraint表示操作数必须使用%eax
寄存器。然后内联汇编的主体变成空操作,因为您的输出操作数已经在您告诉编译器的位置。
不能保证 gcc 不会在 asm 块之前发出破坏 %eax
的代码。这就是为什么这样做毫无意义。 volatile
告诉编译器不要根据其他源指令重新排序 asm 语句,但优化可能仍然意味着早期的初始化被折叠到使用它们的后期代码中。或相反亦然。不过,将它与显式寄存器局部变量结合使用可能有助于避免 gcc 使用您选择的寄存器作为计算其他内容的临时寄存器。 (见下文)。
要设置寄存器,请使用带有 "a"
约束的内联汇编来强制该操作数的值在 %eax
中(或 %ebx
用于 "b"
约束,等等,请参阅 x86 特定机器约束的文档)在内联 asm 出现的位置。如果你真的想 使用 那个寄存器值,从内联 asm 语句里面做,否则寄存器可能被其他东西覆盖。
您也可以声明必须在特定寄存器中分配的变量,但我认为在没有初始化的情况下使用它们会使编译器不愉快。 (检测未定义行为的代码转换过程可能会导致问题。IDK。)语法为
register int eax asm ("eax"); // don't do this to read eax, it probably doesn't do what you think it would
The docs make it sound like using it for reading registers is not going to be a good idea. 但是,对于设置寄存器值,这可能有效。 (只要寄存器分配器接受了您的提示并将该变量保存在请求的寄存器中。)
register int foo asm("regname")
有时在实践中仍然可以像以前一样使用 GCC(在他们更改文档以不支持它之前,除了内联 asm)。但 IIRC 并非总是如此。
Clang 通常不会做任何事情,除了限制 "r"
或 "=r"
为扩展 asm 语句做出的选择,这是 GCC 文档保证的最低限度。因此,即使现在没有记录(因此不受支持)而没有实际 asm
语句的它的使用恰好适用于您的 GCC 版本,用于您的代码在程序中的某个时刻读取或写入寄存器,它很可能不能移植到 clang。
不要这样做除非你有很好的理由。它不太可能对正常编程有用。普通内联 asm,或调用函数,是结合 C 和 asm 的更好方法。这仅在您想要对程序的内部状态进行某种笨拙的检查时才有用。
在普通函数中以这种方式读取寄存器意味着编译器可以在您的 asm
语句之前放置一些指令来修改该寄存器,因此您无法确定它实际上来自 asm 调用者,如果你想发明一个自定义调用约定。
由于您没有给出用例来说明为什么要这样做,我强烈建议您不要编写执行此操作的代码。只是听起来很丑。
由于您使用的是 gcc,这里是有关在 gcc 中使用 asm 模板的文档:https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#AssemblerTemplate
为了设置寄存器的值,您可以执行以下操作:
uint64_t rax = 0;
__asm__ __volatile__("mov %0, %%rax\n\t"
: /* no output */
: "a" (rax)
: "%rax");
为了读取寄存器的值,您需要执行以下操作:
uint64_t rax;
__asm__ __volatile__("mov %%rax, %0\n\t"
: "=a"(rax)
: /* no input */
: /* no clobbers */);
我需要能够从我的 C 代码访问汇编寄存器。我有一种简单的方法可以将寄存器的值设置为变量的值并将寄存器的值作为变量获取?希望这是有道理的...
如果您正在使用 VC(Microsoft 的 C 编译器)
int regVal;
__asm {
mov [regVal], eax
}
这会将 eax
持有的任何内容加载到 regVal
如果您使用的是 GCC:
int regVal;
asm("movl %%eax, %0" : "=r"(regVal) :);
希望我做对了,我实际上并没有在 GCC 中进行内联汇编。
如果您真的想在执行某些代码时知道寄存器的值,使用调试器 更有意义。 (就像带有 layout asm; layout reg
的 gdb)。只看寄存器值而不看 asm 代码毫无意义。
请参阅 x86 wiki 中的链接和指南。
然而,使用 GNU 内联汇编,你可以做类似的事情
int eax_snapshot;
asm volatile (
""
: "=a" (eax_snapshot)
// no inputs, no clobbers
);
// any amount of intervening code
printf("eax was %x\n", eax_snapshot); // print whatever eax had at the point where the inline asm appeared in program order
The a
constraint表示操作数必须使用%eax
寄存器。然后内联汇编的主体变成空操作,因为您的输出操作数已经在您告诉编译器的位置。
不能保证 gcc 不会在 asm 块之前发出破坏 %eax
的代码。这就是为什么这样做毫无意义。 volatile
告诉编译器不要根据其他源指令重新排序 asm 语句,但优化可能仍然意味着早期的初始化被折叠到使用它们的后期代码中。或相反亦然。不过,将它与显式寄存器局部变量结合使用可能有助于避免 gcc 使用您选择的寄存器作为计算其他内容的临时寄存器。 (见下文)。
要设置寄存器,请使用带有 "a"
约束的内联汇编来强制该操作数的值在 %eax
中(或 %ebx
用于 "b"
约束,等等,请参阅 x86 特定机器约束的文档)在内联 asm 出现的位置。如果你真的想 使用 那个寄存器值,从内联 asm 语句里面做,否则寄存器可能被其他东西覆盖。
您也可以声明必须在特定寄存器中分配的变量,但我认为在没有初始化的情况下使用它们会使编译器不愉快。 (检测未定义行为的代码转换过程可能会导致问题。IDK。)语法为
register int eax asm ("eax"); // don't do this to read eax, it probably doesn't do what you think it would
The docs make it sound like using it for reading registers is not going to be a good idea. 但是,对于设置寄存器值,这可能有效。 (只要寄存器分配器接受了您的提示并将该变量保存在请求的寄存器中。)
register int foo asm("regname")
有时在实践中仍然可以像以前一样使用 GCC(在他们更改文档以不支持它之前,除了内联 asm)。但 IIRC 并非总是如此。
Clang 通常不会做任何事情,除了限制 "r"
或 "=r"
为扩展 asm 语句做出的选择,这是 GCC 文档保证的最低限度。因此,即使现在没有记录(因此不受支持)而没有实际 asm
语句的它的使用恰好适用于您的 GCC 版本,用于您的代码在程序中的某个时刻读取或写入寄存器,它很可能不能移植到 clang。
不要这样做除非你有很好的理由。它不太可能对正常编程有用。普通内联 asm,或调用函数,是结合 C 和 asm 的更好方法。这仅在您想要对程序的内部状态进行某种笨拙的检查时才有用。
在普通函数中以这种方式读取寄存器意味着编译器可以在您的 asm
语句之前放置一些指令来修改该寄存器,因此您无法确定它实际上来自 asm 调用者,如果你想发明一个自定义调用约定。
由于您没有给出用例来说明为什么要这样做,我强烈建议您不要编写执行此操作的代码。只是听起来很丑。
由于您使用的是 gcc,这里是有关在 gcc 中使用 asm 模板的文档:https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#AssemblerTemplate
为了设置寄存器的值,您可以执行以下操作:
uint64_t rax = 0;
__asm__ __volatile__("mov %0, %%rax\n\t"
: /* no output */
: "a" (rax)
: "%rax");
为了读取寄存器的值,您需要执行以下操作:
uint64_t rax;
__asm__ __volatile__("mov %%rax, %0\n\t"
: "=a"(rax)
: /* no input */
: /* no clobbers */);