从C访问CPU寄存器

从C访问CPU寄存器,c,inline-assembly,cpu-registers,C,Inline Assembly,Cpu Registers,最近我一直在玩C中的内联assmbly,想知道是否可以直接从变量访问寄存器 大概是这样的: volatile uint64_t* flags = RFLAGS; 其中RFLAGS是CPU标志寄存器。显然,上面的代码没有编译,但我想知道是否有类似的方法来实现所需的结果 用gcc编译Ubuntu x86_64当然可以。您可以PUSHF、PUSHFD或PUSHFQ标记并将它们弹出到另一个寄存器中。例如: unsigned int flags; __asm{ pushfd pop edx

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

大概是这样的:

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


用gcc编译Ubuntu x86_64当然可以。您可以
PUSHF
PUSHFD
PUSHFQ
标记并将它们弹出到另一个寄存器中。例如:

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");

从那里你可以在闲暇时看到它们

您可以通过内联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输入/输出约束的寄存器使用,并且一些目标具有永久保存在通用寄存器中的特殊值(线程本地存储指针是最常见的),这在某些情况下可能很有用。

此答案适用于MSVC、Turbo C、,或者其他不能用于OP系统的编译器。这通常是错误的。除非标志由同一asm块中的asm指令生成,否则无法从内联asm有意义地访问标志。你当然不能从C算术中得到结果。我并不反对这些标志只会反映当前函数和上下文中的状态,并且只有在你真正关心它们的情况下才最好使用汇编,但直截了当地说这是“一般错误”是不正确的。然而,由于代码的格式冒犯了你,我很乐意添加一个与GCC兼容的版本。GCC版本仍然是错误的。它在函数执行过程中的某个随机点获取标志。OP想要什么是不可能的,因为无法将“算术运算X的标志”指定为内联asm的输入约束。还要注意的是,您不能仅通过名称从asm访问变量。我认为我的评论不足以解释我的反对意见,因此我添加了一个答案。虽然您完全正确,但访问由编译器生成的算术指令设置的状态标志将是完全无用的(因为不知道是什么设置了它们),同样,干涉方向标志DF也是愚蠢的(因为编译器生成的代码可能需要以另一种方式设置方向标志),读取系统标志可能很有用。(但这种情况很少发生,而且在实践中,只有当一些完整的汇编代码——比如条目{,{,{u 32,{u 64}.S——已经将寄存器藏在方便的地方时才会发生。)是的,我是个挑剔的人。