C ++防止浮点寄存器在页面错误处理程序中被清零

C++ prevent floating point registers from being zeroed in page fault handler

我正在编写一个程序,要求浮点寄存器 ([xy]mm0-15) 在页面错误后不被覆盖。我编写了以下生成页面错误然后捕获它的示例代码:

#include <signal.h>
#include <iostream>

using namespace std;

void handle(int, siginfo_t*, void* vcontext);

// Page fault handler.  When this is called the fp registers are all zeroed
void handle(int, siginfo_t*, void* vcontext) {
  cout << "Get fp reg here";
  exit(0);
}

main (int argc, char *argv[]) {
  // Setup sigaction handler for page fault
  struct sigaction act;
  act.sa_sigaction = handle;
  sigaction(SIGSEGV, &act, 0); 

  // Generate page fault
  int i = 10;
  int* iPtr = &i;
  iPtr += 10000;
  cout << *iPtr; // This line will generate a page fault, then sigaction will redirect the program to `handle`.
}

然后我将程序放入 GDB 中,一次单步执行一条指令。我发现在 cout << *iPtr;cout << "Get fp reg here"; 之间(在 handle() 中)所有 fp 寄存器值都设置为零。我需要的是保留寄存器值,以便我可以在 handle()

中使用它们

附带说明一下,我使用的是 Red Hat。

FP 寄存器(以及整个 AVX 之前的 CPU 状态)可通过 ucontext_t 结构中 handle 的第三个参数获得。请参阅 sigaction(2) and sigreturn(2) 函数的文档。但是,YMM 寄存器并未明确包含在其中。

ucontext_t中存储的fpstate来自CPU的FXSAVE指令。这包括 XMM 寄存器(也是 YMM 寄存器的下半部分)。完整的 CPU 状态使用 XSAVE 指令或其变体之一保存。当启用适当的 CPU 功能集时,XSAVE 保存的扩展状态将在额外数据中包含 YMM 寄存器的上半部分。

YMM 寄存器的高半部分将存储在 EBX 寄存器中由 CPUID 指令(EAX = 0Dh,ECX = 2)给出的偏移量(当前为 576)。 YMM数据的长度在EAX寄存器中CPUID之后(目前是128)。

要确定各个 YMM 寄存器值,您需要将两半粘合在一起。

有一个 PDF 版本的幻灯片可以概述整个过程。