从C访问CPU寄存器

时间:2015-05-09 00:00:59

标签: c inline-assembly cpu-registers

最近我一直在使用C中的内联汇编,并想知道我是否可以直接从变量访问寄存器

这样的事情:

volatile uint64_t* flags = RFLAGS;

其中RFLAGS是CPU标志寄存器。显然,上面的代码没有编译,但我想知道是否有类似的方法来实现所需的结果。

使用gcc

编译Ubuntu x86_64

2 个答案:

答案 0 :(得分:7)

您可以通过内联asm获取标志寄存器的值,但此操作无用,因为您无法控制与其他操作相关的访问顺序。特别是,你可能想要的是因为某些特定算术运算产生的标志在你的asm块的开头是可用的,但是没有办法向编译器表达那个约束。例如,假设您写道:

z = x + y;
__asm__ ( "pushf ; pop %0" : "=r"(flags) );

您可能希望添加产生的标记可用。但是,编译器可能选择:

  • 在asm之后对算术进行重新排序,因为两者都没有依赖于另一个的结果。
  • 在中间用add / sub调整堆栈指针,破坏标志。
  • 使用lea代替add来实现添加,不产生任何标记。
  • 完全基于确定未使用结果而省略添加。

同样的原则适用于访问可能由编译器生成的代码修改的任何寄存器。 语法(在GCC /“GNU C”中)用于访问不受此问题影响的寄存器;它看起来像:

register int var __asm__("regname");

其中regname被寄存器的名称替换。这在大多数目标上基本上没用,但是它可以允许您控制输入/输出约束的寄存器使用到asm,并且一些目标具有永久保存在通用寄存器中的特殊值(线程本地存储指针是最常见的)这在某些情况下可能有用。

答案 1 :(得分:3)

是的,当然。您可以PUSHFPUSHFDPUSHFQ标记,然后将它们弹出到另一个寄存器中。例如:

unsigned int flags;
__asm{
  pushfd
  pop edx
  mov flags, edx
}

对于使用AT& T语法的Ubuntu下的gcc,您可能会发现以下内容更容易使用:

unsigned int flags:
__asm__("pushf\n\t"
        "pop edx\n\t"
        "movl edx, flags");

从那里你可以随意看看它们!