FS寄存器为空

时间:2016-01-20 18:21:28

标签: x86 seh

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

#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 寄存器的值。并且程序在执行 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;

可能是什么问题?

2 个答案:

答案 0 :(得分:1)

总结一下:

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

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

答案 1 :(得分:0)

除了64位问题之外,你无意中破坏了%eax(没有告诉编译器)。

我很惊讶它与您的原始资源一起工作。你很幸运,编译器无论如何都会立即覆盖%eax,可能是因为你使用-O0制作了令人讨厌的代码,这些代码永远不会在寄存器中保留任何东西。因此,只要您使用优化编译,您的代码就会崩溃。

你也很幸运,编译器没有在两个asm语句之间插入任何指令,这些语句破坏了%eax。永远不要指望在两个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)存储回内存,它会这样做。没有必要强制它这样做,%eax只是从prev生成了一个冗余的reg-reg移动到其他reg gcc决定保留movl的内容。

因为(根据Hans的说法),这段代码仅在32位有用,我将"e"后缀保留为存储到内存,因此当没有涉及寄存器来确定时,编译器可以确定操作数大小它。 (即,当值是全局或静态符号的编译时常量地址时。)

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

您可以删除约束的l部分和mov r/m64, imm32后缀,以便在第二个asm语句的32位和64位之间轻松移植,但代价是强制编译器浪费一条指令,首先将地址放入寄存器,即使它不需要。在64位模式下,"i"(英特尔语法)就是您所需要的。我使用an "e" constraint而不是if(sizeof(void*) == 4) { asm("movl ..."); } else { asm("movq..."); }约束,因为任意64位常量都不能编码,只能符号扩展32位。幸运的是,在默认代码模型中,符号地址可以安全地假设为低2GB。链接器可以将地址填入32位重定位。

为了使其在32位和64位之间可以使用立即操作数进行移植,您需要将其输出为64位的“movq”。我认为你必须使用预处理器来测试i386和amd64。 -O0也可能有用。这会使mov r/m32, imm32的代码变得非常丑陋,但是分支的伪造方面仍会汇集(到=0)并且永远不会运行。