当嵌入式框架(例如 Crash-Tester 应用程序中的 CrashLib)中抛出 C++ 异常时,似乎没有调用 KSCrashMonitor_CPPException.c 中的 __cxa_throw 函数。
我一直无法想出解决方案。 在我看来,任何嵌入式库都将始终使用 libc++ 中的 __cxa_throw,而不是 KSCrash 中的实现。
另外:当嵌入式框架中抛出未处理的 C++ 异常时,KSCrash 将在报告期间崩溃,因为在 KSCrashReport.c:writeBacktrace() 中 stackCursor->advanceCursor 将等于 NULL。
编辑:添加了报告期间 KSCrash 崩溃的信息。
这有什么好运气吗? 我正在尝试将 KSCrash 集成到我的应用程序中,但由于嵌入式框架而卡住了。
如果您对此有任何解决方法,请告诉我。
没有运气,我不相信这是可能的,至少在我们的用例中是不可能的。 这是我对情况的理解:
KSCrash 记录堆栈跟踪的方式是创建它自己的 __cxa_throw 实现,它允许在堆栈被 libc++ 展开之前访问堆栈。 只要 KSCrash 与 C++ 代码静态链接,此方法就可以工作,因为链接器将查找并使用 KSCrash 形式的 __cxa_throw 而不是 libc++ 中的实现。 但是对于链接到 libc++ 的嵌入式框架,将使用来自 libc++ 的 __cxa_throw 实现,因为该框架不知道 KSCrash 中的代码。 如果您可以控制嵌入式框架,也许有一种解决方法,但这在我们的用例中不起作用。
如果您找到解决方案,请告诉我。
我可以控制嵌入式框架。 但不确定如何解决。
我也试过PLCrashReporter,同样的问题。
这是否意味着 KSCrash 应该理想地构建为静态库并链接到应用程序中,以至少捕获应用程序代码中抛出的 C++ 异常?
但是,如果我正确理解了您的错误报告@pdrtrifork ,那仍然会在任何动态库中抛出任何未被 KSCrash 捕获的异常? 如果我们控制嵌入的动态库,我们是否可以仅使用 __cxa_throw 存根将静态库链接到它们中的每一个,它只会推迟到共享的 KSCrash 处理程序?
哈,所以在调查这个问题时,我最终来到了http://stackoverflow.com/questions/36846628/conditionally-overriding-cxa-throw ,这是由我们自己的@kstenerud开设的😄
无论如何,PR #219 至少应该防止在崩溃报告期间崩溃。
@kstenerud我认为这个问题应该重命名为 _"Missing stack traces for C++ exceptions 从未与 KSCrash 静态链接的图像中抛出"_。 据我所知, __cxa_throw
覆盖仅适用于与 KSCrash 相同的图像,因此:
后者会影响诸如sentry-swift
、 https://docs.sentry.io/clients/cocoa/之类的客户端,它们将 KSCrash 嵌入为框架。
经过进一步调查,我之前的评论似乎不正确:
我认为这个问题应该重命名为 _"Missing stack traces for C++ exceptions 从未与 KSCrash 静态链接的图像中抛出"_。
在将 KSCrash 作为静态库链接到主应用程序的情况下:
MyApplication
( main.o
, libKSCrash.a
)libDynamicLibrary.dylib
( lib.o
)/usr/lib/libc++abi.dylib
MyApplication
抛出的任何异常都将进入libKSCrash.a::__cxa_throw
。 此外,由于libKSCrash.a
将__cxa_throw
$ 声明为weak
,因此如果main.o
有自己的__cxa_throw
,则链接MyApplication
不会失败__cxa_throw
覆盖。 到现在为止还挺好。 但是,正如这个错误报告所观察到的,从libDynamicLibrary.dylib
引发的任何异常都不会触发libKSCrash.a::__cxa_throw
,而是会以/usr/lib/libc++abi.dylib::__cxa_throw
结束。
在将 KSCrash 作为动态库链接到主应用程序的情况下:
MyApplication
( main.o
)libKSCrash.dylib
/usr/lib/libc++abi.dylib
libDynamicLibrary.dylib
( lib.o
)/usr/lib/libc++abi.dylib
MyApplication
抛出的任何异常都将进入libKSCrash.dylib::__cxa_throw
,但前提是:
__cxa_throw
是从libKSCrash.dylib
导出的__cxa_throw
未标记为weak
如果__cxa_throw
被标记为weak
( 0000000000002e90 (__TEXT,__text) weak external ___cxa_throw
),那么在链接libKSCrash.dylib
时,链接器似乎会查看/usr/lib/libc++abi.dylib
,找到一个非-weak __cxa_throw
,并得出结论libKSCrash.dylib::__cxa_throw
应该被忽略。 在从MyApplication
的抛出时间查找运行时符号期间,然后忽略libKSCrash.dylib::__cxa_throw
。
这让我相信只有在将 KSCrash 构建为静态库时才应有条件地添加weak
属性。
与第一个用例一样,从libDynamicLibrary.dylib
引发的任何异常都不会触发libKSCrash.dylib::__cxa_throw
,并且会以/usr/lib/libc++abi.dylib::__cxa_throw
结束。
这个问题可能会通过对未定义的__cxa_throw
引用的加载图像进行一些低级别的运行时修补来解决。 使用文章中引用的测试应用程序,我已经验证该技术确实也适用于__cxa_throw
。
我正在尝试在我的应用程序的框架中检查崩溃。 当我尝试获取崩溃日志时,我看到堆栈跟踪没有显示实际的崩溃,而是 KSCrash 正在崩溃。
这是同一个问题吗?
线程 0 崩溃:
0 libsystem_kernel.dylib 0x0000000181cc5348 0x181ca4000 + 136008 (__pthread_kill + 8)
1 libsystem_pthread.dylib 0x0000000181ddd7a4 0x181dd6000 + 30628 (+ 360)
2 libsystem_c.dylib 0x0000000181c34fd8 0x181bd2000 + 405464(中止 + 140)
3 libc++abi.dylib 0x0000000181698068 0x181696000 + 8296 (+ 132)
4 libc++abi.dylib 0x0000000181698210 0x181696000 + 8720 ( + 304)
5 libobjc.A.dylib 0x00000001816c0810 0x1816b8000 + 34832 (+ 124)
6 KSCrash 0x00000001054a0590 0x10549c000 + 17808 (kscm_cppexception_getAPI + 280)
7 libc++abi.dylib 0x00000001816b054c 0x181696000 + 107852 (+ 16)
8 libc++abi.dylib 0x00000001816b0158 0x181696000 + 106840 (__cxa_rethrow + 144)
9 libobjc.A.dylib 0x00000001816c06e8 0x1816b8000 + 34536 (objc_exception_rethrow + 44)
10 核心基础 0x0000000182072344 0x18206a000 + 33604 (CFRunLoopRunSpecific + 544)
11 图形服务 0x0000000183f03f84 0x183ef9000 + 44932 (GSEventRunModal + 100)
12 UIKit 0x000000018b61e880 0x18b5ab000 + 473216 (UIApplicationMain + 208)
@torarnv嗨,你能和我们分享你成功的 mach-o-hook 演示吗? 当我尝试mach_hook
__cxa_throw 时遇到了 EXC_BAD_ACCESS。
void ter_handler(){
printf("custom handler\n");
}
void test(){
throw std::runtime_error("test function");
}
static void (*orig_throw)(void * thrown_exception, std::type_info *tinfo, void (*dest)(void *));
void hooked_throw(void * thrown_exception, std::type_info *tinfo, void (*dest)(void *)){
printf("hooked_throw...\n");
return orig_throw(thrown_exception, tinfo, dest);
}
int main(int argc, char * argv[])
{
<strong i="5">@autoreleasepool</strong> {
struct rebinding binds[1];
struct rebinding bind1 = {"__cxa_throw", (void *)hooked_throw, (void **)&orig_throw};
binds[0] = bind1;
rebind_symbols(binds, 1);
std::set_terminate(ter_handler);
try {
throw std::runtime_error("test error");
}
catch (...){
printf ("catch exception\n");
}
test();
}
}
试试这个……祝你好运
我遇到了一次 KSCrash 崩溃,
如何解决?
我们已经创建了一个 PR 实现上面提到的@huakucha的动态钩子:#375
最有用的评论
经过进一步调查,我之前的评论似乎不正确:
在将 KSCrash 作为静态库链接到主应用程序的情况下:
MyApplication
(main.o
,libKSCrash.a
)libDynamicLibrary.dylib
(lib.o
)/usr/lib/libc++abi.dylib
MyApplication
抛出的任何异常都将进入libKSCrash.a::__cxa_throw
。 此外,由于libKSCrash.a
将__cxa_throw
$ 声明为weak
,因此如果main.o
有自己的__cxa_throw
,则链接MyApplication
不会失败__cxa_throw
覆盖。 到现在为止还挺好。 但是,正如这个错误报告所观察到的,从libDynamicLibrary.dylib
引发的任何异常都不会触发libKSCrash.a::__cxa_throw
,而是会以/usr/lib/libc++abi.dylib::__cxa_throw
结束。在将 KSCrash 作为动态库链接到主应用程序的情况下:
MyApplication
(main.o
)libKSCrash.dylib
/usr/lib/libc++abi.dylib
libDynamicLibrary.dylib
(lib.o
)/usr/lib/libc++abi.dylib
MyApplication
抛出的任何异常都将进入libKSCrash.dylib::__cxa_throw
,但前提是:__cxa_throw
是从libKSCrash.dylib
导出的__cxa_throw
未标记为weak
如果
__cxa_throw
被标记为weak
(0000000000002e90 (__TEXT,__text) weak external ___cxa_throw
),那么在链接libKSCrash.dylib
时,链接器似乎会查看/usr/lib/libc++abi.dylib
,找到一个非-weak__cxa_throw
,并得出结论libKSCrash.dylib::__cxa_throw
应该被忽略。 在从MyApplication
的抛出时间查找运行时符号期间,然后忽略libKSCrash.dylib::__cxa_throw
。这让我相信只有在将 KSCrash 构建为静态库时才应有条件地添加
weak
属性。与第一个用例一样,从
libDynamicLibrary.dylib
引发的任何异常都不会触发libKSCrash.dylib::__cxa_throw
,并且会以/usr/lib/libc++abi.dylib::__cxa_throw
结束。这个问题可能会通过对未定义的
__cxa_throw
引用的加载图像进行一些低级别的运行时修补来解决。 使用文章中引用的测试应用程序,我已经验证该技术确实也适用于__cxa_throw
。