5

有什么方法可以将 NULL 指针访问转换为 Linux 下的 C++ 异常?类似于 Java 中的 NullPointerException。我希望以下程序能够成功返回,而不是崩溃(假设编译器在编译期间无法计算出这个 NULL 指针访问):

class NullPointerException {};

void accessNullPointer(char* ptr) {
    *ptr = 0;
}

int main() {
    try {
        accessNullPointer(0);
    } catch (NullPointerException&) {
        return 1;
    }
    return 0;
}

我不期待任何标准的方法,因为 C++ 下的 NULL 指针访问是未定义的行为,只是想知道如何在 x86_64 Linux/GCC 下完成它。

我对此做了一些非常原始的研究,有可能:

  1. 在 Linux 下访问 NULL 指针时,会产生一个 SIGSEGV。
  2. 在 SIGSEGV 处理程序内部,程序的内存和寄存器信息将可用(如果sigaction()用于注册信号处理程序)。如果程序被反汇编,导致 SIGSEGV 的指令也可用。
  3. 修改程序的内存和/或寄存器,并创建/伪造一个异常实例(可能通过调用低级展开库函数,如_Unwind_RaiseException等)
  4. 最后从信号处理程序返回,希望程序会像抛出正常异常一样启动 C++ 堆栈展开过程。

这是来自 GCC 手册页(-fnon-call-exceptions)的引用:

生成允许捕获指令抛出异常的代码。请注意,这需要并非随处都存在的特定于平台的运行时支持。此外,它只允许捕获指令抛出异常,即内存引用或浮点指令。它不允许从诸如“SIGALRM”之类的任意信号处理程序中抛出异常。

看来这个“特定于平台的运行时”正是我想要的。有人知道 Linux/x86_64 这样的运行时吗?或者如果不存在这样的运行时,请给我一些关于如何实现这样的运行时的信息?

我希望该解决方案也能在多线程程序中工作。

4

2 回答 2

2

不,没有好的方法可以做到这一点,也不应该有。throw异常由源代码中的语句引发。这对于推理异常安全很重要:您可以查看代码并查看可以引发异常的位置,也许更重要的是,您可以查看代码并查看不会引发异常的位置。如果您所做的几乎任何事情都可能引发异常,那么编写异常安全的代码而不用catch子句将其弄乱就变得非常困难。微软在他们早期的 C++ 编译器中尝试了这一点:他们在操作系统的结构化异常之上搭载 C++ 异常处理,结果是一场灾难。

于 2016-06-07T13:09:54.230 回答
0

使用 注册替代信号堆栈signalaltstack()

使用 SA_SIGINFO 注册的信号处理程序处理程序的第三个参数是指向ucontext_t包含已保存寄存器的 a 的指针。信号处理程序应该调整它以模拟对函数的调用。然后该函数可以抛出异常。

潜在的复杂性包括需要保留被调用者保存的寄存器的值、x86-64 上的红色区域(可以禁用)以及某些 ISA(如 ARM)上的返回地址寄存器。

于 2016-08-03T21:51:22.770 回答