FS 寄存器为空

FS register is null

我必须实现基于 SEH 的异常处理程序。 首先,我编写了以下示例代码,我在其中尝试使用 fs register.

注册异常处理程序
#include <iostream>
#include <exception>
#include <windows.h>

using namespace std;


EXCEPTION_DISPOSITION myHandler(
    _EXCEPTION_RECORD *ExcRecord,
    void * EstablisherFrame,
    _CONTEXT *ContextRecord,
    void * DispatcherContext)
{
    cout << "In the exception handler" << endl;
    cout << "Just a demo. exiting..." << endl;
    return ExceptionContinueExecution;
}

int main()
{
    cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!!

    EXCEPTION_REGISTRATION myExceptReg;
    EXCEPTION_REGISTRATION *pReg = &myExceptReg;
    myExceptReg.handler = myHandler;
    DWORD prev;

    asm("movl %fs:0 , %eax");
    asm("movl %%eax , %0": "=r" (prev));

    myExceptReg.prev = (EXCEPTION_REGISTRATION*) prev;

    asm ("movl %0, %%eax" : "=m" (pReg));
    asm("movl %eax , %fs:0");

    //      int* ptr = 0;
    //      exception e;

    return 0;
}

当我调试代码时,我看到 fs 寄存器的值是 zero。程序在执行 asm("movl %fs:0 , %eax");

后崩溃

以下是此代码的等效程序集示例。

000000000000401626:   mov     %rax,%rcx
000000000000401629:   callq   0x44d7a0 <std::ostream::operator<<(std::ostream& (*)(std::ostream&))>
32                  EXCEPTION_REGISTRATION *pReg = &myExceptReg;
00000000000040162e:   lea     0x20(%rbp),%rax
000000000000401632:   mov     %rax,0x18(%rbp)
33                  myExceptReg.handler = myHandler;
000000000000401636:   lea     -0x13d(%rip),%rax        # 0x401500     <myHandler(_EXCEPTION_RECORD*, void*, _CONTEXT*, void*)>
00000000000040163d:   mov     %rax,0x28(%rbp)
36                  asm("movl %fs:0 , %eax");
000000000000401641:   mov     %fs:0x0,%eax
37                  asm("movl %%eax , %0": "=r" (prev));
000000000000401649:   mov     %eax,%ebx
00000000000040164b:   mov     %ebx,0x3c(%rbp)
39                  myExceptReg.prev = (EXCEPTION_REGISTRATION*) prev;
00000000000040164e:   mov     0x3c(%rbp),%eax
000000000000401651:   mov     %rax,0x20(%rbp)
41                  asm ("movl %0, %%eax" : "=m" (pReg));
000000000000401655:   mov     0x18(%rbp),%eax
42                  asm("movl %eax , %fs:0");
000000000000401658:   mov     %eax,%fs:0x0
50                  return 0;

可能是什么问题?

总结一下:

调试输出显示正在为 64 位编译代码,并且(正如 Hans 指出的那样)所使用的异常处理样式仅对 32 位有效。确保代码针对 32 位编译可以解决问题。

如果这回答了您的问题,请单击左侧的复选标记以奖励业力。

除了 64 位问题之外,您还在无故破坏 %eax(没有告诉编译器)。

我很惊讶它能与您的原始来源一起使用。幸运的是,编译器立即覆盖了 %eax,这可能是因为您使用 -O0 编写了永远不会在寄存器中保存任何内容的讨厌代码。因此,一旦您在优化编译时,您的代码就会崩溃。

您还很幸运,编译器没有在破坏 %eax 的两个 asm 语句之间插入任何指令。永远不要指望两个 asm 块之间存在的寄存器或标志:在一个块中使用多个指令。

除此之外,asm ("movl %0, %%eax" : "=m" (pReg)); 告诉编译器 asm 语句覆盖内存中的 pReg,而不读取旧值。同样,只有 -O0 使您免于此错误,因为它没有优化掉 pReg = &myExceptReg;,如果 pReg 无论如何都将被覆盖,则不需要计算它。对于 -O1 或更高版本,您应该期望 pReg 未初始化


使用类似

的东西
void* prev;
asm volatile("mov %%fs:0, %0": "=r" (prev));
...
asm volatile("movl %0, %%fs:0" : : "re" (pReg) : "memory");
// reg or immediate source, but not memory, are encodable with a memory destination

当被询问时,编译器将负责让变量存在于寄存器中。如果它想在之后立即将 prev 存储回内存,它会这样做。没有必要强迫它这样做,"=r" (prev) 只是生成了一个冗余的 reg-reg 从 %eax 移动到任何其他 reg gcc 决定保留 prev 的地方。

由于(根据Hans的说法),这段代码只在32bit中有用,我在存储到内存的时候留下了movl后缀,所以编译器可以在没有的时候判断operand-size注册涉及确定它。 (即当值是全局或静态符号的 compile-time-constant 地址时。)

除此之外,此源代码可以编译为正确的 64 位代码,因为它没有明确命名任何寄存器。

您可以删除约束的 "e" 部分和 l 后缀,以便在 32 位和 64 位之间轻松移植第二个 asm 语句,但代价是强制编译器会浪费一条先将地址放入寄存器的指令,即使它不需要这样做。在 64 位模式下,mov r/m64, imm32(Intel 语法)就是您所需要的。我使用 an "e" constraint 而不是 "i" 约束,因为无法对任意 64 位常量进行编码,只能对 sign-extended 32 位进行编码。幸运的是,在默认代码模型中,符号地址可以安全地假设在低 2GB 中。链接器可以将地址填充到 32 位重定位中。

为了使其在 32 位和 64 位之间可移植地使用立即数操作数,您需要让它输出 64 位的“movq”。我认为您必须使用预处理器来测试 i386 与 amd64。 if(sizeof(void*) == 4) { asm("movl ..."); } else { asm("movq..."); } 也可能有效。这会使带有 -O0 的代码变得非常丑陋,但分支的伪造端仍然 assemble (至 mov r/m32, imm32)并且永远不会 运行.