使用Windows SEH和GCC label-as-value跳转到本地错误处理程序

时间:2013-04-24 02:31:03

标签: c windows gcc seh

我需要对失败案例引发异常的一些代码进行单元测试。简而言之,我需要通过展开堆栈帧或对错误进行本地跳转来处理异常。使用MSVC不是一种选择。

AddVectoredExceptionHandler的MSDN example表示我可以修改eip,然后返回EXCEPTION_CONTINUE_EXECUTION来执行本地跳转。显而易见的问题是跳到什么地址。 GCC的Label as Value功能似乎应该就是这样。

如果从错误功能返回一个,则以下示例有效。但是,如果添加了第二个return语句,则跳转的偏移量会非常小并且跳转失败。为什么呢?

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0502
#endif

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

static bool handler_called;
static DWORD handler_eip;

static LONG WINAPI local_handler(struct _EXCEPTION_POINTERS* ExceptionInfo) {
   handler_called = true;
   PCONTEXT Context = ExceptionInfo->ContextRecord;
   Context->Eip = handler_eip;
   return EXCEPTION_CONTINUE_EXECUTION;
}

static void badcall(void) {
   handler_called = false;
   handler_eip = &&fail;

   int value = 100;
   int zero = 0;
   value = value/0;
   printf("not handled.\n");
   assert(false);
   //return; // Uncomment this to break the handler

   fail:
   printf("error handled.\n");
   return;
}

int main(int argc, char* argv[]) {
   void* old_handler = SetUnhandledExceptionFilter(&local_handler);
   badcall();
   SetUnhandledExceptionFilter(old_handler);
   return(0);
}

1 个答案:

答案 0 :(得分:1)

GCC的死代码消除似乎是罪魁祸首 - 与SEH无关。当无法访问函数的底部块时,将删除这些语句。 GCC随后在我们获取地址的位置任意创建标签。

由于我们直接操作程序流,C的规则告诉我们,当编译器做这样的古怪时,我们不允许抱怨。

这是一个更小的例子:

static void* handler_eip;
static void badcall(void) {
   handler_eip = &&fail;

   int value = 100/0;
   printf("not handled.\n");
   //goto fail;
   return;

   fail:
   printf("error handled.\n");
   return;
}

注意带有goto的L2的位置:

_badcall:
    pushl    %ebp
    movl    %esp, %ebp
    subl    $56, %esp
    movl    $L2, _handler_eip
    movl    $100, %eax
    movl    $0, -28(%ebp)
    movl    %eax, %edx
    sarl    $31, %edx
    idivl    -28(%ebp)
    movl    %eax, -12(%ebp)
    movl    $LC0, (%esp)
    call    _puts
    nop
L2:
    movl    $LC1, (%esp)
    call    _puts
    nop
    leave
    ret

没有:

_badcall:
    pushl    %ebp
    movl    %esp, %ebp
    subl    $56, %esp
L2:
    movl    $L2, _handler_eip
    movl    $100, %eax
    movl    $0, -28(%ebp)
    movl    %eax, %edx
    sarl    $31, %edx
    idivl    -28(%ebp)
    movl    %eax, -12(%ebp)
    movl    $LC0, (%esp)
    call    _puts
    nop
    leave
    ret
相关问题