Kscrash: Falsche Stacktraces für C++-Ausnahmen in eingebetteten Frameworks

Erstellt am 20. Feb. 2017  ·  12Kommentare  ·  Quelle: kstenerud/KSCrash

Es sieht so aus, als ob die __cxa_throw-Funktion in KSCrashMonitor_CPPException.c nicht aufgerufen wird, wenn eine C++-Ausnahme in einem eingebetteten Framework geworfen wird, zB CrashLib in der Crash-Tester-App.

Ich bin nicht in der Lage gewesen, eine Lösung dafür zu finden. Für mich sieht es so aus, als würde jede eingebettete Bibliothek immer __cxa_throw von libc++ verwenden und nicht die Implementierung in KSCrash.

Zusätzlich: Wenn eine unbehandelte C++-Ausnahme in einem eingebetteten Framework ausgelöst wird, stürzt KSCrash während der Berichterstellung ab, da stackCursor->advanceCursor in KSCrashReport.c:writeBacktrace() gleich NULL ist.

Bearbeiten: Information hinzugefügt, dass KSCrash während der Berichterstellung abstürzt.

Hilfreichster Kommentar

Nach weiteren Nachforschungen scheint mein vorheriger Kommentar falsch gewesen zu sein:

Ich denke, dieses Problem sollte umbenannt werden in so etwas wie _"Fehlende Stack-Traces für C++-Ausnahmen, die von Bildern ausgelöst werden, die nicht statisch mit KSCrash verknüpft sind"_.

Falls KSCrash als statische Bibliothek in die Hauptanwendung eingebunden wird:

  • MyApplication ( main.o , libKSCrash.a )

    • libDynamicLibrary.dylib ( lib.o )

    • /usr/lib/libc++abi.dylib

Alle Ausnahmen, die von MyApplication ausgelöst werden, werden in libKSCrash.a::__cxa_throw . Da außerdem libKSCrash.a __cxa_throw als weak $ deklariert, schlägt das Verknüpfen MyApplication nicht fehl, wenn main.o ein eigenes __cxa_throw hat libDynamicLibrary.dylib ausgelösten Ausnahmen nicht libKSCrash.a::__cxa_throw aus und landen in /usr/lib/libc++abi.dylib::__cxa_throw .

Falls KSCrash als dynamische Bibliothek in die Hauptanwendung eingebunden wird:

  • MyApplication ( main.o )

    • libKSCrash.dylib

    • /usr/lib/libc++abi.dylib

    • libDynamicLibrary.dylib ( lib.o )

    • /usr/lib/libc++abi.dylib

Alle Ausnahmen, die von MyApplication ausgelöst werden, werden in libKSCrash.dylib::__cxa_throw , aber nur, wenn:

  1. __cxa_throw wird aus libKSCrash.dylib exportiert
  2. __cxa_throw ist nicht als weak gekennzeichnet

Wenn __cxa_throw als weak ( 0000000000002e90 (__TEXT,__text) weak external ___cxa_throw ) markiert ist, dann scheint der Linker beim Verknüpfen libKSCrash.dylib nach /usr/lib/libc++abi.dylib zu suchen und einen Non zu finden -weak __cxa_throw und schließen daraus, dass libKSCrash.dylib::__cxa_throw ignoriert werden sollte. Während der Laufzeitsymbolsuche zur Auslösezeit von MyApplication wird libKSCrash.dylib::__cxa_throw dann ignoriert.

Dies lässt mich glauben, dass das Attribut weak nur bedingt hinzugefügt werden sollte, wenn KSCrash als statische Bibliothek erstellt wird.

Wie beim ersten Anwendungsfall lösen alle von libDynamicLibrary.dylib ausgelösten Ausnahmen nicht libKSCrash.dylib::__cxa_throw aus und landen in /usr/lib/libc++abi.dylib::__cxa_throw .

Dieses Problem könnte möglicherweise durch ein Low-Level-Patch zur Laufzeit von geladenen Images mit undefinierten Verweisen auf __cxa_throw behoben werden. Mit der Testanwendung, auf die im Artikel verwiesen wird, habe ich verifiziert, dass die Technik tatsächlich auch für __cxa_throw funktioniert.

cooked_throw

Alle 12 Kommentare

Glück damit? Ich versuche, KSCrash in meine App zu integrieren, bin aber wegen des eingebetteten Frameworks hängen geblieben.
Bitte lassen Sie mich wissen, ob Sie dafür eine Arbeit haben.

Kein Glück, und ich glaube nicht, dass es möglich ist, zumindest nicht in unserem Anwendungsfall. So verstehe ich die Situation:

Die Art und Weise, wie KSCrash den Stack-Trace aufzeichnet, besteht darin, eine eigene Implementierung von __cxa_throw zu erstellen, die den Zugriff auf den Stack ermöglicht, bevor er von libc++ entladen wird. Dies funktioniert, solange KSCrash statisch mit dem C++-Code gelinkt ist, da der Linker __cxa_throw von KSCrash anstelle der Implementierung in libc++ finden und verwenden wird. Aber im Fall eines eingebetteten Frameworks, das auf libc++ verlinkt, wird die __cxa_throw-Implementierung von libc++ verwendet, da das Framework den Code in KSCrash nicht kennt. Vielleicht gibt es eine Problemumgehung, wenn Sie die Kontrolle über das eingebettete Framework haben, aber das funktioniert in unserem Anwendungsfall nicht.

Bitte lassen Sie mich wissen, wenn Sie eine Lösung finden.

Ich habe die Kontrolle über das eingebettete Framework. Aber nicht sicher, wie man es repariert.
Ich habe auch PLCrashReporter ausprobiert, das gleiche Problem damit.

Bedeutet dies, dass KSCrash idealerweise als statische Bibliothek erstellt und in die Anwendung eingebunden werden sollte, um zumindest im Anwendungscode ausgelöste C++-Ausnahmen abzufangen?

Aber das würde immer noch Ausnahmen hinterlassen, die in einer dynamischen Bibliothek ausgelöst werden, die von KSCrash nicht erfasst wird, wenn ich Ihren Fehlerbericht richtig verstehe @pdrtrifork ? Wenn wir die eingebetteten dynamischen Bibliotheken kontrollieren, könnten wir eine statische Bibliothek mit nur diesem __cxa_throw-Stub zu jeder von ihnen verlinken, was nur zu einem gemeinsam genutzten KSCrash-Handler führen würde?

Hah, also bin ich bei der Untersuchung hier gelandet http://stackoverflow.com/questions/36846628/conditionally-overriding-cxa-throw , das von unserem eigenen @kstenerud 😄 geöffnet wurde

Wie auch immer, PR #219 sollte zumindest Abstürze während der Absturzmeldung verhindern.

@kstenerud Ich denke, dieses Problem sollte umbenannt werden in etwas wie _"Fehlende Stack-Traces für C++-Ausnahmen, die von Bildern ausgelöst werden, die nicht statisch mit KSCrash verknüpft sind"_. Soweit ich das beurteilen kann, funktioniert die Überschreibung __cxa_throw nur im selben Image wie KSCrash, also:

  • Wenn KSCrash als statische Bibliothek erstellt und mit der Haupt-App verknüpft ist, erhalten Sie Rückverfolgungen für C++-Ausnahmen, wenn die Ausnahme in der Haupt-App ausgelöst wird, aber nicht, wenn sie von dynamisch geladenen Bibliotheken ausgelöst wird
  • Wenn KSCrash als statische Bibliothek erstellt und mit einer dynamischen (Wrapper-)Bibliothek verknüpft ist, erhalten Sie C++-Ausnahmerückverfolgungen, wenn die Ausnahme von dieser dynamischen Bibliothek ausgelöst wird, aber nicht, wenn sie von der Haupt-App oder anderen dynamisch geladenen Bibliotheken ausgelöst wird
  • Wenn KSCrash als gemeinsam genutzte Bibliothek (Framework) erstellt wurde, erhalten Sie C++-Ausnahmerückverfolgungen, wenn die Ausnahme von KSCrash ausgelöst wird, aber nicht, wenn sie von der Haupt-App oder anderen dynamisch geladenen Bibliotheken ausgelöst wird

Letzteres würde Clients wie sentry-swift , https://docs.sentry.io/clients/cocoa/ betreffen, die KSCrash als Framework einbetten.

Nach weiteren Nachforschungen scheint mein vorheriger Kommentar falsch gewesen zu sein:

Ich denke, dieses Problem sollte umbenannt werden in so etwas wie _"Fehlende Stack-Traces für C++-Ausnahmen, die von Bildern ausgelöst werden, die nicht statisch mit KSCrash verknüpft sind"_.

Falls KSCrash als statische Bibliothek in die Hauptanwendung eingebunden wird:

  • MyApplication ( main.o , libKSCrash.a )

    • libDynamicLibrary.dylib ( lib.o )

    • /usr/lib/libc++abi.dylib

Alle Ausnahmen, die von MyApplication ausgelöst werden, werden in libKSCrash.a::__cxa_throw . Da außerdem libKSCrash.a __cxa_throw als weak $ deklariert, schlägt das Verknüpfen MyApplication nicht fehl, wenn main.o ein eigenes __cxa_throw hat libDynamicLibrary.dylib ausgelösten Ausnahmen nicht libKSCrash.a::__cxa_throw aus und landen in /usr/lib/libc++abi.dylib::__cxa_throw .

Falls KSCrash als dynamische Bibliothek in die Hauptanwendung eingebunden wird:

  • MyApplication ( main.o )

    • libKSCrash.dylib

    • /usr/lib/libc++abi.dylib

    • libDynamicLibrary.dylib ( lib.o )

    • /usr/lib/libc++abi.dylib

Alle Ausnahmen, die von MyApplication ausgelöst werden, werden in libKSCrash.dylib::__cxa_throw , aber nur, wenn:

  1. __cxa_throw wird aus libKSCrash.dylib exportiert
  2. __cxa_throw ist nicht als weak gekennzeichnet

Wenn __cxa_throw als weak ( 0000000000002e90 (__TEXT,__text) weak external ___cxa_throw ) markiert ist, dann scheint der Linker beim Verknüpfen libKSCrash.dylib nach /usr/lib/libc++abi.dylib zu suchen und einen Non zu finden -weak __cxa_throw und schließen daraus, dass libKSCrash.dylib::__cxa_throw ignoriert werden sollte. Während der Laufzeitsymbolsuche zur Auslösezeit von MyApplication wird libKSCrash.dylib::__cxa_throw dann ignoriert.

Dies lässt mich glauben, dass das Attribut weak nur bedingt hinzugefügt werden sollte, wenn KSCrash als statische Bibliothek erstellt wird.

Wie beim ersten Anwendungsfall lösen alle von libDynamicLibrary.dylib ausgelösten Ausnahmen nicht libKSCrash.dylib::__cxa_throw aus und landen in /usr/lib/libc++abi.dylib::__cxa_throw .

Dieses Problem könnte möglicherweise durch ein Low-Level-Patch zur Laufzeit von geladenen Images mit undefinierten Verweisen auf __cxa_throw behoben werden. Mit der Testanwendung, auf die im Artikel verwiesen wird, habe ich verifiziert, dass die Technik tatsächlich auch für __cxa_throw funktioniert.

cooked_throw

Ich versuche, den Absturz in einem Framework in meiner App zu überprüfen. Wenn ich versuche, das Absturzprotokoll abzurufen, sehe ich, dass der Stack-Trace nicht den tatsächlichen Absturz anzeigt, stattdessen stürzt KSCrash ab.

Ist das das gleiche Problem?

Thread 0 abgestürzt:
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 (Abbruch + 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 GraphicsServices 0x0000000183f03f84 0x183ef9000 + 44932 (GSEventRunModal + 100)
12 UIKit 0x000000018b61e880 0x18b5ab000 + 473216 (UIApplicationMain + 208)

@torarnv Hallo, könntest du deine erfolgreiche Mach-o-Hook-Demo mit uns teilen? Ich bin auf EXC_BAD_ACCESS gestoßen, als ich versuche, mach_hook __cxa_throw zu machen.

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();
    }
}

versuchen Sie dies ... viel Glück

Ich treffe einen Absturz mit KSCrash,
kscrash

wie kann man es lösen?

Wir haben eine PR erstellt, die den oben erwähnten dynamischen Hook von @huakucha implementiert : #375

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

happy201993 picture happy201993  ·  10Kommentare

ferrous777 picture ferrous777  ·  30Kommentare

kstenerud picture kstenerud  ·  4Kommentare

1t2t3t4t picture 1t2t3t4t  ·  3Kommentare

chzhij5 picture chzhij5  ·  17Kommentare