Maison Tutoriel système Linux Extension de la pile du débogueur Linux !

Extension de la pile du débogueur Linux !

Jan 06, 2024 pm 10:25 PM
linux linux教程 红帽 linux系统 linux命令 certification Linux chapeau rouge Linux vidéo Linux

Présentation Parfois, l'information la plus importante que vous devez connaître est la manière dont l'état actuel de votre programme en est arrivé là. Il existe une commande backtrace, qui vous donne la chaîne d'appels de fonction actuelle de votre programme. Cet article vous montrera comment implémenter le déroulement de la pile sur x86_64 pour générer un tel traçage.
Index des séries

Ces liens seront mis en ligne au fur et à mesure que d'autres articles seront publiés.

  1. Préparer l'environnement
  2. Point d'arrêt
  3. Registres et mémoire
  4. ELF et NAIN
  5. Code source et signaux
  6. Exécution étape par étape au niveau du code source
  7. Points d'arrêt au niveau de la source
  8. Extension de la pile
  9. Lire les variables
  10. Prochaines étapes

Utilisez le programme suivant comme exemple :

void a() {
//stopped here
}
void b() {
a();
}
void c() {
a();
}
int main() {
b();
c();
}
Copier après la connexion

Si le débogueur s'arrête à la ligne //stopped here', il existe deux manières d'y accéder : main->b->a ou main->c->a`. Si nous définissons un point d'arrêt avec LLDB, continuons l'exécution et demandons un traçage, alors nous obtenons ce qui suit :

* 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
Copier après la connexion

Cela signifie que nous sommes actuellement dans la fonction a, a saute de la fonction b, b saute de la fonction principale, etc. Les deux dernières images indiquent comment le compilateur amorce la fonction principale.

La question est maintenant de savoir comment l'implémenter sur x86_64. L'approche la plus robuste serait d'analyser la partie .eh_frame du fichier ELF et de comprendre comment dérouler la pile à partir de là, mais ce serait pénible. Vous pourriez le faire en utilisant libunwind ou similaire, mais c'est ennuyeux. Au lieu de cela, nous supposons que le compilateur a configuré la pile d'une manière ou d'une autre et nous la parcourrons manuellement. Pour ce faire, nous devons d’abord comprendre la disposition de la pile.

High
| ... |
+---------+
+24| Arg 1 |
+---------+
+16| Arg 2 |
+---------+
+ 8| Return |
+---------+
EBP+--> |Saved EBP|
+---------+
- 8| Var 1 |
+---------+
ESP+--> | Var 2 |
+---------+
| ... |
Low
Copier après la connexion

Comme vous pouvez le voir, le pointeur du dernier frame de pile est stocké au début du frame de pile actuel, créant une liste chaînée de pointeurs. La pile est déroulée en fonction de cette liste chaînée. Nous pouvons trouver la fonction de la trame suivante dans la liste en recherchant l'adresse de retour dans le message DWARF. Certains compilateurs ignoreront le suivi de l'adresse de base de trame d'EBP, car celle-ci peut être exprimée sous la forme d'un décalage par rapport à ESP et libérer un registre supplémentaire. Même avec les optimisations activées, passer -fno-omit-frame-pointer à GCC ou Clang le forcera à suivre les conventions dont nous dépendons.

Nous ferons tout le travail dans la fonction print_backtrace :

void debugger::print_backtrace() {
Copier après la connexion

La première chose à décider est le format à utiliser pour imprimer les informations du cadre. J'ai utilisé un lambda pour déployer cette méthode :

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;
};
Copier après la connexion

La première image imprimée est l’image en cours d’exécution. Nous pouvons obtenir des informations sur cette trame en recherchant le compteur de programme actuel dans DWARF :

auto current_func = get_function_from_pc(get_pc());
output_frame(current_func);
Copier après la connexion

Ensuite, nous devons obtenir le pointeur de trame et l'adresse de retour de la fonction actuelle. Le pointeur de trame est stocké dans le registre rbp et l'adresse de retour est empilée sur 8 octets à partir du pointeur de trame.

auto frame_pointer = get_register_value(m_pid, reg::rbp);
auto return_address = read_memory(frame_pointer+8);
Copier après la connexion

Nous avons maintenant toutes les informations dont nous avons besoin pour étendre la pile. Je continue de me dérouler jusqu'à ce que le débogueur atteigne main, mais vous pouvez également choisir de vous arrêter lorsque le pointeur de trame est 0x0, qui sont les fonctions que vous appelez avant d'appeler la fonction principale. Nous récupérerons le pointeur de trame et l’adresse de retour de chaque trame et imprimerons les informations.

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);
}
}
Copier après la connexion

C'est tout ! Voici l'intégralité de la fonction :

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);
}
}
Copier après la connexion
Ajouter une commande

Bien sûr, nous devons exposer cette commande à l'utilisateur.

else if(is_prefix(command, "backtrace")) {
print_backtrace();
}
Copier après la connexion
Test

Une façon de tester cette fonctionnalité consiste à écrire un programme de test avec un tas de petites fonctions qui s'appellent les unes les autres. Définissez quelques points d'arrêt, parcourez le code et assurez-vous que votre traçage est exact.

Nous avons parcouru un long chemin depuis un programme qui ne pouvait que générer et s'attacher à d'autres programmes. L'avant-dernier article de cette série complétera l'implémentation du débogueur en prenant en charge la lecture et l'écriture de variables. En attendant, vous pouvez trouver le code de cet article ici.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

AI Hentai Generator

AI Hentai Generator

Générez AI Hentai gratuitement.

Article chaud

R.E.P.O. Crystals d'énergie expliqués et ce qu'ils font (cristal jaune)
3 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Meilleurs paramètres graphiques
3 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Comment réparer l'audio si vous n'entendez personne
3 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Comment déverrouiller tout dans Myrise
4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Comment ouvrir web.xml Comment ouvrir web.xml Apr 03, 2025 am 06:51 AM

Pour ouvrir un fichier web.xml, vous pouvez utiliser les méthodes suivantes: Utilisez un éditeur de texte (tel que le bloc-notes ou TextEdit) pour modifier les commandes à l'aide d'un environnement de développement intégré (tel qu'Eclipse ou NetBeans) (Windows: Notepad web.xml; Mac / Linux: Open -A TextEdit web.xml)

Quatre façons d'implémenter le multithreading dans le langage C Quatre façons d'implémenter le multithreading dans le langage C Apr 03, 2025 pm 03:00 PM

Le multithreading dans la langue peut considérablement améliorer l'efficacité du programme. Il existe quatre façons principales d'implémenter le multithreading dans le langage C: créer des processus indépendants: créer plusieurs processus en cours d'exécution indépendante, chaque processus a son propre espace mémoire. Pseudo-Multithreading: Créez plusieurs flux d'exécution dans un processus qui partagent le même espace mémoire et exécutent alternativement. Bibliothèque multi-thread: Utilisez des bibliothèques multi-threades telles que PTHEADS pour créer et gérer des threads, en fournissant des fonctions de fonctionnement de thread riches. Coroutine: une implémentation multi-thread légère qui divise les tâches en petites sous-tâches et les exécute tour à tour.

À quoi sert le mieux le Linux? À quoi sert le mieux le Linux? Apr 03, 2025 am 12:11 AM

Linux est mieux utilisé comme gestion de serveurs, systèmes intégrés et environnements de bureau. 1) Dans la gestion des serveurs, Linux est utilisé pour héberger des sites Web, des bases de données et des applications, assurant la stabilité et la fiabilité. 2) Dans les systèmes intégrés, Linux est largement utilisé dans les systèmes électroniques intelligents et automobiles en raison de sa flexibilité et de sa stabilité. 3) Dans l'environnement de bureau, Linux fournit des applications riches et des performances efficaces.

Comment est la compatibilité Debian Hadoop Comment est la compatibilité Debian Hadoop Apr 02, 2025 am 08:42 AM

Debianlinux est connu pour sa stabilité et sa sécurité et est largement utilisé dans les environnements de serveur, de développement et de bureau. Bien qu'il y ait actuellement un manque d'instructions officielles sur la compatibilité directe avec Debian et Hadoop, cet article vous guidera sur la façon de déployer Hadoop sur votre système Debian. Exigences du système Debian: Avant de commencer la configuration de Hadoop, assurez-vous que votre système Debian répond aux exigences de fonctionnement minimales de Hadoop, qui comprend l'installation de l'environnement d'exécution Java (JRE) nécessaire et des packages Hadoop. Étapes de déploiement de Hadoop: Télécharger et unzip Hadoop: Téléchargez la version Hadoop dont vous avez besoin sur le site officiel d'Apachehadoop et résolvez-le

Dois-je installer un client Oracle lors de la connexion à une base de données Oracle à l'aide de Go? Dois-je installer un client Oracle lors de la connexion à une base de données Oracle à l'aide de Go? Apr 02, 2025 pm 03:48 PM

Dois-je installer un client Oracle lors de la connexion à une base de données Oracle à l'aide de Go? Lorsque vous développez GO, la connexion aux bases de données Oracle est une exigence commune ...

Debian Strings est-il compatible avec plusieurs navigateurs Debian Strings est-il compatible avec plusieurs navigateurs Apr 02, 2025 am 08:30 AM

"Debianstrings" n'est pas un terme standard, et sa signification spécifique n'est pas encore claire. Cet article ne peut pas commenter directement la compatibilité de son navigateur. Cependant, si "DebianStrings" fait référence à une application Web exécutée sur un système Debian, sa compatibilité du navigateur dépend de l'architecture technique de l'application elle-même. La plupart des applications Web modernes se sont engagées à compatibilité entre les navigateurs. Cela repose sur les normes Web suivantes et l'utilisation de technologies frontales bien compatibles (telles que HTML, CSS, JavaScript) et les technologies back-end (telles que PHP, Python, Node.js, etc.). Pour s'assurer que l'application est compatible avec plusieurs navigateurs, les développeurs doivent souvent effectuer des tests croisés et utiliser la réactivité

Impossible de se connecter à MySQL en tant que racine Impossible de se connecter à MySQL en tant que racine Apr 08, 2025 pm 04:54 PM

Les principales raisons pour lesquelles vous ne pouvez pas vous connecter à MySQL en tant que racines sont des problèmes d'autorisation, des erreurs de fichier de configuration, des problèmes de mot de passe incohérents, des problèmes de fichiers de socket ou une interception de pare-feu. La solution comprend: vérifiez si le paramètre Bind-Address dans le fichier de configuration est configuré correctement. Vérifiez si les autorisations de l'utilisateur racine ont été modifiées ou supprimées et réinitialisées. Vérifiez que le mot de passe est précis, y compris les cas et les caractères spéciaux. Vérifiez les paramètres et les chemins d'autorisation du fichier de socket. Vérifiez que le pare-feu bloque les connexions au serveur MySQL.

libv est deux libv est deux Apr 03, 2025 pm 08:03 PM

J'ai développé un projet appelé Lua-Libuv et je suis heureux de partager mon expérience. L'intention initiale du projet est d'explorer comment utiliser Libuv (une bibliothèque d'E / S asynchrone écrite en c) pour créer un serveur HTTP simple sans avoir à apprendre le langage C en profondeur. Avec l'aide de Chatgpt, j'ai terminé le code de base de HTTP.C. Lorsque je traite des connexions persistantes, j'ai réussi à mettre en œuvre la clôture de la connexion et à libérer les ressources au bon moment. Au début, j'ai essayé de créer un serveur simple qui a mis fin au programme principal en fermant la connexion, mais j'ai eu quelques problèmes. J'ai essayé d'envoyer des blocs de données à l'aide de streaming, et pendant que cela fonctionne, cela bloque le thread principal. En fin de compte, j'ai décidé d'abandonner cette approche parce que mon objectif n'était pas d'apprendre la langue C en profondeur. Enfin, je

See all articles