【问题标题】:FS register is nullFS 寄存器为空
【发布时间】:2016-01-20 18:21:27
【问题描述】:

我必须实现一个基于 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;

可能是什么问题?

【问题讨论】:

  • 这种异常处理方式仅在 32 位代码中有效。 64 位代码必须遵循 x64 ABI,异常处理是其中的一部分。它是基于表格的。
  • @Hans Passant,我正在尝试使用 32 位代码,但这里的 32 位代码有问题吗?这段代码中的什么使它看起来像是 64 位的?
  • 如果您的寄存器被命名为 rax、rbp 和 rcx 而不是 eax、ebp 和 ecx,那么您使用的是 64 位。
  • 我正在使用 mingw 4.6.2 编译 32 位 exe 的代码。汇编代码是我在调试时在反汇编窗口中找到的。
  • 构建时是否明确传递了 -m32 ?或者只是假设编译器默认生成 32 位?一个 64 位的可执行文件也可能解释为什么使用 fs 不起作用,因为 64 位通常使用 gs。

标签: x86 seh


【解决方案1】:

总结一下:

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

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

【讨论】:

  • 这远远不能涵盖 OP 的内联汇编的大量问题。 >.
【解决方案2】:

除了 64 位的问题,你无缘无故地破坏 %eax(没有告诉编译器)。

我很惊讶它完全适用于您的原始来源。你很幸运,编译器马上就覆盖了%eax,可能是因为你使用-O0 来编写讨厌的代码,这些代码永远不会在寄存器中长时间保存任何东西。所以你的代码会在你编译优化后立即损坏。

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

除此之外,asm ("movl %0, %%eax" : "=m" (pReg)); 告诉编译器 asm 语句会覆盖内存中的 pReg,而不读取旧值。同样,只有-O0 使您免于此错误,因为它没有优化掉pReg = &amp;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 的说法),此代码仅在 32 位中有用,因此我在 movl 后缀中保留了存储到内存的位置,因此编译器可以在不涉及寄存器来确定操作数大小时确定操作数大小。 (即,当值是全局或静态符号的编译时常量地址时。)

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

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

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

【讨论】:

    猜你喜欢
    • 2011-06-19
    • 1970-01-01
    • 2014-11-21
    • 1970-01-01
    • 2011-09-30
    • 1970-01-01
    • 2013-02-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多