Heim > System-Tutorial > LINUX > Ein tiefer Einblick in fortgeschrittene Linux-Debugger-Themen

Ein tiefer Einblick in fortgeschrittene Linux-Debugger-Themen

WBOY
Freigeben: 2024-01-08 22:42:00
nach vorne
443 Leute haben es durchsucht
Einführung Endlich sind wir beim letzten Artikel dieser Serie angelangt! Dieses Mal gebe ich einen allgemeinen Überblick über einige der fortgeschritteneren Konzepte beim Debuggen: Remote-Debugging, Unterstützung gemeinsam genutzter Bibliotheken, Ausdrucksauswertung und Multithreading-Unterstützung. Die Umsetzung dieser Ideen ist komplexer, daher werde ich nicht näher auf die Umsetzung eingehen, beantworte aber gerne Fragen zu diesen Konzepten, wenn Sie Fragen haben.
Serienindex
  1. Bereiten Sie die Umgebung vor
  2. Haltepunkt
  3. Register und Speicher
  4. Elfen und Zwerge
  5. Quellcode und Signale
  6. Die Quellcodeschicht wird Schritt für Schritt ausgeführt
  7. Haltepunkte der Quellcodeebene
  8. Anrufstapel
  9. Variablen verarbeiten
  10. Premium-Theme
Remote-Debugging

Remote-Debugging ist sehr nützlich für eingebettete Systeme oder das Debuggen verschiedener Umgebungen. Außerdem wird eine schmale Grenze zwischen High-Level-Debugger-Operationen und der Interaktion mit dem Betriebssystem und der Hardware gezogen. Tatsächlich können Debugger wie GDB und LLDB auch beim Debuggen lokaler Programme als Remote-Debugger ausgeführt werden. Die allgemeine Struktur ist wie folgt:
Linux 调试器之高级主题!

debugarch

Der Debugger ist die Komponente, mit der wir über die Befehlszeile interagieren. Wenn Sie eine IDE verwenden, gibt es möglicherweise eine weitere Ebene darüber, die über die Maschinenschnittstelle mit dem Debugger kommuniziert. Auf dem Zielcomputer (wahrscheinlich derselbe wie der native Computer) gibt es einen Debug-Stub, der theoretisch ein Wrapper um eine sehr kleine Debugging-Bibliothek des Betriebssystems ist, die alle Debugging-Aufgaben auf niedriger Ebene ausführt, wie das Setzen von Haltepunkten für Adressen. Ich sage „theoretisch“, weil Debug-Stubs heutzutage immer größer werden. Beispielsweise beträgt die Größe des LLDB-Debug-Stubs auf meinem Computer 7,6 MB. Der Debug-Stub kommuniziert mit dem debuggten Prozess und dem Debugger über das Remote-Protokoll, indem er einige betriebssystemspezifische Funktionen nutzt (in unserem Fall ptrace).
Das gebräuchlichste Remote-Debugging-Protokoll ist das GDB-Remote-Protokoll. Dabei handelt es sich um ein textbasiertes Paketformat, das zur Weitergabe von Befehlen und Informationen zwischen dem Debugger und den Debug-Stubs verwendet wird. Ich werde nicht näher darauf eingehen, aber Sie können hier weiterlesen. Wenn Sie LLDB starten und den Befehl log enable gdb-remote packets ausführen, erhalten Sie einen Trace aller über das Remote-Protokoll gesendeten Pakete. Auf GDB können Sie dasselbe mit set remotelogfile tun.

Als einfaches Beispiel ist dies das Paket, für das ein Haltepunkt festgelegt werden soll:

$Z0,400570,1#43
Nach dem Login kopieren

$ markiert den Anfang des Pakets. Z0 ist der Befehl zum Einfügen eines Speicherhaltepunkts. 400570 und 1 sind Parameter, wobei ersterer die Adresse zum Festlegen des Haltepunkts und letzterer der Haltepunkttypbezeichner für ein bestimmtes Ziel ist. Schließlich ist #43 eine Prüfsumme, um sicherzustellen, dass die Daten nicht beschädigt sind.

Das GDB-Remote-Protokoll lässt sich sehr einfach mit benutzerdefinierten Paketen erweitern, was für die Implementierung plattform- oder sprachspezifischer Funktionen nützlich ist.

Freigegebene Bibliotheken und Unterstützung für dynamisches Laden

Der Debugger muss wissen, welche gemeinsam genutzten Bibliotheken vom zu debuggenden Programm geladen werden, damit er Haltepunkte setzen, Informationen und Symbole auf Quellcodeebene abrufen usw. kann. Neben der Suche nach dynamisch verknüpften Bibliotheken muss der Debugger auch Bibliotheken verfolgen, die zur Laufzeit über dlopen geladen werden. Um diesen Zweck zu erreichen, unterhält der dynamische Linker eine Schnittstruktur. Diese Struktur verwaltet eine verknüpfte Liste gemeinsam genutzter Bibliotheksdeskriptoren sowie einen Zeiger auf eine Funktion, die immer dann aufgerufen wird, wenn die verknüpfte Liste aktualisiert wird. Diese Struktur wird im Abschnitt .dynamic der ELF-Datei gespeichert und vor der Programmausführung initialisiert.

Ein einfacher Tracking-Algorithmus:

  • Der Tracer sucht nach dem Programmeintrag im ELF-Header (oder kann Hilfsvektoren verwenden, die in /proc//aux gespeichert sind).
  • Das Ablaufverfolgungsprogramm setzt einen Haltepunkt am Eingang des Programms und startet die Ausführung.
  • Wenn der Haltepunkt erreicht ist, finden Sie die Adresse der Schnittpunktstruktur, indem Sie in der ELF-Datei nach der Ladeadresse von .dynamic suchen.
  • Überprüfen Sie die Schnittpunktstruktur auf eine Liste der aktuell geladenen Bibliotheken.
  • Setzen Sie einen Haltepunkt für die Linker-Update-Funktion.
  • Die Liste wird jedes Mal aktualisiert, wenn ein Haltepunkt erreicht wird.
  • Das Tracking-Programm führt eine Endlosschleife aus, führt das Programm weiter aus und wartet auf Signale, bis das Signal des Tracking-Programms beendet wird.

Ich habe ein kleines Beispiel dieser Konzepte geschrieben, das Sie hier finden können. Ich kann in Zukunft ausführlicher schreiben, wenn jemand Interesse hat.

Ausdrucksberechnung

Ausdrucksauswertung ist eine Funktion des Programms, die es Benutzern ermöglicht, Ausdrücke in der ursprünglichen Quellsprache auszuwerten, während sie das Programm debuggen. Beispielsweise können Sie in LLDB oder GDB print foo() ausführen, um die foo-Funktion aufzurufen und das Ergebnis zu drucken.

Je nach Komplexität des Ausdrucks gibt es verschiedene Berechnungsmethoden. Wenn es sich bei dem Ausdruck nur um einen einfachen Bezeichner handelt, kann der Debugger die Debug-Informationen einsehen, die Variable finden und den Wert ausdrucken, genau wie wir es im letzten Teil dieser Serie getan haben. Wenn der Ausdruck etwas komplex ist, kann es möglich sein, den Code in einen Zwischenausdruck (IR) zu kompilieren und ihn zu interpretieren, um das Ergebnis zu erhalten. Beispielsweise verwendet LLDB für einige Ausdrücke Clang, um den Ausdruck in eine LLVM-IR zu kompilieren und zu interpretieren. Wenn der Ausdruck komplexer ist oder den Aufruf bestimmter Funktionen erfordert, muss der Code möglicherweise per JIT an das Ziel gesendet und im Adressraum des Debuggers ausgeführt werden. Dazu gehört der Aufruf von mmap, um ausführbaren Speicher zu reservieren, dann das Kopieren des kompilierten Codes in diesen Block und die Ausführung. LLDB wird mithilfe der JIT-Funktionen von LLVM implementiert.

Wenn Sie mehr über die JIT-Kompilierung erfahren möchten, kann ich Eli Benderskys Artikel zu diesem Thema wärmstens empfehlen.

多线程调试支持

本系列展示的调试器仅支持单线程应用程序,但是为了调试大多数真实程序,多线程支持是非常需要的。支持这一点的最简单的方法是跟踪线程的创建,并解析 procfs 以获取所需的信息。

Linux 线程库称为 pthreads。当调用 pthread_create 时,库会使用 clone 系统调用来创建一个新的线程,我们可以用 ptrace 跟踪这个系统调用(假设你的内核早于 2.5.46)。为此,你需要在连接到调试器之后设置一些 ptrace 选项:

ptrace(PTRACE_SETOPTIONS, m_pid, nullptr, PTRACE_O_TRACECLONE);
Nach dem Login kopieren

现在当 clone 被调用时,该进程将收到我们的老朋友 SIGTRAP 信号。对于本系列中的调试器,你可以将一个例子添加到 handle_sigtrap 来处理新线程的创建:

case (SIGTRAP | (PTRACE_EVENT_CLONE << 8)):
//get the new thread ID
unsigned long event_message = 0;
ptrace(PTRACE_GETEVENTMSG, pid, nullptr, message);
//handle creation
//...
Nach dem Login kopieren

一旦收到了,你可以看看 /proc//task/ 并查看内存映射之类来获得所需的所有信息。

GDB 使用 libthread_db,它提供了一堆帮助函数,这样你就不需要自己解析和处理。设置这个库很奇怪,我不会在这展示它如何工作,但如果你想使用它,你可以去阅读这个教程。

多线程支持中最复杂的部分是调试器中线程状态的建模,特别是如果你希望支持不间断模式或当你计算中涉及不止一个 CPU 的某种异构调试。

最后!

呼!这个系列花了很长时间才写完,但是我在这个过程中学到了很多东西,我希望它是有帮助的。如果你有关于调试或本系列中的任何问题,请在 Twitter @TartanLlama或评论区联系我。如果你有想看到的其他任何调试主题,让我知道我或许会再发其他的文章。

Das obige ist der detaillierte Inhalt vonEin tiefer Einblick in fortgeschrittene Linux-Debugger-Themen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:linuxprobe.com
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage