Il semble que la fonction __cxa_throw dans KSCrashMonitor_CPPException.c ne soit pas appelée lorsqu'une exception C++ est levée dans un framework intégré, par exemple CrashLib dans l'application Crash-Tester.
Je n'ai pas été en mesure de trouver une solution à cela. Pour moi, il semble que toute bibliothèque intégrée utilisera toujours __cxa_throw de libc++, et non l'implémentation dans KSCrash.
De plus : lorsqu'une exception C++ non gérée est levée dans un framework intégré, KSCrash se bloque lors de la création de rapports, car stackCursor->advanceCursor est égal à NULL dans KSCrashReport.c:writeBacktrace().
Edit : Ajout d'informations sur le plantage de KSCrash lors de la création de rapports.
De la chance avec ça? J'essaie d'intégrer KSCrash dans mon application, mais je suis bloqué à cause du framework intégré.
S'il vous plaît laissez-moi savoir si vous avez du travail autour de cela.
Pas de chance, et je ne crois pas que ce soit possible, du moins pas dans notre cas d'utilisation. Voici ma compréhension de la situation :
La manière dont KSCrash enregistre la trace de la pile consiste à créer sa propre implémentation de __cxa_throw, qui autorise l'accès à la pile avant qu'elle ne soit déroulée par libc++. Cela fonctionne tant que KSCrash est lié statiquement au code C++, car l'éditeur de liens trouvera et utilisera __cxa_throw sous forme KSCrash au lieu de l'implémentation dans libc++. Mais dans le cas d'un framework intégré lié à libc++, l'implémentation __cxa_throw de libc++ sera utilisée, car le framework ne connaît pas le code dans KSCrash. Il existe peut-être une solution de contournement si vous contrôlez le framework intégré, mais cela ne fonctionnera pas dans notre cas d'utilisation.
Merci de me dire si vous trouvez une solution.
J'ai le contrôle sur le framework embarqué. Mais je ne sais pas comment y remédier.
J'ai aussi essayé PLCrashReporter, même problème.
Cela signifie-t-il que KSCrash devrait idéalement être construit comme une bibliothèque statique et lié à l'application, pour au moins intercepter les exceptions C++ lancées dans le code de l'application ?
Mais cela laisserait toujours des exceptions levées dans n'importe quelle bibliothèque dynamique non interceptée par KSCrash si je comprends bien votre rapport de bogue @pdrtrifork ? Si nous contrôlons les bibliothèques dynamiques intégrées, pourrions-nous lier une bibliothèque statique à chacune d'elles avec juste ce stub __cxa_throw, qui ne ferait que s'en remettre à un gestionnaire KSCrash partagé ?
Hah, donc en enquêtant sur cela, je me suis retrouvé ici http://stackoverflow.com/questions/36846628/conditionally-overriding-cxa-throw , qui a été ouvert par notre propre @kstenerud 😄
Quoi qu'il en soit, le PR #219 devrait au moins empêcher le plantage lors du rapport de plantage.
@kstenerud Je pense que ce problème devrait être renommé en quelque chose comme _"Traces de pile manquantes pour les exceptions C++ lancées à partir d'images non liées statiquement à KSCrash"_. Pour autant que je sache, le remplacement __cxa_throw
ne fonctionne que dans la même image que KSCrash, donc :
Ce dernier affecterait des clients tels que sentry-swift
, https://docs.sentry.io/clients/cocoa/ , qui embarque KSCrash comme framework.
Après une enquête plus approfondie, il semble que mon commentaire précédent était incorrect:
Je pense que ce problème devrait être renommé en quelque chose comme _"Traces de pile manquantes pour les exceptions C++ lancées à partir d'images non liées statiquement à KSCrash"_.
Dans le cas où KSCrash est lié à l'application principale en tant que bibliothèque statique :
MyApplication
( main.o
, libKSCrash.a
)libDynamicLibrary.dylib
( lib.o
)/usr/lib/libc++abi.dylib
Toutes les exceptions lancées à partir de MyApplication
entreront dans le libKSCrash.a::__cxa_throw
. De plus, puisque libKSCrash.a
déclare __cxa_throw
comme weak
, la liaison MyApplication
n'échoue pas si main.o
a son propre __cxa_throw
remplacement. Jusqu'ici tout va bien. Cependant, comme l'a observé ce rapport de bogue, toute exception émise depuis libDynamicLibrary.dylib
ne déclenchera pas libKSCrash.a::__cxa_throw
et se terminera par /usr/lib/libc++abi.dylib::__cxa_throw
.
Dans le cas où KSCrash est lié à l'application principale en tant que bibliothèque dynamique :
MyApplication
( main.o
)libKSCrash.dylib
/usr/lib/libc++abi.dylib
libDynamicLibrary.dylib
( lib.o
)/usr/lib/libc++abi.dylib
Toutes les exceptions levées depuis MyApplication
entreront dans le libKSCrash.dylib::__cxa_throw
, mais seulement si :
__cxa_throw
est exporté de libKSCrash.dylib
__cxa_throw
n'est pas marqué comme weak
Si __cxa_throw
est marqué comme weak
( 0000000000002e90 (__TEXT,__text) weak external ___cxa_throw
), alors lors de la liaison libKSCrash.dylib
l'éditeur de liens semble regarder /usr/lib/libc++abi.dylib
, trouver un non -weak __cxa_throw
, et conclure que libKSCrash.dylib::__cxa_throw
doit être ignoré. Lors de la recherche de symboles d'exécution au moment du lancement à partir de MyApplication
, libKSCrash.dylib::__cxa_throw
est alors ignoré.
Cela m'amène à croire que l'attribut weak
ne doit être ajouté conditionnellement que lors de la construction de KSCrash en tant que bibliothèque statique.
Comme pour le premier cas d'utilisation, toutes les exceptions levées depuis libDynamicLibrary.dylib
ne déclencheront pas libKSCrash.dylib::__cxa_throw
, et finiront par /usr/lib/libc++abi.dylib::__cxa_throw
.
Ce problème pourrait potentiellement être résolu avec quelques correctifs d'exécution de bas niveau des images chargées avec des références indéfinies à __cxa_throw
. En utilisant l'application de test référencée dans l'article, j'ai vérifié que la technique fonctionne également pour __cxa_throw
.
J'essaie de vérifier le crash dans un cadre de mon application. Lorsque j'essaie d'obtenir le journal des plantages, je constate que la trace de la pile ne montre pas le plantage réel, mais que KSCrash plante.
Est-ce le même problème ?
Discussion 0 plantée :
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 (abandon + 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 CoreFoundation 0x0000000182072344 0x18206a000 + 33604 (CFRunLoopRunSpecific + 544)
11 Services graphiques 0x0000000183f03f84 0x183ef9000 + 44932 (GSEventRunModal + 100)
12 UIKit 0x000000018b61e880 0x18b5ab000 + 473216 (UIApplicationMain + 208)
@torarnv Bonjour, pourriez-vous partager avec nous votre démo mach-o-hook réussie ? J'ai rencontré EXC_BAD_ACCESS lorsque j'essaie de mach_hook
__cxa_throw.
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();
}
}
essayez ceci... bonne chance
Je rencontre un plantage avec KSCrash ,
comment le résoudre ?
Nous avons créé un crochet dynamique implémentant des relations publiques mentionné ci-dessus par @huakucha : #375
Commentaire le plus utile
Après une enquête plus approfondie, il semble que mon commentaire précédent était incorrect:
Dans le cas où KSCrash est lié à l'application principale en tant que bibliothèque statique :
MyApplication
(main.o
,libKSCrash.a
)libDynamicLibrary.dylib
(lib.o
)/usr/lib/libc++abi.dylib
Toutes les exceptions lancées à partir de
MyApplication
entreront dans lelibKSCrash.a::__cxa_throw
. De plus, puisquelibKSCrash.a
déclare__cxa_throw
commeweak
, la liaisonMyApplication
n'échoue pas simain.o
a son propre__cxa_throw
remplacement. Jusqu'ici tout va bien. Cependant, comme l'a observé ce rapport de bogue, toute exception émise depuislibDynamicLibrary.dylib
ne déclenchera paslibKSCrash.a::__cxa_throw
et se terminera par/usr/lib/libc++abi.dylib::__cxa_throw
.Dans le cas où KSCrash est lié à l'application principale en tant que bibliothèque dynamique :
MyApplication
(main.o
)libKSCrash.dylib
/usr/lib/libc++abi.dylib
libDynamicLibrary.dylib
(lib.o
)/usr/lib/libc++abi.dylib
Toutes les exceptions levées depuis
MyApplication
entreront dans lelibKSCrash.dylib::__cxa_throw
, mais seulement si :__cxa_throw
est exporté delibKSCrash.dylib
__cxa_throw
n'est pas marqué commeweak
Si
__cxa_throw
est marqué commeweak
(0000000000002e90 (__TEXT,__text) weak external ___cxa_throw
), alors lors de la liaisonlibKSCrash.dylib
l'éditeur de liens semble regarder/usr/lib/libc++abi.dylib
, trouver un non -weak__cxa_throw
, et conclure quelibKSCrash.dylib::__cxa_throw
doit être ignoré. Lors de la recherche de symboles d'exécution au moment du lancement à partir deMyApplication
,libKSCrash.dylib::__cxa_throw
est alors ignoré.Cela m'amène à croire que l'attribut
weak
ne doit être ajouté conditionnellement que lors de la construction de KSCrash en tant que bibliothèque statique.Comme pour le premier cas d'utilisation, toutes les exceptions levées depuis
libDynamicLibrary.dylib
ne déclencheront paslibKSCrash.dylib::__cxa_throw
, et finiront par/usr/lib/libc++abi.dylib::__cxa_throw
.Ce problème pourrait potentiellement être résolu avec quelques correctifs d'exécution de bas niveau des images chargées avec des références indéfinies à
__cxa_throw
. En utilisant l'application de test référencée dans l'article, j'ai vérifié que la technique fonctionne également pour__cxa_throw
.