3

我的项目是用 C++ 编写的,它利用动态生成的代码将一些东西粘合在一起(使用 Fabrice Bellard 的TCC和一些手动生成的程序集 thunk)。动态生成的代码有时会跳转到用 C++ 实现的“运行时助手”并返回。

有一个功能允许完全中止动态生成的代码,无论它在哪里,跳回 C++(调用者)。为了实现这一点,我只是使用 C++ 异常:运行时帮助程序(伪装成 C 函数)简单地引发 C++ 异常,并通过生成的函数传播回 C++。我正在使用SJLJ,到目前为止一切正常,但我不想依赖特定的实现(我读到只有 SJLJ 才安全)。

除了上面的中止方案,我的 C++ 代码主要在危急情况下使用异常,它不用于通用控制流。但是,我依靠 RAII 自动销毁堆栈上的对象。

我的问题是:如果 setjmp 在调用动态生成的函数之前正确设置,并且 longjmp 永远不会通过依赖 RAII 的 C++ 函数传播(我必须确保没有用 C++ 实现的运行时助手使用它)并且总是落在 setjmp (在函数之前设置)?

或者 C++ 是如此脆弱以至于不能保证它能够正常工作并且会破坏某些东西?或者也许只有在抛出实际异常时 C++ 才会中断?如果异常在本地抛出并立即捕获(在生成的程序集调用的运行时助手中)怎么办,它安全吗?或者可能只是因为堆栈中有一些外来帧,它会拒绝工作?

例如:

jmp_buf buf; // thread-local
char* msg;   // thread-local

// ... some C++ code here, potentially some RAII thingy

GeneratedFunc func = (GeneratedFunc)compile_stuff();
if (!setjmp(buf)) {
    // somewhere deep inside, it calls longjmp to jump back to the top function in case a problem happens
    func();
} else {
    printf("error: %s\n", msg);
    // do something about the error here
}

// some other C++ code
4

1 回答 1

1

在理论上和实践上使用 longjmp/setjmp 是否安全,前提是 setjmp 在调用动态生成的函数之前设置好,并且 longjmp 永远不会通过依赖 RAII 的 C++ 函数传播(我必须确保在C++ 使用它)并且总是落在 setjmp (设置在函数之前)?

该标准的 18.10/4 说:

...longjmp(jmp_buf jbuf, int val)在本国际标准中有更多限制行为。如果将 and 替换为and ,则setjmp/longjmp调用对具有未定义的行为,并且将为任何自动对象调用任何非平凡的析构函数。setjmplongjmpcatchthrow

因此,它不仅仅是 RAII,而是任何带有非平凡析构函数的堆栈托管对象(即“资源”可能在构造获取但仍需要在销毁期间释放,或者除了资源释放之外可能存在销毁的副作用,例如日志记录)。

或者 C++ 是如此脆弱以至于不能保证它能够正常工作并且会破坏某些东西?

它应该受到上述关于琐碎析构函数的警告(这是一个很大的限制)。

或者也许只有在抛出实际异常时 C++ 才会中断?如果异常在本地抛出并立即捕获(在生成的程序集调用的运行时助手中)怎么办,它安全吗?

setjmp这与/longjmp行为无关。如果您在正常的 C++ 编译器生成的代码中抛出和捕获,则执行(重新)输入动态生成的代码不应该有任何残留/后续问题。相同的方法用于调用/从 C 调用的包装 C++ 库;异常可能会在 C++ 库的边界上被捕获并转换为 C 可以处理的错误代码。

于 2015-02-12T03:30:44.867 回答