Kscrash: Traces de pile incorrectes pour les exceptions C++ dans les frameworks intégrés

Créé le 20 févr. 2017  ·  12Commentaires  ·  Source: kstenerud/KSCrash

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.

Commentaire le plus utile

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 :

  1. __cxa_throw est exporté de libKSCrash.dylib
  2. __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 .

cooked_throw

Tous les 12 commentaires

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 :

  • si KSCrash est construit en tant que bibliothèque statique et lié à l'application principale, vous obtenez des backtraces d'exception C++ si l'exception est levée dans l'application principale, mais pas lorsqu'elle est lancée à partir de bibliothèques chargées dynamiquement
  • si KSCrash est construit en tant que bibliothèque statique et lié à une bibliothèque dynamique (wrapper), vous obtenez des backtraces d'exception C++ si l'exception est levée à partir de cette bibliothèque dynamique, mais pas lorsqu'elle est lancée à partir de l'application principale ou d'autres bibliothèques chargées dynamiquement
  • si KSCrash est construit en tant que bibliothèque partagée (framework), vous obtenez des backtraces d'exception C++ si l'exception est levée depuis KSCrash, mais pas lorsqu'elle est lancée depuis l'application principale ou d'autres bibliothèques chargées dynamiquement

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 :

  1. __cxa_throw est exporté de libKSCrash.dylib
  2. __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 .

cooked_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 ,
kscrash

comment le résoudre ?

Nous avons créé un crochet dynamique implémentant des relations publiques mentionné ci-dessus par @huakucha : #375

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

chzhij5 picture chzhij5  ·  17Commentaires

1t2t3t4t picture 1t2t3t4t  ·  3Commentaires

ferrous777 picture ferrous777  ·  30Commentaires

kstenerud picture kstenerud  ·  4Commentaires

happy201993 picture happy201993  ·  10Commentaires