Linux-Debugger-Stack-Erweiterung!
Einführung | Manchmal ist die wichtigste Information, die Sie wissen müssen, wie Ihr aktueller Programmstand dorthin gelangt ist. Es gibt einen Backtrace-Befehl, der Ihnen die aktuelle Funktionsaufrufkette Ihres Programms liefert. In diesem Beitrag erfahren Sie, wie Sie das Stack-Unwinding auf x86_64 implementieren, um einen solchen Traceback zu generieren. |
Diese Links werden online geschaltet, sobald andere Beiträge veröffentlicht werden.
- Bereiten Sie die Umgebung vor
- Haltepunkt
- Register und Speicher
- ELF und ZWERG
- Quellcode und Signale
- Schritt-für-Schritt-Ausführung auf Quellcode-Ebene
- Haltepunkte auf Quellenebene
- Stack-Erweiterung
- Variablen lesen
- Nächste Schritte
Nehmen Sie das folgende Programm als Beispiel:
void a() { //stopped here } void b() { a(); } void c() { a(); } int main() { b(); c(); }
Wenn der Debugger an der Zeile //stopped here' stoppt, gibt es zwei Möglichkeiten, dorthin zu gelangen: main->b->a oder main->c->a`. Wenn wir mit LLDB einen Breakpoint setzen, die Ausführung fortsetzen und einen Traceback anfordern, dann erhalten wir Folgendes:
* frame #0: 0x00000000004004da a.out`a() + 4 at bt.cpp:3 frame #1: 0x00000000004004e6 a.out`b() + 9 at bt.cpp:6 frame #2: 0x00000000004004fe a.out`main + 9 at bt.cpp:14 frame #3: 0x00007ffff7a2e830 libc.so.6`__libc_start_main + 240 at libc-start.c:291 frame #4: 0x0000000000400409 a.out`_start + 41
Das bedeutet, wir befinden uns derzeit in Funktion a, a springt von Funktion b, b springt von der Hauptfunktion usw. In den letzten beiden Frames führt der Compiler das Bootstrapping der Hauptfunktion durch.
Die Frage ist nun, wie wir es auf x86_64 umsetzen. Der robusteste Ansatz wäre, den .eh_frame-Teil der ELF-Datei zu analysieren und herauszufinden, wie der Stapel von dort abgewickelt werden kann, aber das wäre mühsam. Sie könnten es mit libunwind oder ähnlichem machen, aber das ist langweilig. Stattdessen gehen wir davon aus, dass der Compiler den Stapel auf irgendeine Weise eingerichtet hat, und durchlaufen ihn manuell. Dazu müssen wir zunächst das Layout des Stapels verstehen.
High | ... | +---------+ +24| Arg 1 | +---------+ +16| Arg 2 | +---------+ + 8| Return | +---------+ EBP+--> |Saved EBP| +---------+ - 8| Var 1 | +---------+ ESP+--> | Var 2 | +---------+ | ... | Low
Wie Sie sehen können, wird der Frame-Zeiger des letzten Stack-Frames am Anfang des aktuellen Stack-Frames gespeichert, wodurch eine verknüpfte Liste von Zeigern erstellt wird. Der Stapel wird basierend auf dieser verknüpften Liste abgewickelt. Wir können die Funktion für den nächsten Frame in der Liste finden, indem wir in der DWARF-Nachricht nach der Rücksprungadresse suchen. Einige Compiler ignorieren die Verfolgung der Frame-Basisadresse von EBP, da diese als Offset von ESP ausgedrückt werden kann und ein zusätzliches Register freigibt. Selbst wenn Optimierungen aktiviert sind, wird die Übergabe von -fno-omit-frame-pointer an GCC oder Clang dazu führen, dass die Konventionen eingehalten werden, auf die wir angewiesen sind.
Wir erledigen die ganze Arbeit in der Funktion print_backtrace:
void debugger::print_backtrace() {
Als Erstes müssen Sie entscheiden, welches Format zum Ausdrucken der Rahmeninformationen verwendet werden soll. Ich habe ein Lambda verwendet, um diese Methode einzuführen:
auto output_frame = [frame_number = 0] (auto&& func) mutable { std::cout << "frame #" << frame_number++ << ": 0x" << dwarf::at_low_pc(func) << ' ' << dwarf::at_name(func) << std::endl; };
Der erste gedruckte Frame ist der aktuell ausgeführte Frame. Informationen zu diesem Frame erhalten wir, indem wir den aktuellen Programmzähler in DWARF nachschlagen:
auto current_func = get_function_from_pc(get_pc()); output_frame(current_func);
Als nächstes müssen wir den Frame-Zeiger und die Rückgabeadresse der aktuellen Funktion abrufen. Der Frame-Zeiger wird im RBP-Register gespeichert und die Rücksprungadresse beträgt 8 Bytes, gestapelt vom Frame-Zeiger.
auto frame_pointer = get_register_value(m_pid, reg::rbp); auto return_address = read_memory(frame_pointer+8);
Jetzt haben wir alle Informationen, die wir brauchen, um den Stack zu erweitern. Ich spule einfach weiter ab, bis der Debugger die Hauptfunktion erreicht, Sie können aber auch anhalten, wenn der Frame-Zeiger 0x0 ist. Dies sind die Funktionen, die Sie aufrufen, bevor Sie die Hauptfunktion aufrufen. Wir erfassen den Frame-Zeiger und die Rücksprungadresse von jedem Frame und drucken die Informationen aus.
while (dwarf::at_name(current_func) != "main") { current_func = get_function_from_pc(return_address); output_frame(current_func); frame_pointer = read_memory(frame_pointer); return_address = read_memory(frame_pointer+8); } }
Das ist es! Hier ist die gesamte Funktion:
void debugger::print_backtrace() { auto output_frame = [frame_number = 0] (auto&& func) mutable { std::cout << "frame #" << frame_number++ << ": 0x" << dwarf::at_low_pc(func) << ' ' << dwarf::at_name(func) << std::endl; }; auto current_func = get_function_from_pc(get_pc()); output_frame(current_func); auto frame_pointer = get_register_value(m_pid, reg::rbp); auto return_address = read_memory(frame_pointer+8); while (dwarf::at_name(current_func) != "main") { current_func = get_function_from_pc(return_address); output_frame(current_func); frame_pointer = read_memory(frame_pointer); return_address = read_memory(frame_pointer+8); } }
Natürlich müssen wir diesen Befehl dem Benutzer zugänglich machen.
else if(is_prefix(command, "backtrace")) { print_backtrace(); }
Eine Möglichkeit, diese Funktionalität zu testen, besteht darin, ein Testprogramm mit einer Reihe kleiner Funktionen zu schreiben, die sich gegenseitig aufrufen. Legen Sie ein paar Haltepunkte fest, springen Sie durch den Code und stellen Sie sicher, dass Ihr Traceback korrekt ist.
Wir haben einen langen Weg von einem Programm zurückgelegt, das nur spawnen und sich an andere Programme anhängen konnte. Der vorletzte Artikel dieser Reihe vervollständigt die Debugger-Implementierung durch die Unterstützung des Lesens und Schreibens von Variablen. Bis dahin finden Sie den Code für diesen Beitrag hier.
Das obige ist der detaillierte Inhalt vonLinux-Debugger-Stack-Erweiterung!. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Heiße KI -Werkzeuge

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool
Ausziehbilder kostenlos

Clothoff.io
KI-Kleiderentferner

AI Hentai Generator
Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

Heiße Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen



Die Hauptgründe, warum Sie sich bei MySQL nicht als Root anmelden können, sind Berechtigungsprobleme, Konfigurationsdateifehler, Kennwort inkonsistent, Socket -Dateiprobleme oder Firewall -Interception. Die Lösung umfasst: Überprüfen Sie, ob der Parameter Bind-Address in der Konfigurationsdatei korrekt konfiguriert ist. Überprüfen Sie, ob die Root -Benutzerberechtigungen geändert oder gelöscht und zurückgesetzt wurden. Stellen Sie sicher, dass das Passwort korrekt ist, einschließlich Fall- und Sonderzeichen. Überprüfen Sie die Einstellungen und Pfade der Socket -Dateiberechtigte. Überprüfen Sie, ob die Firewall Verbindungen zum MySQL -Server blockiert.

C-Sprachbedingungskompilation ist ein Mechanismus zum selektiven Kompilieren von Codeblöcken, die auf Kompilierungszeitbedingungen basieren. Zu den Einführungsmethoden gehören: Verwenden von #IF- und #else -Direktiven, um Codeblöcke basierend auf den Bedingungen auszuwählen. Zu den häufig verwendeten bedingten Ausdrücken gehören STDC, _win32 und Linux. Praktischer Fall: Drucken Sie verschiedene Nachrichten entsprechend dem Betriebssystem. Verwenden Sie unterschiedliche Datentypen gemäß der Anzahl der Ziffern des Systems. Verschiedene Header -Dateien werden gemäß dem Compiler unterstützt. Die bedingte Kompilierung verbessert die Portabilität und Flexibilität des Codes und macht es an den Compiler-, Betriebssystem- und CPU -Architekturänderungen anpassbar.

1.0.1 Vorwort Dieses Projekt (einschließlich Code und Kommentare) wurde während meines Autodidakt-Rostes aufgezeichnet. Es kann ungenaue oder unklare Aussagen geben. Bitte entschuldigen Sie sich. Wenn Sie davon profitieren, ist es noch besser. 1.0.2 Warum ist Rustrust zuverlässig und effizient? Rost kann C und C mit ähnlicher Leistung, aber höherer Sicherheit ersetzen, und erfordert keine häufige Neukompilation, um auf Fehler wie C und C zu prüfen. Thread-Safe (stellen Sie sicher, dass Multi-Thread-Code vor der Ausführung sicher ist). Vermeiden Sie undefiniertes Verhalten (z. B. Array aus Grenzen, nicht initialisierte Variablen oder Zugriff auf den freien Speicher). Rust bietet moderne Sprachmerkmale wie Generika

Die fünf grundlegenden Komponenten von Linux sind: 1. Der Kernel, Verwaltung von Hardware -Ressourcen; 2. Die Systembibliothek, die Funktionen und Dienste bereitstellt; 3. Shell, die Schnittstelle, in der Benutzer mit dem System interagieren können; 4. Das Dateisystem, das Daten speichert und organisiert; 5. Anwendungen, die Systemressourcen verwenden, um Funktionen zu implementieren.

Es gibt viele Gründe, warum MySQL Startup fehlschlägt und durch Überprüfung des Fehlerprotokolls diagnostiziert werden kann. Zu den allgemeinen Ursachen gehören Portkonflikte (prüfen Portbelegung und Änderung der Konfiguration), Berechtigungsprobleme (Überprüfen Sie den Dienst Ausführen von Benutzerberechtigungen), Konfigurationsdateifehler (Überprüfung der Parametereinstellungen), Datenverzeichniskorruption (Wiederherstellung von Daten oder Wiederaufbautabellenraum), InnoDB-Tabellenraumprobleme (prüfen IBDATA1-Dateien), Plug-in-Ladeversagen (Überprüfen Sie Fehlerprotokolle). Wenn Sie Probleme lösen, sollten Sie sie anhand des Fehlerprotokolls analysieren, die Hauptursache des Problems finden und die Gewohnheit entwickeln, Daten regelmäßig zu unterstützen, um Probleme zu verhindern und zu lösen.

Die C -Sprachfunktionsbibliothek ist eine Toolbox mit verschiedenen Funktionen, die in verschiedenen Bibliotheksdateien organisiert sind. Durch das Hinzufügen einer Bibliothek muss sie über die Befehlszeilenoptionen des Compiler angeben. Der GCC -Compiler verwendet beispielsweise die Option -L -Option, gefolgt von der Abkürzung des Bibliotheksnamens. Wenn sich die Bibliotheksdatei nicht unter dem Standard -Suchpfad befindet, müssen Sie die Option -L verwenden, um den Bibliotheksdateipfad anzugeben. Die Bibliothek kann in statische Bibliotheken und dynamische Bibliotheken unterteilt werden. Statische Bibliotheken sind direkt mit dem Programm zur Kompilierung verbunden, während dynamische Bibliotheken zur Laufzeit geladen werden.

MySQL kann nicht direkt auf Android ausgeführt werden, kann jedoch indirekt mit den folgenden Methoden implementiert werden: Die Verwendung der Leichtgewichtsdatenbank SQLite, die auf dem Android -System basiert, benötigt keinen separaten Server und verfügt über eine kleine Ressourcennutzung, die für Anwendungen für Mobilgeräte sehr geeignet ist. Stellen Sie sich remote eine Verbindung zum MySQL -Server her und stellen Sie über das Netzwerk zum Lesen und Schreiben von Daten über das Netzwerk eine Verbindung zur MySQL -Datenbank auf dem Remote -Server her. Es gibt jedoch Nachteile wie starke Netzwerkabhängigkeiten, Sicherheitsprobleme und Serverkosten.

Die Lösung für den MySQL -Installationsfehler ist: 1. Überprüfen Sie die Systemumgebung sorgfältig, um sicherzustellen, dass die Anforderungen der MySQL -Abhängigkeitsbibliothek erfüllt werden. Unterschiedliche Betriebssysteme und Versionsanforderungen sind unterschiedlich. 2. Lesen Sie die Fehlermeldung sorgfältig durch und ergreifen Sie entsprechende Maßnahmen gemäß den Eingabeaufforderungen (z. B. fehlende Bibliotheksdateien oder unzureichende Berechtigungen), z. B. die Installation von Abhängigkeiten oder die Verwendung von SUDO -Befehlen; 3. Versuchen Sie bei Bedarf, den Quellcode zu installieren und das Kompilierungsprotokoll sorgfältig zu überprüfen. Dies erfordert jedoch eine bestimmte Menge an Linux -Kenntnissen und -erfahrung. Der Schlüssel zur letztendlichen Lösung des Problems besteht darin, die Systemumgebung und Fehlerinformationen sorgfältig zu überprüfen und auf die offiziellen Dokumente zu verweisen.
