Heim System-Tutorial LINUX Entdecken Sie Techniken zur Variablenverarbeitung in Linux-Debuggern!

Entdecken Sie Techniken zur Variablenverarbeitung in Linux-Debuggern!

Jan 15, 2024 pm 11:09 PM
linux linux教程 红帽 linux系统 linux命令 Linux-Zertifizierung Red Hat Linux Linux-Video

Einführung Variablen sind hinterlistig. Manchmal sitzen sie gerne in der Kasse und landen dann auf dem Stapel, sobald sie sich umdrehen. Zu Optimierungszwecken kann der Compiler sie vollständig aus dem Fenster werfen. Ganz gleich, wie sich Variablen durch den Speicher bewegen, wir brauchen eine Möglichkeit, sie im Debugger zu verfolgen und zu manipulieren. In diesem Artikel lernen Sie den Umgang mit Variablen im Debugger und demonstrieren eine einfache Implementierung mit libelfin.
Serienartikelindex
  1. Bereiten Sie die Umgebung vor
  2. Haltepunkt
  3. Register und Speicher
  4. ELF und ZWERG
  5. Quellcode und Signale
  6. Schritt-für-Schritt-Ausführung auf Quellcode-Ebene
  7. Haltepunkte auf Quellenebene
  8. Stack-Erweiterung
  9. Variablen verarbeiten
  10. Fortgeschrittene Themen

Bevor Sie beginnen, stellen Sie bitte sicher, dass Sie die Version von libelfin fbreg in meinem Zweig verwenden. Dies enthält einige Hacks, um das Abrufen der Basisadresse des aktuellen Stack-Frames und das Auswerten einer Liste von Positionen zu unterstützen, die von nativem libelfin nicht bereitgestellt werden. Möglicherweise müssen Sie den Parameter -gdwarf-2 an GCC übergeben, um kompatible DWARF-Nachrichten zu generieren. Aber bevor ich das umsetze, werde ich detailliert beschreiben, wie die Positionskodierung in der neuesten DWARF 5-Spezifikation funktioniert. Wenn Sie mehr wissen möchten, können Sie den Standard hier abrufen.

ZWERGEN-Standort

Der Speicherort einer Variablen im Speicher zu einem bestimmten Zeitpunkt wird in der DWARF-Nachricht mithilfe des Attributs DW_AT_location codiert. Eine Standortbeschreibung kann eine einzelne Standortbeschreibung, eine zusammengesetzte Standortbeschreibung oder eine Liste von Standorten sein.

  • Einfache Positionsbeschreibung: Beschreibt die Position eines zusammenhängenden Teils (normalerweise aller Teile) eines Objekts. Eine einfache Ortsbeschreibung kann einen Ort im adressierbaren Speicher oder einem Register oder dessen Fehlen (mit oder ohne bekannten Wert) beschreiben. Zum Beispiel DW_OP_fbreg -32: Eine gesamte gespeicherte Variable – 32 Bytes beginnend mit der Stapelrahmenbasis.
  • Zusammengesetzte Ortsbeschreibung: Bei der Beschreibung von Objekten in Form von Fragmenten kann jedes Objekt in einem Teil eines Registers enthalten sein oder unabhängig von anderen Fragmenten an einem Speicherort gespeichert werden. Beispiel: DW_OP_reg3 DW_OP_piece 4 DW_OP_reg10 DW_OP_piece 2: Die ersten vier Bytes befinden sich in Register 3 und die letzten beiden Bytes befinden sich in einer Variablen in Register 10.
  • Positionsliste: Beschreibt Objekte, die eine begrenzte Lebensdauer haben oder während ihrer Lebensdauer ihren Standort ändern. Zum Beispiel:
      • [ 0]DW_OP_reg0
      • [ 1]DW_OP_reg3
      • [ 2]DW_OP_reg2
  • Eine Variable, deren Speicherort basierend auf dem aktuellen Wert des Programmzählers zwischen den Registern verschoben wird.

DW_AT_location wird abhängig von der Art der Standortbeschreibung auf drei verschiedene Arten kodiert. exprloc kodiert einfache und zusammengesetzte Positionsbeschreibungen. Sie bestehen aus einer Bytelänge, gefolgt von einem DWARF-Ausdruck oder einer Ortsbeschreibung. Kodierte Standortlisten für loclist und loclistptr, die den Index oder Offset im Abschnitt .debug_loclists bereitstellen, der die tatsächliche Standortliste beschreibt.

Zwerg-Ausdruck

Verwenden Sie DWARF-Ausdrücke, um die tatsächliche Position einer Variablen zu berechnen. Dazu gehört eine Reihe von Operationen, die Stapelwerte manipulieren. Da viele DWARF-Operationen verfügbar sind, werde ich sie nicht im Detail erklären. Stattdessen gebe ich einige Beispiele für jeden Ausdruck, um Ihnen etwas zu geben, mit dem Sie arbeiten können. Haben Sie auch keine Angst davor; libelfin übernimmt die ganze Komplexität für uns.

  • Literale Kodierung
    • DW_OP_lit0, DW_OP_lit1...DW_OP_lit31
      • Literale auf den Stapel schieben
    • DW_OP_addr
      • Schieben Sie den Adressoperanden auf den Stapel
    • DW_OP_constu
      • Vorzeichenlosen Wert auf den Stapel verschieben
  • Wert registrieren
    • DW_OP_fbreg
      • Übertragen Sie den an der Basis des Stapelrahmens gefundenen Wert, versetzt um den angegebenen Wert
    • DW_OP_breg0, DW_OP_breg1... DW_OP_breg31
      • Schiebe den Inhalt des angegebenen Registers plus den angegebenen Offset auf den Stapel
  • Stack-Operationen
    • DW_OP_dup
      • Kopieren Sie den Wert oben im Stapel
    • DW_OP_deref
      • Behandeln Sie den oberen Teil des Stapels als Speicheradresse und ersetzen Sie ihn durch den Inhalt dieser Adresse
  • Arithmetische und logische Operationen
    • DW_OP_and
      • Legen Sie die beiden Werte oben auf den Stapel und schieben Sie ihr logisches UND zurück
    • DW_OP_plus
      • Wie DW_OP_and, aber mit Mehrwert
  • Kontrollflussvorgänge
    • DW_OP_le, DW_OP_eq, DW_OP_gt usw.
      • Erfassen Sie die ersten beiden Werte, vergleichen Sie sie und drücken Sie 1, wenn die Bedingung wahr ist, andernfalls 0
    • DW_OP_bra
      • Bedingter Zweig: Wenn die Oberseite des Stapels nicht 0 ist, springen Sie im Ausdruck über den Offset
      • vorwärts oder rückwärts
  • Konvertierung eingeben
    • DW_OP_convert
      • Konvertieren Sie den Wert oben im Stapel in einen anderen Typ, der durch einen DWARF-Infoeintrag am angegebenen Offset beschrieben wird
  • Sondereinsätze
    • DW_OP_nop
      • Nichts tun!
ZWERG-Typ

DWARF-Typdarstellungen müssen leistungsstark genug sein, um Debugger-Benutzern nützliche Variablendarstellungen bereitzustellen. Benutzer möchten oft in der Lage sein, auf Anwendungsebene und nicht auf Maschinenebene zu debuggen, und sie müssen verstehen, was ihre Variablen tun.

Der DWARF-Typ wird zusammen mit den meisten anderen Debugging-Informationen in DIE codiert. Sie können Eigenschaften haben, die ihren Namen, ihre Kodierung, Größe, Bytes usw. angeben. Es stehen unzählige Typ-Tags zur Darstellung von Zeigern, Arrays, Strukturen, Typdefinitionen und allem anderen zur Verfügung, das Sie in einem C- oder C++-Programm sehen könnten.

Nehmen Sie diese einfache Struktur als Beispiel:

struct test{
int i;
float j;
int k[42];
test* next;
};
Nach dem Login kopieren

Der übergeordnete DIE dieser Struktur sieht folgendermaßen aus:

< 1><0x0000002a> DW_TAG_structure_type
DW_AT_name "test"
DW_AT_byte_size 0x000000b8
DW_AT_decl_file 0x00000001 test.cpp
DW_AT_decl_line 0x00000001
Nach dem Login kopieren

Was oben steht, ist, dass wir eine Struktur namens test mit der Größe 0xb8 haben, die in Zeile 1 von test.cpp deklariert ist. Als nächstes gibt es eine Reihe von Sub-DIEs, die die Mitglieder beschreiben.

< 2><0x00000032> DW_TAG_member
DW_AT_name "i"
DW_AT_type <0x00000063>
DW_AT_decl_file 0x00000001 test.cpp
DW_AT_decl_line 0x00000002
DW_AT_data_member_location 0
< 2><0x0000003e> DW_TAG_member
DW_AT_name "j"
DW_AT_type <0x0000006a>
DW_AT_decl_file 0x00000001 test.cpp
DW_AT_decl_line 0x00000003
DW_AT_data_member_location 4
< 2><0x0000004a> DW_TAG_member
DW_AT_name "k"
DW_AT_type <0x00000071>
DW_AT_decl_file 0x00000001 test.cpp
DW_AT_decl_line 0x00000004
DW_AT_data_member_location 8
< 2><0x00000056> DW_TAG_member
DW_AT_name "next"
DW_AT_type <0x00000084>
DW_AT_decl_file 0x00000001 test.cpp
DW_AT_decl_line 0x00000005
DW_AT_data_member_location 176(as signed = -80)
Nach dem Login kopieren

Jedes Mitglied hat einen Namen, einen Typ (der ein DIE-Offset ist), eine Deklarationsdatei und -zeile sowie einen Byte-Offset zur Struktur, in der sich sein Mitglied befindet. Seine Typpunkte sind wie folgt.

< 1><0x00000063> DW_TAG_base_type
DW_AT_name "int"
DW_AT_encoding DW_ATE_signed
DW_AT_byte_size 0x00000004
< 1><0x0000006a> DW_TAG_base_type
DW_AT_name "float"
DW_AT_encoding DW_ATE_float
DW_AT_byte_size 0x00000004
< 1><0x00000071> DW_TAG_array_type
DW_AT_type <0x00000063>
< 2><0x00000076> DW_TAG_subrange_type
DW_AT_type <0x0000007d>
DW_AT_count 0x0000002a
< 1><0x0000007d> DW_TAG_base_type
DW_AT_name "sizetype"
DW_AT_byte_size 0x00000008
DW_AT_encoding DW_ATE_unsigned
< 1><0x00000084> DW_TAG_pointer_type
DW_AT_type <0x0000002a>
Nach dem Login kopieren

Wie Sie sehen können, ist int auf meinem Laptop ein 4-Byte-Ganzzahltyp mit Vorzeichen und float eine 4-Byte-Gleitkommazahl. Der Integer-Array-Typ hat 2a Elemente, indem er auf einen int-Typ als Elementtyp und auf sizetype (stellen Sie sich das als size_t vor) als Indextyp zeigt. Der Test *-Typ ist DW_TAG_pointer_type, der sich auf den Test DIE bezieht.

Implementierung eines einfachen Variablenlesers

Wie oben erwähnt, übernimmt Libelfin den Großteil der Komplexität für uns. Es implementiert jedoch nicht alle Methoden zur Darstellung variabler Positionen und die Handhabung dieser in unserem Code wird sehr komplex. Daher entscheide ich mich jetzt dafür, nur exprloc zu unterstützen. Bitte fügen Sie bei Bedarf Unterstützung für weitere Ausdruckstypen hinzu. Wenn Sie wirklich mutig sind, senden Sie bitte einen Patch an libelfin, um den erforderlichen Support zu vervollständigen!

Der Umgang mit Variablen umfasst hauptsächlich das Auffinden verschiedener Teile im Speicher oder in Registern, und das Lesen oder Schreiben ist dasselbe wie zuvor. Der Einfachheit halber erkläre ich Ihnen nur, wie Sie das Lesen umsetzen.

Zuerst müssen wir libelfin mitteilen, wie es Register aus unserem Prozess lesen soll. Wir erstellen eine Klasse, die von expr_context erbt, und verwenden ptrace, um alles zu verarbeiten:

class ptrace_expr_context : public dwarf::expr_context {
public:
ptrace_expr_context (pid_t pid) : m_pid{pid} {}
dwarf::taddr reg (unsigned regnum) override {
return get_register_value_from_dwarf_register(m_pid, regnum);
}
dwarf::taddr pc() override {
struct user_regs_struct regs;
ptrace(PTRACE_GETREGS, m_pid, nullptr, &regs);
return regs.rip;
}
dwarf::taddr deref_size (dwarf::taddr address, unsigned size) override {
//TODO take into account size
return ptrace(PTRACE_PEEKDATA, m_pid, address, nullptr);
}
private:
pid_t m_pid;
};
Nach dem Login kopieren

Das Lesen wird von der Funktion read_variables in unserer Debugger-Klasse übernommen:

void debugger::read_variables() {
using namespace dwarf;
auto func = get_function_from_pc(get_pc());
//...
}
Nach dem Login kopieren

Als erstes haben wir oben die Funktion gefunden, in der wir uns gerade befinden. Anschließend müssen wir die Einträge in dieser Funktion durchlaufen, um die Variablen zu finden:

for (const auto& die : func) {
if (die.tag == DW_TAG::variable) {
//...
}
}
Nach dem Login kopieren

Wir erhalten die Standortinformationen, indem wir nach dem DW_AT_location-Eintrag in DIE suchen:

auto loc_val = die[DW_AT::location];
Nach dem Login kopieren

Dann stellen wir sicher, dass es sich um einen Ausdruck handelt, und bitten libelfin, unseren Ausdruck zu bewerten:

if (loc_val.get_type() == value::type::exprloc) {
ptrace_expr_context context {m_pid};
auto result = loc_val.as_exprloc().evaluate(&context);
Nach dem Login kopieren

Nachdem wir den Ausdruck ausgewertet haben, müssen wir den Inhalt der Variablen lesen. Es kann im Speicher oder in Registern liegen, daher behandeln wir beide Fälle:

switch (result.location_type) {
case expr_result::type::address:
{
auto value = read_memory(result.value);
std::cout << at_name(die) << " (0x" << std::hex << result.value << ") = "
<< value << std::endl;
break;
}
case expr_result::type::reg:
{
auto value = get_register_value_from_dwarf_register(m_pid, result.value);
std::cout << at_name(die) << " (reg " << result.value << ") = "
<< value << std::endl;
break;
}
default:
throw std::runtime_error{"Unhandled variable location"};
}
Nach dem Login kopieren

Sie können sehen, dass ich den Wert anhand des Variablentyps ohne Erklärung ausgedruckt habe. Hoffentlich können Sie mit diesem Code sehen, wie das Schreiben von Variablen oder die Suche nach Variablen mit einem bestimmten Namen unterstützt wird.

Endlich können wir dies zu unserem Befehlsparser hinzufügen:

else if(is_prefix(command, "variables")) {
read_variables();
}
Nach dem Login kopieren
Testen Sie es

Schreiben Sie eine kleine Funktion mit einigen Variablen, kompilieren Sie sie ohne Optimierung und mit Debug-Informationen und prüfen Sie dann, ob Sie den Wert der Variablen lesen können. Versuchen Sie, an die Speicheradresse zu schreiben, an der die Variable gespeichert ist, und beobachten Sie, wie das Programm sein Verhalten ändert.

Es gibt bereits neun Artikel und der letzte ist noch übrig! Beim nächsten Mal werde ich einige fortgeschrittenere Konzepte besprechen, die für Sie von Interesse sein könnten. Den Code für diesen Beitrag finden Sie nun hier.

Das obige ist der detaillierte Inhalt vonEntdecken Sie Techniken zur Variablenverarbeitung in Linux-Debuggern!. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

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

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

Video Face Swap

Video Face Swap

Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen

Java-Tutorial
1662
14
PHP-Tutorial
1261
29
C#-Tutorial
1234
24
Linux -Architektur: Enthüllung der 5 Grundkomponenten Linux -Architektur: Enthüllung der 5 Grundkomponenten Apr 20, 2025 am 12:04 AM

Die fünf grundlegenden Komponenten des Linux -Systems sind: 1. Kernel, 2. Systembibliothek, 3. System Utilities, 4. Grafische Benutzeroberfläche, 5. Anwendungen. Der Kernel verwaltet Hardware -Ressourcen, die Systembibliothek bietet vorkompilierte Funktionen, Systemversorgungsunternehmen werden für die Systemverwaltung verwendet, die GUI bietet visuelle Interaktion und Anwendungen verwenden diese Komponenten, um Funktionen zu implementieren.

So überprüfen Sie die Lageradresse von Git So überprüfen Sie die Lageradresse von Git Apr 17, 2025 pm 01:54 PM

Um die Git -Repository -Adresse anzuzeigen, führen Sie die folgenden Schritte aus: 1. Öffnen Sie die Befehlszeile und navigieren Sie zum Repository -Verzeichnis; 2. Führen Sie den Befehl "git remote -v" aus; 3.. Zeigen Sie den Repository -Namen in der Ausgabe und der entsprechenden Adresse an.

VSCODE Vorheriger nächster Verknüpfungsschlüssel VSCODE Vorheriger nächster Verknüpfungsschlüssel Apr 15, 2025 pm 10:51 PM

VS Code One-Step/Nächster Schritt Verknüpfungsschlüsselnutzung: Einschritt (rückwärts): Windows/Linux: Strg ←; macOS: CMD ← Nächster Schritt (vorwärts): Windows/Linux: Strg →; macos: cmd →

Was ist der Hauptzweck von Linux? Was ist der Hauptzweck von Linux? Apr 16, 2025 am 12:19 AM

Zu den Hauptanwendungen von Linux gehören: 1. Server -Betriebssystem, 2. Eingebettes System, 3. Desktop -Betriebssystem, 4. Entwicklungs- und Testumgebung. Linux zeichnet sich in diesen Bereichen aus und bietet Stabilität, Sicherheits- und effiziente Entwicklungstools.

Wie man nach dem Schreiben des Codes erhaben ausgeführt wird Wie man nach dem Schreiben des Codes erhaben ausgeführt wird Apr 16, 2025 am 08:51 AM

Es gibt sechs Möglichkeiten, Code in Sublime auszuführen: durch Hotkeys, Menüs, Build-Systeme, Befehlszeilen, Standard-Build-Systeme und benutzerdefinierte Build-Befehle und führen Sie einzelne Dateien/Projekte aus, indem Sie mit der rechten Maustaste auf Projekte/Dateien klicken. Die Verfügbarkeit des Build -Systems hängt von der Installation des erhabenen Textes ab.

So führen Sie Java -Code in Notepad aus So führen Sie Java -Code in Notepad aus Apr 16, 2025 pm 07:39 PM

Obwohl Notepad den Java -Code nicht direkt ausführen kann, kann er durch Verwendung anderer Tools erreicht werden: Verwenden des Befehlszeilencompilers (JAVAC), um eine Bytecode -Datei (Dateiname.class) zu generieren. Verwenden Sie den Java Interpreter (Java), um Bytecode zu interpretieren, den Code auszuführen und das Ergebnis auszugeben.

Laravel -Installationscode Laravel -Installationscode Apr 18, 2025 pm 12:30 PM

Um Laravel zu installieren, befolgen Sie die folgenden Schritte: Installieren Sie den Komponisten (für MacOS/Linux und Windows). Installieren Sie LaRavel Installer. Erstellen Sie eine neue Projektstart -Service -Access -Anwendung (URL: http://127.0.0.1:8000).

So verwenden Sie VSCODE So verwenden Sie VSCODE Apr 15, 2025 pm 11:21 PM

Visual Studio Code (VSCODE) ist ein plattformübergreifender, Open-Source-Editor und kostenloser Code-Editor, der von Microsoft entwickelt wurde. Es ist bekannt für seine leichte, Skalierbarkeit und Unterstützung für eine Vielzahl von Programmiersprachen. Um VSCODE zu installieren, besuchen Sie bitte die offizielle Website, um das Installateur herunterzuladen und auszuführen. Bei der Verwendung von VSCODE können Sie neue Projekte erstellen, Code bearbeiten, Code bearbeiten, Projekte navigieren, VSCODE erweitern und Einstellungen verwalten. VSCODE ist für Windows, MacOS und Linux verfügbar, unterstützt mehrere Programmiersprachen und bietet verschiedene Erweiterungen über den Marktplatz. Zu den Vorteilen zählen leicht, Skalierbarkeit, umfangreiche Sprachunterstützung, umfangreiche Funktionen und Versionen

See all articles