Native Abstürze diagnostizieren

Die folgenden Abschnitte enthalten Informationen zu gängigen Arten von nativen Abstürzen, eine Analyse eines Beispiel-Crash-Dumps und eine Erläuterung von Tombstones. Für jeden Absturztyp wird ein Beispiel für die debuggerd-Ausgabe mit hervorgehobenen wichtigen Hinweisen angezeigt, damit Sie die Art des Absturzes unterscheiden können.

Abbrechen

Abbruchstests sind interessant, weil sie absichtlich durchgeführt werden. Es gibt viele verschiedene Möglichkeiten, einen Abbruch auszuführen (z. B. durch Aufrufen von abort(3), Scheitern einer assert(3) oder Verwendung eines der Android-spezifischen fatalen Logging-Typen). Sie alle beinhalten jedoch den Aufruf von abort. Ein Aufruf von abort signalisiert dem aufrufenden Thread SIGABRT. In der debuggerd-Ausgabe sollten Sie also nach einem Frame mit „abort“ in libc.so und SIGABRT suchen, um diesen Fall zu erkennen.

Möglicherweise gibt es eine explizite Zeile „abort message“. Sehen Sie sich auch die logcat-Ausgabe an, um zu sehen, was in diesem Thread protokolliert wurde, bevor er sich selbst beendet hat. Im Gegensatz zu assert(3) oder den Protokollierungsfunktionen für kritische Fehler auf hoher Ebene akzeptiert abort(3) keine Nachricht.

In aktuellen Android-Versionen wird der Systemaufruf tgkill(2) inline eingefügt. Daher sind die Stacks am einfachsten zu lesen, wobei der Aufruf von abort(3) ganz oben steht:

pid: 4637, tid: 4637, name: crasher  >>> crasher <<<
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
Abort message: 'some_file.c:123: some_function: assertion "false" failed'
    r0  00000000  r1  0000121d  r2  00000006  r3  00000008
    r4  0000121d  r5  0000121d  r6  ffb44a1c  r7  0000010c
    r8  00000000  r9  00000000  r10 00000000  r11 00000000
    ip  ffb44c20  sp  ffb44a08  lr  eace2b0b  pc  eace2b16
backtrace:
    #00 pc 0001cb16  /system/lib/libc.so (abort+57)
    #01 pc 0001cd8f  /system/lib/libc.so (__assert2+22)
    #02 pc 00001531  /system/bin/crasher (do_action+764)
    #03 pc 00002301  /system/bin/crasher (main+68)
    #04 pc 0008a809  /system/lib/libc.so (__libc_init+48)
    #05 pc 00001097  /system/bin/crasher (_start_main+38)

Bei älteren Android-Versionen gab es einen verwickelten Pfad zwischen dem ursprünglichen Abbruchaufruf (hier Frame 4) und der tatsächlichen Signalübertragung (hier Frame 0). Das war besonders bei 32-Bit-ARM-Geräten der Fall, bei denen __libc_android_abort (hier Frame 3) zur Sequenz raise/pthread_kill/tgkill der anderen Plattformen hinzugefügt wurde:

pid: 1656, tid: 1656, name: crasher  >>> crasher <<<
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
Abort message: 'some_file.c:123: some_function: assertion "false" failed'
    r0 00000000  r1 00000678  r2 00000006  r3 f70b6dc8
    r4 f70b6dd0  r5 f70b6d80  r6 00000002  r7 0000010c
    r8 ffffffed  r9 00000000  sl 00000000  fp ff96ae1c
    ip 00000006  sp ff96ad18  lr f700ced5  pc f700dc98  cpsr 400b0010
backtrace:
    #00 pc 00042c98  /system/lib/libc.so (tgkill+12)
    #01 pc 00041ed1  /system/lib/libc.so (pthread_kill+32)
    #02 pc 0001bb87  /system/lib/libc.so (raise+10)
    #03 pc 00018cad  /system/lib/libc.so (__libc_android_abort+34)
    #04 pc 000168e8  /system/lib/libc.so (abort+4)
    #05 pc 0001a78f  /system/lib/libc.so (__libc_fatal+16)
    #06 pc 00018d35  /system/lib/libc.so (__assert2+20)
    #07 pc 00000f21  /system/xbin/crasher
    #08 pc 00016795  /system/lib/libc.so (__libc_init+44)
    #09 pc 00000abc  /system/xbin/crasher

Sie können einen solchen Absturz mit crasher abort reproduzieren.

Reine Dereferenzierung des Nullzeigers

Dies ist der klassische native Absturz. Obwohl er nur ein Sonderfall des nächsten Absturztyps ist, sollte er separat erwähnt werden, da er in der Regel am wenigsten Nachdenken erfordert.

Im folgenden Beispiel wird die Funktion, die zum Absturz führt, in libc.so ausgeführt. Da die Stringfunktionen jedoch nur mit den übergebenen Pointern arbeiten, können Sie daraus schließen, dass strlen(3) mit einem Nullzeiger aufgerufen wurde. Dieser Absturz sollte direkt an den Autor des aufrufenden Codes gesendet werden. In diesem Fall ist Frame 01 der fehlerhafte Aufrufer.

pid: 25326, tid: 25326, name: crasher  >>> crasher <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
    r0 00000000  r1 00000000  r2 00004c00  r3 00000000
    r4 ab088071  r5 fff92b34  r6 00000002  r7 fff92b40
    r8 00000000  r9 00000000  sl 00000000  fp fff92b2c
    ip ab08cfc4  sp fff92a08  lr ab087a93  pc efb78988  cpsr 600d0030

backtrace:
    #00 pc 00019988  /system/lib/libc.so (strlen+71)
    #01 pc 00001a8f  /system/xbin/crasher (strlen_null+22)
    #02 pc 000017cd  /system/xbin/crasher (do_action+948)
    #03 pc 000020d5  /system/xbin/crasher (main+100)
    #04 pc 000177a1  /system/lib/libc.so (__libc_init+48)
    #05 pc 000010e4  /system/xbin/crasher (_start+96)

Sie können einen solchen Absturz mit crasher strlen-NULL reproduzieren.

Dereferenzierung eines Nullzeigers mit niedriger Adresse

In vielen Fällen ist die Fehleradresse nicht 0, sondern eine andere niedrige Zahl. Insbesondere zwei- oder dreistellige Adressen sind sehr häufig, während eine sechsstellige Adresse fast sicher keine Nullzeiger-Dereferenzierung ist. Dafür wäre ein Offset von 1 MiB erforderlich. Das tritt in der Regel auf, wenn ein Code einen Nullzeiger so dereferenziert, als wäre er ein gültiger Datentyp. Gängige Funktionen sind fprintf(3) (oder eine andere Funktion, die eine FILE*-Datei annimmt) und readdir(3), da im Code häufig nicht geprüft wird, ob der Aufruf von fopen(3) oder opendir(3) zuerst erfolgreich war.

Hier ein Beispiel für readdir:

pid: 25405, tid: 25405, name: crasher  >>> crasher <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xc
    r0 0000000c  r1 00000000  r2 00000000  r3 3d5f0000
    r4 00000000  r5 0000000c  r6 00000002  r7 ff8618f0
    r8 00000000  r9 00000000  sl 00000000  fp ff8618dc
    ip edaa6834  sp ff8617a8  lr eda34a1f  pc eda618f6  cpsr 600d0030

backtrace:
    #00 pc 000478f6  /system/lib/libc.so (pthread_mutex_lock+1)
    #01 pc 0001aa1b  /system/lib/libc.so (readdir+10)
    #02 pc 00001b35  /system/xbin/crasher (readdir_null+20)
    #03 pc 00001815  /system/xbin/crasher (do_action+976)
    #04 pc 000021e5  /system/xbin/crasher (main+100)
    #05 pc 000177a1  /system/lib/libc.so (__libc_init+48)
    #06 pc 00001110  /system/xbin/crasher (_start+96)

Hier ist die direkte Ursache für den Absturz, dass pthread_mutex_lock(3) versucht hat, auf die Adresse 0xc (Frame 0) zuzugreifen. Aber als Erstes löst pthread_mutex_lock die Referenzierung des state-Elements der übergebenen pthread_mutex_t* auf. In der Quelle sehen Sie, dass sich das Element im Offset 0 der Struktur befindet. Das bedeutet, dass pthread_mutex_lock der ungültige Pointer 0xc zugewiesen wurde. In Frame 1 sehen Sie, dass der Verweis von readdir übergeben wurde, wodurch das Feld mutex_ aus dem übergebenen DIR* extrahiert wird. Sie sehen, dass mutex_ bei einem Offset von sizeof(int) + sizeof(size_t) + sizeof(dirent*) in struct DIR liegt, was auf einem 32‑Bit-Gerät 4 + 4 + 4 = 12 = 0xc entspricht. Sie haben also den Fehler gefunden: readdir wurde vom Aufrufer ein Nullzeiger übergeben. Sie können den Stack jetzt in das Stack-Tool einfügen, um herauszufinden, wo in logcat das Problem aufgetreten ist.

  struct DIR {
    int fd_;
    size_t available_bytes_;
    dirent* next_;
    pthread_mutex_t mutex_;
    dirent buff_[15];
    long current_pos_;
  };

In den meisten Fällen können Sie diese Analyse überspringen. Eine ausreichend niedrige Fehleradresse bedeutet in der Regel, dass Sie alle libc.so-Frames im Stack einfach überspringen und den aufrufenden Code direkt beschuldigen können. Aber nicht immer. So können Sie einen überzeugenden Fall vorlegen.

Sie können diese Art von Absturz mit crasher fprintf-NULL oder crasher readdir-NULL reproduzieren.

FORTIFY-Fehler

Ein FORTIFY-Fehler ist ein Sonderfall eines Abbruchs, der auftritt, wenn die C-Bibliothek ein Problem erkennt, das zu einer Sicherheitslücke führen kann. Viele C-Bibliotheksfunktionen sind fortified. Sie nehmen ein zusätzliches Argument an, das angibt, wie groß ein Puffer tatsächlich ist, und prüfen bei der Laufzeit, ob der durchzuführende Vorgang tatsächlich passt. Hier ein Beispiel, in dem der Code versucht, read(fd, buf, 32) in einen Puffer zu schreiben, der nur 10 Byte lang ist:

pid: 25579, tid: 25579, name: crasher  >>> crasher <<<
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
Abort message: 'FORTIFY: read: prevented 32-byte write into 10-byte buffer'
    r0 00000000  r1 000063eb  r2 00000006  r3 00000008
    r4 ff96f350  r5 000063eb  r6 000063eb  r7 0000010c
    r8 00000000  r9 00000000  sl 00000000  fp ff96f49c
    ip 00000000  sp ff96f340  lr ee83ece3  pc ee86ef0c  cpsr 000d0010

backtrace:
    #00 pc 00049f0c  /system/lib/libc.so (tgkill+12)
    #01 pc 00019cdf  /system/lib/libc.so (abort+50)
    #02 pc 0001e197  /system/lib/libc.so (__fortify_fatal+30)
    #03 pc 0001baf9  /system/lib/libc.so (__read_chk+48)
    #04 pc 0000165b  /system/xbin/crasher (do_action+534)
    #05 pc 000021e5  /system/xbin/crasher (main+100)
    #06 pc 000177a1  /system/lib/libc.so (__libc_init+48)
    #07 pc 00001110  /system/xbin/crasher (_start+96)

Sie können einen solchen Absturz mit crasher fortify reproduzieren.

Von -fstack-protector erkannte Stack-Beschädigung

Die Option -fstack-protector des Compilers fügt Funktionen mit On-Stack-Puffern Prüfungen hinzu, um Pufferüberläufe zu verhindern. Diese Option ist standardmäßig für Plattformcode aktiviert, aber nicht für Apps. Wenn diese Option aktiviert ist, fügt der Compiler dem Prolog der Funktion Anweisungen hinzu, um einen zufälligen Wert direkt nach dem letzten lokalen Wert auf dem Stack zu schreiben, und dem Epilog der Funktion, um ihn wieder zu lesen und zu prüfen, ob er sich nicht geändert hat. Wenn sich dieser Wert geändert hat, wurde er durch einen Pufferüberlauf überschrieben. Daher ruft der Epilog __stack_chk_fail auf, um eine Meldung zu protokollieren und abzubrechen.

pid: 26717, tid: 26717, name: crasher  >>> crasher <<<
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
Abort message: 'stack corruption detected'
    r0 00000000  r1 0000685d  r2 00000006  r3 00000008
    r4 ffd516d8  r5 0000685d  r6 0000685d  r7 0000010c
    r8 00000000  r9 00000000  sl 00000000  fp ffd518bc
    ip 00000000  sp ffd516c8  lr ee63ece3  pc ee66ef0c  cpsr 000e0010

backtrace:
    #00 pc 00049f0c  /system/lib/libc.so (tgkill+12)
    #01 pc 00019cdf  /system/lib/libc.so (abort+50)
    #02 pc 0001e07d  /system/lib/libc.so (__libc_fatal+24)
    #03 pc 0004863f  /system/lib/libc.so (__stack_chk_fail+6)
    #04 pc 000013ed  /system/xbin/crasher (smash_stack+76)
    #05 pc 00001591  /system/xbin/crasher (do_action+280)
    #06 pc 00002219  /system/xbin/crasher (main+100)
    #07 pc 000177a1  /system/lib/libc.so (__libc_init+48)
    #08 pc 00001144  /system/xbin/crasher (_start+96)

Sie können diese Art von Abbruch anhand des Vorhandenseins von __stack_chk_fail im Backtrace und der spezifischen Abbruchsmeldung unterscheiden.

Sie können einen solchen Absturz mit crasher smash-stack reproduzieren.

Seccomp-SIGSYS von einem nicht zulässigen Systemaufruf

Das seccomp-System (insbesondere seccomp-bpf) schränkt den Zugriff auf Systemaufrufe ein. Weitere Informationen zu seccomp für Plattformentwickler finden Sie im Blogpost Seccomp-Filter in Android O. Ein Thread, der einen eingeschränkten Systemaufruf ausführt, erhält ein SIGSYS-Signal mit dem Code SYS_SECCOMP. Die Systemaufrufnummer wird zusammen mit der Architektur in der Zeile „Ursache“ angezeigt. Die Systemaufrufnummern variieren je nach Architektur. Beispielsweise hat der Systemaufruf readlinkat(2) unter x86 die Nummer 305, unter x86-64 aber 267. Die Aufrufnummer ist sowohl bei arm als auch bei arm64 wieder unterschiedlich. Da die Systemaufrufnummern je nach Architektur variieren, ist es in der Regel einfacher, anhand des Stack-Traces herauszufinden, welcher Systemaufruf nicht zulässig war, als in den Headern nach der Systemaufrufnummer zu suchen.

pid: 11046, tid: 11046, name: crasher  >>> crasher <<<
signal 31 (SIGSYS), code 1 (SYS_SECCOMP), fault addr --------
Cause: seccomp prevented call to disallowed arm system call 99999
    r0 cfda0444  r1 00000014  r2 40000000  r3 00000000
    r4 00000000  r5 00000000  r6 00000000  r7 0001869f
    r8 00000000  r9 00000000  sl 00000000  fp fffefa58
    ip fffef898  sp fffef888  lr 00401997  pc f74f3658  cpsr 600f0010

backtrace:
    #00 pc 00019658  /system/lib/libc.so (syscall+32)
    #01 pc 00001993  /system/bin/crasher (do_action+1474)
    #02 pc 00002699  /system/bin/crasher (main+68)
    #03 pc 0007c60d  /system/lib/libc.so (__libc_init+48)
    #04 pc 000011b0  /system/bin/crasher (_start_main+72)

Sie können nicht zulässige Systemaufrufe von anderen Abstürzen anhand des Vorhandenseins von SYS_SECCOMP in der Signalzeile und der Beschreibung in der Zeile mit der Ursache unterscheiden.

Sie können einen solchen Absturz mit crasher seccomp reproduzieren.

Verstoß gegen die XOM-Richtlinie (nur Android 10)

Nur für arm64 in Android 10 wurden ausführbare Segmente von Binärdateien und Bibliotheken als Härtungstechnik gegen Code-Reuse-Angriffe in den nur ausführbaren (nicht lesbaren) Speicher zugeordnet. Diese Abschwächung interagierte schlecht mit anderen Abschwächungen und wurde später entfernt.

Wenn Code unlesbar gemacht wird, führt dies zu vorsätzlichen und unbeabsichtigten Lesezugriffen auf Speichersegmente, die als „nur ausführen“ gekennzeichnet sind, um eine SIGSEGV mit dem Code SEGV_ACCERR zu werfen. Dies kann durch einen Fehler, eine Sicherheitslücke, Daten, die mit Code vermischt sind (z. B. ein Literalpool), oder eine vorsätzliche Speicherprüfung verursacht werden.

Der Compiler geht davon aus, dass Code und Daten nicht vermischt sind, aber Probleme können durch handgeschriebene Assemblercodes auftreten. In vielen Fällen können diese Probleme behoben werden, indem Sie die Konstanten einfach in einen .data-Abschnitt verschieben. Wenn die Code-Selbstprüfung für ausführbare Codeabschnitte unbedingt erforderlich ist, sollte mprotect(2) zuerst aufgerufen werden, um den Code lesbar zu markieren, und dann noch einmal, um ihn nach Abschluss des Vorgangs unlesbar zu markieren.

pid: 2938, tid: 2940, name: crasher64  >>> crasher64 <<<
signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x5f2ced24a8
Cause: execute-only (no-read) memory access error; likely due to data in .text.
    x0  0000000000000000  x1  0000005f2cecf21f  x2  0000000000000078  x3  0000000000000053
    x4  0000000000000074  x5  8000000000000000  x6  ff71646772607162  x7  00000020dcf0d16c
    x8  0000005f2ced24a8  x9  000000781251c55e  x10 0000000000000000  x11 0000000000000000
    x12 0000000000000014  x13 ffffffffffffffff  x14 0000000000000002  x15 ffffffffffffffff
    x16 0000005f2ced52f0  x17 00000078125c0ed8  x18 0000007810e8e000  x19 00000078119fbd50
    x20 00000078125d6020  x21 00000078119fbd50  x22 00000b7a00000b7a  x23 00000078119fbdd8
    x24 00000078119fbd50  x25 00000078119fbd50  x26 00000078119fc018  x27 00000078128ea020
    x28 00000078119fc020  x29 00000078119fbcb0
    sp  00000078119fba40  lr  0000005f2ced1b94  pc  0000005f2ced1ba4

backtrace:
      #00 pc 0000000000003ba4  /system/bin/crasher64 (do_action+2348)
      #01 pc 0000000000003234  /system/bin/crasher64 (thread_callback+44)
      #02 pc 00000000000e2044  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+36)
      #03 pc 0000000000083de0  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)

Sie können nur-Ausführungsspeicherverstöße anhand der Zeile „Ursache“ von anderen Abstürzen unterscheiden.

Sie können einen solchen Absturz mit crasher xom reproduzieren.

Von fdsan erkannter Fehler

Der fdsan-Dateideskriptor-Sanitizer von Android hilft, häufige Fehler mit Dateideskriptoren zu erkennen, z. B. die Verwendung nach dem Schließen und das doppelte Schließen. Weitere Informationen zum Beheben und Vermeiden dieser Art von Fehlern finden Sie in der Dokumentation zu fdsan.

pid: 32315, tid: 32315, name: crasher64  >>> crasher64 <<<
signal 35 (), code -1 (SI_QUEUE), fault addr --------
Abort message: 'attempted to close file descriptor 3, expected to be unowned, actually owned by FILE* 0x7d8e413018'
    x0  0000000000000000  x1  0000000000007e3b  x2  0000000000000023  x3  0000007fe7300bb0
    x4  3033313465386437  x5  3033313465386437  x6  3033313465386437  x7  3831303331346538
    x8  00000000000000f0  x9  0000000000000000  x10 0000000000000059  x11 0000000000000034
    x12 0000007d8ebc3a49  x13 0000007fe730077a  x14 0000007fe730077a  x15 0000000000000000
    x16 0000007d8ec9a7b8  x17 0000007d8ec779f0  x18 0000007d8f29c000  x19 0000000000007e3b
    x20 0000000000007e3b  x21 0000007d8f023020  x22 0000007d8f3b58dc  x23 0000000000000001
    x24 0000007fe73009a0  x25 0000007fe73008e0  x26 0000007fe7300ca0  x27 0000000000000000
    x28 0000000000000000  x29 0000007fe7300c90
    sp  0000007fe7300860  lr  0000007d8ec2f22c  pc  0000007d8ec2f250

backtrace:
      #00 pc 0000000000088250  /bionic/lib64/libc.so (fdsan_error(char const*, ...)+384)
      #01 pc 0000000000088060  /bionic/lib64/libc.so (android_fdsan_close_with_tag+632)
      #02 pc 00000000000887e8  /bionic/lib64/libc.so (close+16)
      #03 pc 000000000000379c  /system/bin/crasher64 (do_action+1316)
      #04 pc 00000000000049c8  /system/bin/crasher64 (main+96)
      #05 pc 000000000008021c  /bionic/lib64/libc.so (_start_main)

Sie können diese Art von Abbruch anhand des Vorhandenseins von fdsan_error im Backtrace und der spezifischen Abbruchsmeldung unterscheiden.

Sie können einen solchen Absturz mit crasher fdsan_file oder crasher fdsan_dir reproduzieren.

Crash-Dumps untersuchen

Wenn Sie gerade keinen bestimmten Absturz untersuchen, enthält die Plattformquelle ein Tool zum Testen von debuggerd namens „Crasher“. Wenn Sie mm in system/core/debuggerd/ ausführen, wird sowohl crasher als auch crasher64 in Ihrem Pfad angezeigt. Mit letzterem können Sie 64-Bit-Abstürze testen. Crasher kann auf viele interessante Arten abstürzen, je nachdem, welche Befehlszeilenargumente Sie angeben. Mit crasher --help kannst du die aktuell unterstützte Auswahl aufrufen.

Um die verschiedenen Teile eines Crash-Dumps zu veranschaulichen, sehen wir uns diesen Beispiel-Crash-Dump an:

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'Android/aosp_flounder/flounder:5.1.51/AOSP/enh08201009:eng/test-keys'
Revision: '0'
ABI: 'arm'
pid: 1656, tid: 1656, name: crasher  >>> crasher <<<
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
Abort message: 'some_file.c:123: some_function: assertion "false" failed'
    r0 00000000  r1 00000678  r2 00000006  r3 f70b6dc8
    r4 f70b6dd0  r5 f70b6d80  r6 00000002  r7 0000010c
    r8 ffffffed  r9 00000000  sl 00000000  fp ff96ae1c
    ip 00000006  sp ff96ad18  lr f700ced5  pc f700dc98  cpsr 400b0010
backtrace:
    #00 pc 00042c98  /system/lib/libc.so (tgkill+12)
    #01 pc 00041ed1  /system/lib/libc.so (pthread_kill+32)
    #02 pc 0001bb87  /system/lib/libc.so (raise+10)
    #03 pc 00018cad  /system/lib/libc.so (__libc_android_abort+34)
    #04 pc 000168e8  /system/lib/libc.so (abort+4)
    #05 pc 0001a78f  /system/lib/libc.so (__libc_fatal+16)
    #06 pc 00018d35  /system/lib/libc.so (__assert2+20)
    #07 pc 00000f21  /system/xbin/crasher
    #08 pc 00016795  /system/lib/libc.so (__libc_init+44)
    #09 pc 00000abc  /system/xbin/crasher
Tombstone written to: /data/tombstones/tombstone_06
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

Die Zeile mit Sternchen und Leerzeichen ist hilfreich, wenn Sie in einem Protokoll nach nativen Abstürzen suchen. Der String „*** ***“ wird nur selten in Protokollen angezeigt, außer zu Beginn eines nativen Absturzes.

Build fingerprint:
'Android/aosp_flounder/flounder:5.1.51/AOSP/enh08201009:eng/test-keys'

Anhand des Fingerabdrucks können Sie genau ermitteln, bei welchem Build der Absturz aufgetreten ist. Dies entspricht genau der Systemeigenschaft ro.build.fingerprint.

Revision: '0'

Die Revision bezieht sich auf die Hardware und nicht auf die Software. Diese Option wird in der Regel nicht verwendet, kann aber nützlich sein, um Fehler automatisch zu ignorieren, die bekanntermaßen durch fehlerhafte Hardware verursacht werden. Dies entspricht genau der Systemeigenschaft ro.revision.

ABI: 'arm'

Das ABI ist eine der folgenden Optionen: arm, arm64, x86 oder x86-64. Das ist vor allem für das oben erwähnte stack-Script nützlich, damit es weiß, welche Toolchain verwendet werden soll.

pid: 1656, tid: 1656, name: crasher >>> crasher <<<

Diese Zeile gibt den spezifischen Thread im Prozess an, der abgestürzt ist. In diesem Fall war es der Hauptthread des Prozesses, sodass die Prozess- und Thread-ID übereinstimmen. Der erste Name ist der Threadname und der Name, der von >>> und <<< umgeben ist, ist der Prozessname. Bei einer App ist der Prozessname in der Regel der vollständige Paketname (z. B. com.facebook.katana). Das ist hilfreich, wenn Sie Fehler melden oder die App in Google Play suchen möchten. Die PID und die TID können auch hilfreich sein, um die relevanten Logzeilen vor dem Absturz zu finden.

signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------

In dieser Zeile sehen Sie, welches Signal (SIGABRT) empfangen wurde und wie es empfangen wurde (SI_TKILL). Die von debuggerd gemeldeten Signale sind SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV und SIGTRAP. Die signalspezifischen Codes variieren je nach Signal.

Abort message: 'some_file.c:123: some_function: assertion "false" failed'

Nicht alle Abstürze enthalten eine Abbruchsnachricht, aber Abbruchsmeldungen schon. Dieser Wert wird automatisch aus der letzten Zeile der fatalen Logcat-Ausgabe für diese PID/TID erfasst. Im Falle einer absichtlichen Abbruchs wird hier wahrscheinlich eine Erklärung dafür angezeigt, warum das Programm beendet wurde.

r0 00000000 r1 00000678 r2 00000006 r3 f70b6dc8
r4 f70b6dd0 r5 f70b6d80 r6 00000002 r7 0000010c
r8 ffffffed r9 00000000 sl 00000000 fp ff96ae1c
ip 00000006 sp ff96ad18 lr f700ced5 pc f700dc98 cpsr 400b0010

Der Registerdump zeigt den Inhalt der CPU-Register zum Zeitpunkt des Empfangs des Signals. (Dieser Abschnitt variiert stark zwischen ABIs.) Wie nützlich diese sind, hängt vom genauen Absturz ab.

backtrace:
    #00 pc 00042c98 /system/lib/libc.so (tgkill+12)
    #01 pc 00041ed1 /system/lib/libc.so (pthread_kill+32)
    #02 pc 0001bb87 /system/lib/libc.so (raise+10)
    #03 pc 00018cad /system/lib/libc.so (__libc_android_abort+34)
    #04 pc 000168e8 /system/lib/libc.so (abort+4)
    #05 pc 0001a78f /system/lib/libc.so (__libc_fatal+16)
    #06 pc 00018d35 /system/lib/libc.so (__assert2+20)
    #07 pc 00000f21 /system/xbin/crasher
    #08 pc 00016795 /system/lib/libc.so (__libc_init+44)
    #09 pc 00000abc /system/xbin/crasher

Der Backtrace zeigt an, wo im Code wir uns zum Zeitpunkt des Absturzes befanden. Die erste Spalte ist die Frame-Nummer (entspricht dem Stil von gdb, bei dem der tiefste Frame 0 ist). Die PC-Werte sind nicht absolut, sondern relativ zum Speicherort der freigegebenen Bibliothek. Die nächste Spalte ist der Name der zugeordneten Region. In der Regel ist dies eine freigegebene Bibliothek oder eine ausführbare Datei, aber möglicherweise nicht für JIT-kompilierten Code. Wenn Symbole verfügbar sind, wird das Symbol angezeigt, dem der PC-Wert entspricht, zusammen mit dem Offset in diesem Symbol in Byte. Sie können dies in Verbindung mit objdump(1) verwenden, um die entsprechende Assembleranweisung zu finden.

Tombstones lesen

Tombstone written to: /data/tombstones/tombstone_06

Hier sehen Sie, wo debuggerd zusätzliche Informationen hinzugefügt hat. debuggerd speichert bis zu zehn Tombstones, wobei die Zahlen 00 bis 09 durchlaufen und vorhandene Tombstones bei Bedarf überschrieben werden.

Der Tombstone enthält dieselben Informationen wie der Crash-Dump, zusätzlich aber noch einige weitere. Dazu gehören beispielsweise Backtraces für alle Threads (nicht nur für den Thread, der abgestürzt ist), die Gleitkommaregister, Roh-Stack-Dumps und Speicher-Dumps um die Adressen in den Registern. Am nützlichsten ist es, wenn auch eine vollständige Speicherzuordnung enthalten ist (ähnlich wie bei /proc/pid/maps). Hier ein kommentiertes Beispiel für einen Absturz eines 32-Bit-ARM-Prozesses:

memory map: (fault address prefixed with --->)
--->ab15f000-ab162fff r-x 0 4000 /system/xbin/crasher (BuildId:
b9527db01b5cf8f5402f899f64b9b121)

Hier sind zwei Dinge zu beachten. Erstens: Diese Zeile beginnt mit „--->“. Die Maps sind am nützlichsten, wenn der Absturz nicht nur auf eine Nullzeiger-Dereferenzierung zurückzuführen ist. Wenn die Fehleradresse klein ist, handelt es sich wahrscheinlich um eine Variante der Dereferenzierung eines Nullzeigers. Andernfalls können Sie sich die Karten in der Nähe der Fehleradresse ansehen, um herauszufinden, was passiert ist. Einige mögliche Probleme, die anhand der Karten erkannt werden können:

  • Liest/schreibt über das Ende eines Speicherblocks hinaus.
  • Lesen/Schreiben vor dem Beginn eines Speicherblocks.
  • Versuche, nicht-codierte Inhalte auszuführen.
  • Sie stoßen am Ende eines Stapels an.
  • Es wird versucht, in den Code zu schreiben (wie im Beispiel oben).

Zweitens: In Android 6.0 und höher wird in ausführbaren Dateien und freigegebenen Bibliotheksdateien die Build-ID (falls vorhanden) angezeigt. So können Sie genau sehen, welche Version Ihres Codes abgestürzt ist. Plattformbinärdateien enthalten seit Android 6.0 standardmäßig eine Build-ID. NDK r12 und höher übergeben -Wl,--build-id auch automatisch an den Linker.

ab163000-ab163fff r--      3000      1000  /system/xbin/crasher
ab164000-ab164fff rw-         0      1000
f6c80000-f6d7ffff rw-         0    100000  [anon:libc_malloc]

Unter Android ist der Heap nicht unbedingt eine einzelne Region. Heap-Regionen werden mit [anon:libc_malloc] gekennzeichnet.

f6d82000-f6da1fff r--         0     20000  /dev/__properties__/u:object_r:logd_prop:s0
f6da2000-f6dc1fff r--         0     20000  /dev/__properties__/u:object_r:default_prop:s0
f6dc2000-f6de1fff r--         0     20000  /dev/__properties__/u:object_r:logd_prop:s0
f6de2000-f6de5fff r-x         0      4000  /system/lib/libnetd_client.so (BuildId: 08020aa06ed48cf9f6971861abf06c9d)
f6de6000-f6de6fff r--      3000      1000  /system/lib/libnetd_client.so
f6de7000-f6de7fff rw-      4000      1000  /system/lib/libnetd_client.so
f6dec000-f6e74fff r-x         0     89000  /system/lib/libc++.so (BuildId: 8f1f2be4b37d7067d366543fafececa2) (load base 0x2000)
f6e75000-f6e75fff ---         0      1000
f6e76000-f6e79fff r--     89000      4000  /system/lib/libc++.so
f6e7a000-f6e7afff rw-     8d000      1000  /system/lib/libc++.so
f6e7b000-f6e7bfff rw-         0      1000  [anon:.bss]
f6e7c000-f6efdfff r-x         0     82000  /system/lib/libc.so (BuildId: d189b369d1aafe11feb7014d411bb9c3)
f6efe000-f6f01fff r--     81000      4000  /system/lib/libc.so
f6f02000-f6f03fff rw-     85000      2000  /system/lib/libc.so
f6f04000-f6f04fff rw-         0      1000  [anon:.bss]
f6f05000-f6f05fff r--         0      1000  [anon:.bss]
f6f06000-f6f0bfff rw-         0      6000  [anon:.bss]
f6f0c000-f6f21fff r-x         0     16000  /system/lib/libcutils.so (BuildId: d6d68a419dadd645ca852cd339f89741)
f6f22000-f6f22fff r--     15000      1000  /system/lib/libcutils.so
f6f23000-f6f23fff rw-     16000      1000  /system/lib/libcutils.so
f6f24000-f6f31fff r-x         0      e000  /system/lib/liblog.so (BuildId: e4d30918d1b1028a1ba23d2ab72536fc)
f6f32000-f6f32fff r--      d000      1000  /system/lib/liblog.so
f6f33000-f6f33fff rw-      e000      1000  /system/lib/liblog.so

Eine freigegebene Bibliothek hat in der Regel drei benachbarte Einträge. Eine ist lesbar und ausführbar (Code), eine ist schreibgeschützt (schreibgeschützte Daten) und eine ist les- und schreibbar (änderbare Daten). In der ersten Spalte sind die Adressbereiche für die Zuordnung, in der zweiten Spalte die Berechtigungen (im üblichen Unix-ls(1)-Format), in der dritten Spalte der Offset in der Datei (hexadezimal), in der vierten Spalte die Größe der Region (hexadezimal) und in der fünften Spalte die Datei (oder ein anderer Regionsname) aufgeführt.

f6f34000-f6f53fff r-x         0     20000  /system/lib/libm.so (BuildId: 76ba45dcd9247e60227200976a02c69b)
f6f54000-f6f54fff ---         0      1000
f6f55000-f6f55fff r--     20000      1000  /system/lib/libm.so
f6f56000-f6f56fff rw-     21000      1000  /system/lib/libm.so
f6f58000-f6f58fff rw-         0      1000
f6f59000-f6f78fff r--         0     20000  /dev/__properties__/u:object_r:default_prop:s0
f6f79000-f6f98fff r--         0     20000  /dev/__properties__/properties_serial
f6f99000-f6f99fff rw-         0      1000  [anon:linker_alloc_vector]
f6f9a000-f6f9afff r--         0      1000  [anon:atexit handlers]
f6f9b000-f6fbafff r--         0     20000  /dev/__properties__/properties_serial
f6fbb000-f6fbbfff rw-         0      1000  [anon:linker_alloc_vector]
f6fbc000-f6fbcfff rw-         0      1000  [anon:linker_alloc_small_objects]
f6fbd000-f6fbdfff rw-         0      1000  [anon:linker_alloc_vector]
f6fbe000-f6fbffff rw-         0      2000  [anon:linker_alloc]
f6fc0000-f6fc0fff r--         0      1000  [anon:linker_alloc]
f6fc1000-f6fc1fff rw-         0      1000  [anon:linker_alloc_lob]
f6fc2000-f6fc2fff r--         0      1000  [anon:linker_alloc]
f6fc3000-f6fc3fff rw-         0      1000  [anon:linker_alloc_vector]
f6fc4000-f6fc4fff rw-         0      1000  [anon:linker_alloc_small_objects]
f6fc5000-f6fc5fff rw-         0      1000  [anon:linker_alloc_vector]
f6fc6000-f6fc6fff rw-         0      1000  [anon:linker_alloc_small_objects]
f6fc7000-f6fc7fff rw-         0      1000  [anon:arc4random _rsx structure]
f6fc8000-f6fc8fff rw-         0      1000  [anon:arc4random _rs structure]
f6fc9000-f6fc9fff r--         0      1000  [anon:atexit handlers]
f6fca000-f6fcafff ---         0      1000  [anon:thread signal stack guard page]

Seit Android 5.0 benennt die C-Bibliothek die meisten ihrer anonym zugeordneten Regionen, sodass es weniger unbekannte Regionen gibt.

f6fcb000-f6fccfff rw- 0 2000 [stack:5081]

Regionen mit dem Namen [stack:tid] sind die Stacks für die angegebenen Threads.

f6fcd000-f702afff r-x         0     5e000  /system/bin/linker (BuildId: 84f1316198deee0591c8ac7f158f28b7)
f702b000-f702cfff r--     5d000      2000  /system/bin/linker
f702d000-f702dfff rw-     5f000      1000  /system/bin/linker
f702e000-f702ffff rw-         0      2000
f7030000-f7030fff r--         0      1000
f7031000-f7032fff rw-         0      2000
ffcd7000-ffcf7fff rw-         0     21000
ffff0000-ffff0fff r-x         0      1000  [vectors]

Ob [vector] oder [vdso] angezeigt wird, hängt von der Architektur ab. ARM verwendet [vector], während alle anderen Architekturen [vdso] verwenden.