1. Qu'est-ce que GDB
gdb est l'abréviation de GNU debugger, qui est un outil de débogage de programmation.
- Site officiel de GDB : https://www.gnu.org/software/gdb/
- Langages de programmation applicables pour GDB : Ada / C / C++ / objective-c / Pascal etc.
- Fonctionnement de GDB : débogage local et débogage distant.
La dernière version de la version actuelle est la 8.0 et GDB peut fonctionner sur les systèmes d'exploitation Linux et Windows.
1.1 Installer et démarrer GDB
-
gdb -v vérifie si l'installation a réussi, sinon, installez-la (vous devez vous assurer que le compilateur est installé, comme gcc).
-
démarrer gdb
-
-
gdb test_file.exe pour démarrer le débogage de gdb, c'est-à-dire spécifier directement le nom du fichier exécutable à déboguer
-
Entrez gdb directement pour démarrer et utilisez le fichier de commande test_file.exe pour spécifier le nom du fichier après avoir entré gdb
-
Si le fichier d'exécution cible nécessite des paramètres d'entrée et de sortie (tels que les paramètres de réception argv[]), les paramètres peuvent être spécifiés de trois manières :
-
- Au démarrage de gdb, gdb --args text_file.exe
- Après avoir entré gdb, exécutez set args param_1
- Après avoir entré le débogage gdb, exécutez param_1 ou démarrez para_1
-
1.2 Fonctions de gdb
- Démarrez le programme, vous pouvez exécuter le programme comme vous le souhaitez en fonction des exigences définies par l'utilisateur.
- Permet au programme en cours de débogage de s'arrêter au point d'arrêt de débogage spécifié par l'utilisateur (le point d'arrêt peut être une expression conditionnelle).
- Lorsque le programme s'arrête, vous pouvez vérifier ce qui s'est passé dans le programme à ce moment-là. Par exemple, vous pouvez imprimer la valeur d'une variable.
- Modifier dynamiquement l'environnement d'exécution du programme variable.
1.3 L'utilisation de gdb
exécuter le programme
run(r)运行程序,如果要加参数,则是run arg1 arg2 ...
afficher le code source
list(l):查看最近十行源码
list fun:查看fun函数源代码
list file:fun:查看flie文件中的fun函数源代码
Définir des points d'arrêt et regarder des points d'arrêt
break 行号/fun设置断点。
break file:行号/fun设置断点。
break if<condition>:条件成立时程序停住。
info break(缩写:i b):查看断点。
watch expr:一旦expr值发生改变,程序停住。
delete n:删除断点。
débogage en une seule étape
continue(c):运行至下一个断点。
step(s):单步跟踪,进入函数,类似于VC中的step in。
next(n):单步跟踪,不进入函数,类似于VC中的step out。
finish:运行程序,知道当前函数完成返回,并打印函数返回时的堆栈地址和返回值及参数值等信息。
until:当厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序知道退出循环体。
Afficher les données d'exécution
print(p):查看运行时的变量以及表达式。
ptype:查看类型。
print array:打印数组所有元素。
print *array@len:查看动态内存。len是查看数组array的元素个数。
print x=5:改变运行时数据。
1.4 Erreurs de programme
- Erreur de compilation : Lors de l'écriture du programme, la spécification du langage n'est pas respectée, ce qui entraîne une erreur de compilation. Par exemple : les fautes de grammaire.
- Erreur d'exécution : le compilateur ne peut pas détecter ce type d'erreur, mais cela peut entraîner le blocage du programme lors de l'exécution. Par exemple : adresse mémoire accès illégal.
- Erreurs logiques : la compilation et l'exécution sont correctes, mais le programme ne fait pas ce que nous attendons de lui.
Défaut de segment de débogage 1.5gdb
Qu'est-ce qu'une erreur de segmentation ? Le défaut de segmentation est une erreur causée par l'accès à une adresse illégale.
- Accéder à la zone de données système, en particulier écrire des données à l'adresse mémoire protégée par le système. Par exemple : accéder à l'adresse dont l'adresse est 0.
- La mémoire hors limites (tableau hors limites, incohérence de type de variable, etc.) accède à une zone mémoire qui n'appartient pas au programme en cours.
Gdb débogue les défauts de segment, vous pouvez exécuter le programme directement. Lorsque le programme plante, gdb imprimera les informations d'exécution, par exemple : après avoir reçu le signal SIGSEGV, vous pouvez utiliser la commande pour imprimer les informations de backtrace de la pile, puis modifier le programme selon le code d'erreur du bt
programme.
Débogage du fichier 1.6.core
Fichier de base 6.1
Lorsqu'un programme plante, un fichier est généralement généré, appelé core
fichier. Le fichier core enregistre l'image mémoire lorsque le programme plante et ajoute des informations de débogage. Le processus de génération du fichier core est appelé core dump(核心已转储)
. Le système ne génère pas ce fichier par défaut.
6.2 Définir pour générer le fichier core
ulimit -c
: Vérifiez l'état du vidage mémoire.ulimit -c xxxx
: définissez la taille du fichier core.ulimit -c unlimited
: Taille illimitée du fichier core.
6.3 fichier principal de débogage gdb
ulimit -c xxxx
Après avoir défini , exécutez à nouveau le programme et une erreur de segmentation se produit. À ce stade, un core
fichier sera généré, utilisez gdb core
le fichier principal de débogage et utilisez bt
la commande pour imprimer les informations de trace de la pile.
Deux, commandes communes GDB
- Ce qui suit utilise test_file.c comme nom de l'exemple de programme source, test_file.exe comme nom de l'exemple de fichier exécutable et param_1 comme nom de l'exemple de paramètre.
- (gdb) signifie s'exécuter en mode débogage gdb
- Généralement, il existe deux méthodes couramment utilisées, à savoir le débogage des points d'arrêt et le débogage en une seule étape .
- list(l) : liste le code source
- quit(q) : quitter le mode de débogage de gdb
- Après avoir entré gdb, entrez help pour afficher les instructions pour toutes les commandes
2.1 Afficher le code source
liste [nom de la fonction] [nombre de lignes]
2.2 Débogage des points d'arrêt
(1) Définissez un point d'arrêt :
- a. break + [numéro de ligne du code source][nom de la fonction du code source][adresse mémoire]
- b. break... if condition... peut être n'importe lequel des paramètres ci-dessus, et condition est une condition. Par exemple, dans le corps de la boucle, vous pouvez définir break ... if i = 100 pour définir le nombre de boucles
supprimer le point d'arrêt
(gdb) emplacement clair : l'emplacement du paramètre est généralement le numéro de ligne d'une certaine ligne de code ou un nom de fonction spécifique. Lorsque le paramètre d'emplacement est le nom de fonction d'une fonction, cela signifie supprimer tous les points d'arrêt à l'entrée de la fonction.
(gdb) delete [breakpoints] [num] : Le paramètre breakpoints est facultatif, et le paramètre num est le numéro du point d'arrêt spécifié, qui peut être supprimé pour supprimer un certain point d'arrêt, pas tous.
désactiver le point d'arrêt
**désactiver [points d'arrêt] [num…] : le paramètre **points d'arrêt est facultatif ; num... signifie qu'il peut y avoir plusieurs paramètres, et chaque paramètre est le numéro du point d'arrêt à désactiver. Si vous spécifiez num..., la commande disable désactivera le point d'arrêt avec le numéro spécifié ; sinon, si vous ne définissez pas num..., disable désactivera tous les points d'arrêt du programme en cours.
activer le point d'arrêt
- enable [breakpoints] [num…] active plusieurs points d'arrêt spécifiés avec num… paramètres, si num… n'est pas défini, cela signifie activer tous les points d'arrêt désactivés
- enable [breakpoints] once num… Active temporairement plusieurs points d'arrêt numérotés par num…, mais le point d'arrêt ne peut être utilisé qu'une seule fois, puis il reviendra automatiquement à l'état désactivé
- enable [breakpoints] count num… Activer temporairement plusieurs points d'arrêt numérotés par num…, les points d'arrêt peuvent être utilisés compter les fois, puis entrer dans l'état désactivé
- enable [breakpoints] delete num... Active plusieurs points d'arrêt numérotés num..., mais les points d'arrêt ne peuvent être utilisés qu'une seule fois, puis ils seront définitivement supprimés.
break(b) : C'est un point d'arrêt commun, et il existe deux formes de points d'arrêt
(gdb) break location // b location, location représente l'emplacement du point d'arrêt
(gdb) break … if cond // b … if cond, ce qui signifie que si la condition de cond est vraie, le point d'arrêt sera à « … »
En utilisant la commande condition pour définir des expressions conditionnelles pour différents types de points d'arrêt, uniquement lorsque les expressions conditionnelles sont vraies (la valeur est True), le point d'arrêt correspondant sera déclenché et le programme sera suspendu.
tbreak : La commande tbreak peut être considérée comme une autre version de la commande break. L'utilisation et les fonctions des commandes tbreak et break sont très similaires. La seule différence est que le point d'arrêt atteint par la commande tbreak ne sera utilisé qu'une seule fois, même après le programme est suspendu, le point d'arrêt disparaît automatiquement.
rbreak : Différente des commandes break et tbreak, la commande rbreak agit sur les fonctions dans les programmes C et C++, et elle s'arrêtera au début de la fonction spécifiée.
-
(gdb) tbreak regex
-
- regex représente une expression régulière qui se casse au début de la fonction correspondante
-
Le point d'arrêt défini par la commande tbreak a le même effet que celui de la commande break, il existera toujours et ne disparaîtra pas automatiquement.
watch : cette commande atteint un point d'arrêt d'observation, qui peut surveiller la valeur d'une variable ou d'une expression. Ce n'est que lorsque la valeur de la variable surveillée (expression) change que le programme s'arrête.
-
(gdb) regarder cond
-
- cond représente la variable ou l'expression à surveiller
Commande rwatch : tant qu'il y a une opération pour lire la valeur de la variable cible (expression) dans le programme, le programme s'arrête ;
awatch commande : tant qu'il y a une opération pour lire la valeur de la variable cible (expression) ou changer la valeur dans le programme, le programme s'arrête.
catch : La fonction de capture d'un point d'arrêt est de surveiller l'occurrence d'un certain événement dans le programme, comme lorsqu'une certaine exception se produit dans le programme, lorsqu'une bibliothèque dynamique est chargée, etc. Une fois l'heure cible atteinte, le programme s'arrête exécution.
(2) Observez le point d'arrêt :
- a. watch + [variable] [expression] Arrête le programme lorsque la valeur de la variable ou de l'expression change.
- b. rwatch + [variable] [expression] Lorsque la variable ou l'expression est lue, arrête le programme.
- c, awatch + [variable] [expression] Lorsque la variable ou l'expression est lue ou écrite, arrête le programme.
(3) Définissez le point de capture :
catch + événement Lorsque l'événement se produit, arrête le programme.
événement peut être le suivant :
- a, throw Une exception levée par C++. (lancer est un mot-clé)
- b.catch Une exception interceptée par C++. (catch est un mot clé)
- c. Lorsque exec appelle le système, appelez exec. (exec est un mot-clé, actuellement cette fonction n'est disponible que sous HP-UX)
- d. Lorsque fork appelle le système pour appeler fork. (fork est un mot-clé, actuellement cette fonction n'est disponible que sous HP-UX)
- e. Lorsque vfork appelle le système pour appeler vfork. (vfork est un mot-clé, actuellement cette fonction n'est disponible que sous HP-UX)
- f, load ou load Lors du chargement d'une bibliothèque partagée (bibliothèque de liens dynamiques). (load est un mot-clé, actuellement cette fonction n'est disponible que sous HP-UX)
- g, décharger ou décharger lors du déchargement d'une bibliothèque partagée (bibliothèque de liens dynamiques). (unload est un mot-clé, actuellement cette fonction n'est disponible que sous HP-UX)
(4) Signal de capture :
poignée + [argu] + signaux
Signaux : il s'agit d'un signal défini par Linux/Unix. SIGINT signifie un signal de caractère d'interruption, c'est-à-dire le signal de Ctrl+C, SIGBUS signifie un signal de panne matérielle ; SIGCHLD signifie un signal pour changer l'état d'un processus enfant ; SIGKILL signifie un signal pour terminer le fonctionnement du programme, et ainsi de suite.
argument:
- nostop Lorsque le programme en cours de débogage reçoit un signal, GDB n'arrêtera pas l'exécution du programme, mais affichera un message pour vous indiquer qu'il a reçu un tel signal.
- stop GDB arrête votre programme lorsque le programme en cours de débogage reçoit un signal.
- print GDB affiche un message lorsque le programme en cours de débogage reçoit un signal.
- noprint Lorsque le programme en cours de débogage reçoit un signal, GDB ne vous dira pas que le signal a été reçu.
- pass ou noignore GDB ne traite pas les signaux lorsque le programme en cours de débogage les reçoit. Cela signifie que GDB transmettra ce signal au programme en cours de débogage pour traitement.
- nopass ou ignore Lorsque le programme débogué reçoit un signal, GDB ne laissera pas le programme débogué gérer le signal.
(5) Interruption du fil :
break [linespec] thread [threadno] [if …]
linespec Le numéro de ligne du code source où le point d'arrêt est défini. Par exemple : test.c:12 signifie que le fichier définit un point d'arrêt pour la ligne 12 dans test.c.
threadno L'ID du thread. Il est alloué par GDB et vous pouvez afficher les informations sur les threads du programme en cours d'exécution en saisissant des threads d'informations.
si… Définir une condition d'arrêt.
Afficher les informations :
(1) Afficher les données :
variable d'impression afficher les variables
print *array@len Afficher le tableau (array est le pointeur de tableau, len est la longueur de données requise)
Le format de sortie peut être défini en ajoutant des paramètres :
/ 按十六进制格式显示变量。
/d 按十进制格式显示变量。
/u 按十六进制格式显示无符号整型。
/o 按八进制格式显示变量。
/t 按二进制格式显示变量。
/a 按十六进制格式显示变量。
/c 按字符格式显示变量。
/f 按浮点数格式显示变量。
(2) Afficher la mémoire
examiner /nfu + adresse mémoire (variable de pointeur)
- n indique la longueur de la mémoire d'affichage
- f indique le format de sortie (voir ci-dessus)
- u indique le nombre d'octets spécifiés (b simple octet ; h double octet ; w quatre octets ; g huit octets ; la valeur par défaut est quatre octets)
如:x /10cw pFilePath (pFilePath为一个字符串指针,指针占4字节)
x 为examine命令的简写。
(3) Afficher les informations de la pile
trace arrière [-n][n]
- n indique que seules les informations de pile de la couche n sur le dessus de la pile sont imprimées.
- -n signifie imprimer uniquement les informations de pile de n couches au-dessus du bas de la pile.
- Sans paramètres, cela signifie imprimer toutes les informations de la pile.
2.3 Débogage en une seule étape
courir®
continuer©
suivant(n)
- Format de commande : (gdb) next count : count indique le nombre de lignes de code à exécuter en une seule étape, et la valeur par défaut est 1 ligne
- Sa principale caractéristique est que lorsqu'une instruction contenant une fonction appelante est rencontrée, quel que soit le nombre de lignes de code contenues dans la fonction, l'instruction suivante sera exécutée en une seule étape. C'est-à-dire que pour la fonction appelée, la commande suivante ne la traitera que comme une ligne de code
pas)
- (gdb) step count : Le paramètre count indique le nombre de lignes exécutées en même temps, et la valeur par défaut est 1 ligne.
- Normalement, la commande pas à pas et la commande suivante ont la même fonction et exécutent toutes deux le programme pas à pas. La différence est que lorsque la ligne de code exécutée par la commande step contient une fonction, elle entrera dans la fonction et arrêtera l'exécution à la première ligne de code de la fonction.
jusqu'à (u)
- (gdb) until : La commande until sans paramètres peut faire en sorte que le débogueur GDB parcoure rapidement le corps de la boucle actuelle et s'exécute jusqu'à ce que le corps de la boucle s'arrête. Notez que la commande until ne joue en aucun cas ce rôle, seulement lorsqu'elle est exécutée jusqu'à la fin du corps de la boucle (la dernière ligne de code), la commande until aura cet effet ; sinon, la commande until a le même fonctionne comme la commande suivante, uniquement le programme d'exécution en une seule étape
(gdb) until location : Le paramètre location est le numéro de ligne d'une certaine ligne de code
Afficher la valeur de la variable
imprimer§
- p num_1 : Le paramètre num_1 permet de faire référence à la variable ou expression cible à visualiser ou modifier
- Sa fonction est de sortir ou de modifier la valeur de la variable ou de l'expression spécifiée dans le processus de débogage GDB du programme
afficher
- (gdb) expression d'affichage
- (gdb) affichage/fmt expr
- expr indique la variable ou l'expression cible à visualiser ; le paramètre fmt est utilisé pour spécifier le format de la variable ou de l'expression de sortie
- (gdb) undisplay num…
- (gdb) supprimer le numéro d'affichage…
- Le paramètre num... indique le numéro de la variable ou de l'expression cible, et le nombre de nombres peut être multiple
- (gdb) désactiver l'affichage du numéro…
- Désactiver l'affichage automatique des variables ou expressions actives dans la liste
- (gdb) active l'affichage du numéro…
- Vous pouvez également activer une variable ou une expression actuellement désactivée
- Comme la commande d'impression, la commande d'affichage est également utilisée pendant la phase de débogage pour afficher la valeur d'une variable ou d'une expression
- La différence entre eux est que lors de l'utilisation de la commande d'affichage pour afficher la valeur d'une variable ou d'une expression, chaque fois que le programme fait une pause (comme l'exécution en une seule étape), le débogueur GDB l'imprimera automatiquement pour nous, tandis que la commande d'impression ne le fera pas.
GDB handle Commande : traitement du signal
→(gdb) handle signal mode Parmi eux, le paramètre signal indique le signal cible à définir, qui est généralement le nom complet (SIGINT) ou l'abréviation (la partie après suppression de 'SIG', comme INT) d'un certain signal ; si vous voulez spécifier tous les signaux peuvent être représentés par tous.
Le paramètre mode est utilisé pour spécifier la façon dont GDB gère les informations cibles, et sa valeur peut être la suivante :
- ostop : Lorsque le signal se produit, GDB ne suspendra pas le programme, il peut continuer à s'exécuter, mais affichera un message d'invite nous indiquant que le signal s'est produit ;
- stop : Lorsque le signal se produit, GDB suspend l'exécution du programme.
- noprint : GDB n'imprimera aucune information d'invite lorsque le signal se produira ;
- print : lorsqu'un signal se produit, GDB imprime les informations d'invite nécessaires ;
- nopass (ou ignore) : pendant que GDB capture le signal cible, le programme n'est pas autorisé à traiter le signal par lui-même ;
- pass (ou noignore) : bien que le débogage GDB capture le signal cible, il permet également au programme de gérer automatiquement le signal.
En mode gdb, vous pouvez afficher les informations de différents signaux via des signaux d'information ou des signaux d'information <signal_name> (par exemple, des signaux d'information SIGINT).
Commandes frame et backtrace GDB : afficher les informations de la pile
(gdb) frame spec Cette commande peut sélectionner le cadre de pile spécifié par le paramètre spec comme cadre de pile actuel. Il existe trois méthodes couramment utilisées pour spécifier la valeur du paramètre spec :
- Spécifié par le numéro du cadre de pile. 0 est le numéro de cadre de pile correspondant à la fonction actuellement appelée, et la fonction correspondant au cadre de pile avec le plus grand nombre est généralement la fonction main() ;
- A l'aide de la spécification d'adresse du cadre de pile. L'adresse du cadre de pile peut être vue dans les informations imprimées par la commande de cadre d'information (sera discuté plus tard);
- Spécifié par le nom de fonction de la fonction. Notez que s'il s'agit d'une fonction récursive similaire qui correspond à plusieurs cadres de pile, le cadre de pile avec le plus petit nombre est spécifié par cette méthode.
(gdb) cadre d'information Nous pouvons voir les informations stockées dans le cadre de pile actuel
Cette commande imprimera les informations suivantes du cadre de pile actuel dans l'ordre :
- Le numéro du cadre de pile actuel et l'adresse du cadre de pile ;
- L'adresse de stockage de la fonction correspondant au cadre de pile actuel et l'adresse du stockage du code lorsque la fonction est appelée
- L'appelant de la fonction courante, l'adresse du cadre de pile correspondant ;
- Le langage de programmation utilisé pour écrire ce cadre de pile ;
- L'adresse de stockage et la valeur du paramètre de fonction ;
- L'adresse de stockage de la variable locale dans la fonction ;
- Les variables de registre stockées dans le cadre de pile, telles que le registre d'instructions (représenté par rip dans l'environnement 64 bits et eip dans l'environnement 32 bits), le registre de pointeur de base de pile (représenté par rbp dans l'environnement 64 bits, et ebp dans l'environnement 32 bits), etc.
De plus, vous pouvez également utiliser info args
la commande pour afficher la valeur de chaque paramètre de la fonction actuelle ; utilisez info locals
la commande pour afficher la valeur de chaque variable locale dans la fonction actuelle.
(gdb) backtrace [-full] [n] est utilisé pour imprimer les informations de tous les cadres de pile dans l'environnement de débogage actuel
Parmi eux, les paramètres entourés de [] sont facultatifs et leurs significations sont :
- n : une valeur entière, lorsqu'il s'agit d'un entier positif, cela signifie imprimer les informations des n cadres de pile les plus internes ; lorsque n est un entier négatif, cela signifie imprimer les informations des n cadres de pile les plus externes ;
- -full : affiche les valeurs des variables locales lors de l'impression des informations sur le cadre de la pile.
Édition GDB et recherche de code source
Commande d'édition GDB : modifier les fichiers
-
(gdb) modifier [emplacement]
-
(gdb) modifier [nom du fichier] : [emplacement]
-
- location représente l'emplacement dans le programme. Cette commande indique d'activer l'emplacement spécifié du fichier, puis de le modifier.
- Si vous rencontrez une erreur "bash: /bin/ex: No such file or directory", parce que l'éditeur par défaut de GDB est ex, vous devez spécifier l'éditeur, comme export EDITOR=/usr/bin/vim ou export EDITOR =/usr/bin/vi
Commande de recherche GDB : rechercher des fichiers
-
recherche
-
recherche inversée
-
- Le format de commande du premier élément signifie une recherche vers l'avant depuis le début de la ligne actuelle, et le second élément signifie une recherche vers l'arrière depuis le début de la ligne actuelle. Parmi elles, regexp est une expression régulière. Une expression régulière décrit un modèle de correspondance de chaîne, qui peut être utilisé pour vérifier si une chaîne contient une certaine sous-chaîne, remplacer la sous-chaîne correspondante ou extraire une certaine condition d'une sous-chaîne. De nombreux langages de programmation prennent en charge l'utilisation d'expressions régulières.
Troisièmement, l'utilisation du débogueur GDB
De manière générale, GDB vous aide principalement à remplir les quatre fonctions suivantes :
1. Démarrez votre programme et vous pouvez exécuter le programme comme vous le souhaitez en fonction de vos besoins personnalisés.
2. Autorisez le programme en cours de débogage à s'arrêter au point d'arrêt spécifié. (Le point d'arrêt peut être une expression conditionnelle)
3. Lorsque le programme est arrêté, vous pouvez vérifier ce qui s'est passé dans votre programme à ce moment.
4. Modifiez dynamiquement l'environnement d'exécution de votre programme.
D'après ce qui précède, GDB n'est pas différent des outils de débogage généraux. Il complète essentiellement ces fonctions. Cependant, dans les détails, vous constaterez que GDB est un outil de débogage puissant. Vous êtes peut-être plus habitué aux outils de débogage graphiques, mais il existe parfois, l'outil de débogage de la ligne de commande a des fonctions que l'outil graphique ne peut pas compléter. Voyons-les un par un.
Un exemple de débogage :
源程序:tst.c
1 #include <stdio.h>
2
3 int func(int n)
4 {
5 int sum=0,i;
6 for(i=0; i<n; i++)
7 {
8 sum+=i;
9 }
10 return sum;
11 }
12
13
14 main()
15 {
16 int i;
17 long result = 0;
18 for(i=1; i<=100; i++)
19 {
20 result += i;
21 }
22
23 printf("result[1-100] = %d /n", result );
24 printf("result[1-250] = %d /n", func(250) );
25 }
Compiler et générer des fichiers exécutables : (sous Linux)
hchen/test> cc -g tst.c -o tst
Déboguer avec GDB :
hchen/test> gdb tst <---------- 启动GDB
GNU gdb 5.1.1
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-SUSE-linux"...
(gdb) l <-------------------- l命令相当于list,从第一行开始例出原码。
1 #include <stdio.h>
2
3 int func(int n)
4 {
5 int sum=0,i;
6 for(i=0; i<n; i++)
7 {
8 sum+=i;
9 }
10 return sum;
(gdb) <-------------------- 直接回车表示,重复上一次命令
11 }
12
13
14 main()
15 {
16 int i;
17 long result = 0;
18 for(i=1; i<=100; i++)
19 {
20 result += i;
(gdb) break 16 <-------------------- 设置断点,在源程序第16行处。
Breakpoint 1 at 0x8048496: file tst.c, line 16.
(gdb) break func <-------------------- 设置断点,在函数func()入口处。
Breakpoint 2 at 0x8048456: file tst.c, line 5.
(gdb) info break <-------------------- 查看断点信息。
Num Type Disp Enb Address What
1 breakpoint keep y 0x08048496 in main at tst.c:16
2 breakpoint keep y 0x08048456 in func at tst.c:5
(gdb) r <--------------------- 运行程序,run命令简写
Starting program: /home/hchen/test/tst
Breakpoint 1, main () at tst.c:17 <---------- 在断点处停住。
17 long result = 0;
(gdb) n <--------------------- 单条语句执行,next命令简写。
18 for(i=1; i<=100; i++)
(gdb) n
20 result += i;
(gdb) n
18 for(i=1; i<=100; i++)
(gdb) n
20 result += i;
(gdb) c <--------------------- 继续运行程序,continue命令简写。
Continuing.
result[1-100] = 5050 <----------程序输出。
Breakpoint 2, func (n=250) at tst.c:5
5 int sum=0,i;
(gdb) n
6 for(i=1; i<=n; i++)
(gdb) p i <--------------------- 打印变量i的值,print命令简写。
$1 = 134513808
(gdb) n
8 sum+=i;
(gdb) n
6 for(i=1; i<=n; i++)
(gdb) p sum
$2 = 1
(gdb) n
8 sum+=i;
(gdb) p i
$3 = 2
(gdb) n
6 for(i=1; i<=n; i++)
(gdb) p sum
$4 = 3
(gdb) bt <--------------------- 查看函数堆栈。
#0 func (n=250) at tst.c:5
#1 0x080484e4 in main () at tst.c:24
#2 0x400409ed in __libc_start_main () from /lib/libc.so.6
(gdb) finish <--------------------- 退出函数。
Run till exit from #0 func (n=250) at tst.c:5
0x080484e4 in main () at tst.c:24
24 printf("result[1-250] = %d /n", func(250) );
Value returned is $6 = 31375
(gdb) c <--------------------- 继续运行。
Continuing.
result[1-250] = 31375 <----------程序输出。
Program exited with code 027. <--------程序退出,调试结束。
(gdb) q <--------------------- 退出gdb。
hchen/test>
Eh bien, avec les connaissances perceptuelles ci-dessus, apprenons à connaître gdb systématiquement.
Commandes gdb de base :
GDB常用命令 格式 含义 简写
list List [开始,结束] 列出文件的代码清单 l
prit Print 变量名 打印变量内容 p
break Break [行号或函数名] 设置断点 b
continue Continue [开始,结束] 继续运行 c
info Info 变量名 列出信息 i
next Next 下一行 n
step Step 进入函数(步入) S
display Display 变量名 显示参数
file File 文件名(可以是绝对路径和相对路径) 加载文件
run Run args 运行程序 r
Quatre, combat réel GDB
Voici un exemple pratique utilisant la commande ci-dessus :
[[email protected] bufbomb]# gdb bufbomb
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-75.el6)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-RedHat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/Temp/bufbomb/bufbomb...done.
(gdb) b getbuf
Breakpoint 1 at 0x8048ad6
(gdb) run -t cdai
Starting program: /root/Temp/bufbomb/bufbomb -t cdai
Team: cdai
Cookie: 0x5e5ee04e
Breakpoint 1, 0x08048ad6 in getbuf ()
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.149.el6_6.4.i686
(gdb) bt
#0 0x08048ad6 in getbuf ()
#1 0x08048db2 in test ()
#2 0x08049085 in launch ()
#3 0x08049257 in main ()
(gdb) info frame 0
Stack frame at 0xffffb540:
eip = 0x8048ad6 in getbuf; saved eip 0x8048db2
called by frame at 0xffffb560
Arglist at 0xffffb538, args:
Locals at 0xffffb538, Previous frame's sp is 0xffffb540
Saved registers:
ebp at 0xffffb538, eip at 0xffffb53c
(gdb) info registers
eax 0xc 12
ecx 0xffffb548 -19128
edx 0xc8c340 13157184
ebx 0x0 0
esp 0xffffb510 0xffffb510
ebp 0xffffb538 0xffffb538
esi 0x804b018 134524952
edi 0xffffffff -1
eip 0x8048ad6 0x8048ad6 <getbuf+6>
eflags 0x282 [ SF IF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99
(gdb) x/10x $sp
0xffffb510: 0xf7ffc6b0 0x00000001 0x00000001 0xffffb564
0xffffb520: 0x08048448 0x0804a12c 0xffffb548 0x00c8aff4
0xffffb530: 0x0804b018 0xffffffff
(gdb) si
0x08048ad9 in getbuf ()
(gdb) si
0x08048adc in getbuf ()
(gdb) si
0x080489c0 in Gets ()
(gdb) n
Single stepping until exit from function Gets,
which has no line number information.
Type string:123
0x08048ae1 in getbuf ()
(gdb) si
0x08048ae2 in getbuf ()
(gdb) c
Continuing.
Dud: getbuf returned 0x1
Better luck next time
Program exited normally.
(gdb) quit
4.1 Débogage inverse
La fonction Reversal Debugging a été ajoutée après GDB 7.0. Plus précisément, par exemple, j'ai défini des points d'arrêt sur getbuf() et main(), et lorsque le programme est lancé, il s'arrêtera au point d'arrêt de la fonction main(). À ce stade, après avoir tapé record, passez au point d'arrêt suivant getbuf(), et GDB enregistrera les informations d'exécution de main() à getbuf(). Utilisez maintenant rn pour déboguer en sens inverse de getbuf() à main(). Comme dans "X-Men : Days of Future Past", c'est incroyable !
Cette méthode convient pour trouver le code qui a causé le bogue à l'envers du bogue, et l'aspect pratique varie selon la situation. Bien sûr, il a aussi des limites. Lorsque des conditions externes telles que la sortie d'E / S du programme changent, GDB ne peut pas "inverser".
[[email protected] bufbomb]# gdb bufbomb
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-75.el6)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/Temp/bufbomb/bufbomb...done.
(gdb) b getbuf
Breakpoint 1 at 0x8048ad6
(gdb) b main
Breakpoint 2 at 0x80490c6
(gdb) run -t cdai
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/Temp/bufbomb/bufbomb -t cdai
Breakpoint 2, 0x080490c6 in main ()
(gdb) record
(gdb) c
Continuing.
Team: cdai
Cookie: 0x5e5ee04e
Breakpoint 1, 0x08048ad6 in getbuf ()
(gdb) rn
Single stepping until exit from function getbuf,
which has no line number information.
0x08048dad in test ()
(gdb) rn
Single stepping until exit from function test,
which has no line number information.
0x08049080 in launch ()
(gdb) rn
Single stepping until exit from function launch,
which has no line number information.
0x08049252 in main ()
4.2 VSCode+GDB+Qemu déboguant le noyau Linux ARM64
Le noyau Linux est un système très compliqué, et il est difficile pour les débutants de démarrer. S'il existe un environnement de débogage pratique, l'efficacité de l'apprentissage peut être améliorée d'au moins 5 à 10 fois.
Afin d'apprendre le noyau Linux, il y a généralement ces deux besoins :
- Peut se débarrasser du matériel, compiler et exécuter facilement Linux
- Vous pouvez utiliser des outils graphiques pour déboguer Linux
L'auteur utilise VSCode+GDB+Qemu pour répondre à ces deux exigences :
- qemu est utilisé comme machine virtuelle pour démarrer Linux.
- VSCode + GDB est utilisé comme outil de débogage pour le DEBUG graphique.
L'effet final est à peu près le suivant :
interface d'exécution de qemu :
interface de débogage vscode :
Ce qui suit présentera étape par étape comment créer l'environnement ci-dessus. Toutes les opérations de cet article sont effectuées sur une machine virtuelle Vmware Ubuntu16.
Installer la chaîne d'outils de compilation
Étant donné qu'Ubuntu est une architecture X86, pour compiler les fichiers arm64, vous devez installer une chaîne d'outils de compilation croisée
sudo apt-get install gcc-aarch64-linux-gnu
sudo apt-get install libncurses5-dev build-essential git bison flex libssl-dev
créer un système de fichiers racine
Le démarrage de Linux doit coopérer avec le système de fichiers racine. Ici, nous utilisons busybox pour créer un système de fichiers racine simple
compiler la boîte occupée
wget https://busybox.net/downloads/busybox-1.33.1.tar.bz2
tar -xjf busybox-1.33.1.tar.bz2
cd busybox-1.33.1
Activer l'option de compilation de bibliothèque statique
make menuconfig
Settings --->
[*] Build static binary (no shared libs)
Spécifier les outils de compilation
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
compiler
make
make install
La compilation est terminée et le répertoire _install est généré sous le répertoire busybox
système de fichiers personnalisé
Pour que le processus d'initialisation démarre normalement, une configuration supplémentaire est requise
Ajoutez les répertoires etc, dev et lib au répertoire racine
# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install [1:02:17]
$ mkdir etc dev lib
# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install [1:02:17]
$ ls
bin dev etc lib linuxrc sbin usr
Créez des fichiers séparément dans etc :
# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install/etc [1:06:13]
$ cat profile
#!/bin/sh
export HOSTNAME=bryant
export USER=root
export HOME=/home
export PS1="[$USER@$HOSTNAME \W]\# "
PATH=/bin:/sbin:/usr/bin:/usr/sbin
LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH
export PATH LD_LIBRARY_PATH
# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install/etc [1:06:16]
$ cat inittab
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r
# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install/etc [1:06:19]
$ cat fstab
#device mount-point type options dump fsck order
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
sysfs /sys sysfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0
debugfs /sys/kernel/debug debugfs defaults 0 0
kmod_mount /mnt 9p trans=virtio 0 0
# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install/etc [1:06:26]
$ ls init.d
rcS
# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install/etc [1:06:30]
$ cat init.d/rcS
mkdir -p /sys
mkdir -p /tmp
mkdir -p /proc
mkdir -p /mnt
/bin/mount -a
mkdir -p /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
Voici une petite explication de ces fichiers :
- Une fois que busybox est démarré en tant que linuxrc, il lira /etc/profile, qui définit certaines variables d'environnement et propriétés du shell
- Montez le système de fichiers en fonction des informations de montage fournies par /etc/fstab
- busybox lira sysinit depuis /etc/inittab et l'exécutera, où sysinit pointe vers /etc/init.d/rcS
- Dans /etc/init.d/rcS, la commande mdev -s est très importante, elle analysera le répertoire /sys, trouvera les périphériques de caractères et les périphériques de blocage, et mknod sous /dev
répertoire de développement :
# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install/dev [1:17:36]
$ sudo mknod console c 5 1
Cette étape est très importante, sans le fichier console, la sortie du mode utilisateur ne peut pas être imprimée sur le port série
Répertoire lib : copiez la bibliothèque lib et prenez en charge les applications compilées dynamiquement pour exécuter :
# bryant @ ubuntu in ~/Downloads/busybox-1.33.1/_install/lib [1:18:43]
$ cp /usr/aarch64-linux-gnu/lib/*.so* -a .
Compiler le noyau
Configurer le noyau
Le code source du noyau Linux peut être téléchargé directement depuis github.
Générer .config selon le fichier arch/arm64/configs/defconfig
make defconfig ARCH=arm64
Ajoutez la configuration suivante au fichier .config
CONFIG_DEBUG_INFO=y
CONFIG_INITRAMFS_SOURCE="./root"
CONFIG_INITRAMFS_ROOT_UID=0
CONFIG_INITRAMFS_ROOT_GID=0
CONFIG_DEBUG_INFO est pour le débogage
CONFIG_INITRAMFS_SOURCE est de spécifier l'emplacement du disque virtuel du noyau, de sorte que le disque virtuel sera directement compilé dans l'image du noyau après avoir spécifié.
Nous transférons le système de fichiers racine précédemment créé au répertoire racine :
# bryant @ ubuntu in ~/Downloads/linux-arm64 on git:main x [1:26:56]
$ cp -r ../busybox-1.33.1/_install root
exécuter compiler
make ARCH=arm64 Image -j8 CROSS_COMPILE=aarch64-linux-gnu-
Spécifier la cible comme Image ici ne compilera que le noyau, pas les modules, ce qui augmentera la vitesse de compilation
démarrer qemu
télécharger qemu
A noter qu'il est préférable de compiler qemu depuis le code source, la version de qemu installée directement avec apt-get peut être trop basse, ce qui rend impossible le démarrage du noyau arm64. L'auteur utilise la version 4.2.1 de qemu
apt-get install build-essential zlib1g-dev pkg-config libglib2.0-dev binutils-dev libboost-all-dev autoconf libtool libssl-dev libpixman-1-dev libpython-dev python-pip python-capstone virtualenv
wget https://download.qemu.org/qemu-4.2.1.tar.xz
tar xvJf qemu-4.2.1.tar.xz
cd qemu-4.2.1
./configure --target-list=x86_64-softmmu,x86_64-linux-user,arm-softmmu,arm-linux-user,aarch64-softmmu,aarch64-linux-user --enable-kvm
make
sudo make install
Une fois la compilation terminée, qemu se trouve dans le répertoire /usr/local/bin
$ /usr/local/bin/qemu-system-aarch64 --version
QEMU emulator version 4.2.1
Copyright (c) 2003-2019 Fabrice Bellard and the QEMU Project developers
démarrer le noyau Linux
/usr/local/bin/qemu-system-aarch64 -m 512M -smp 4 -cpu cortex-a57 -machine virt -kernel
Voici quelques explications sur les paramètres :
-m 512M
La mémoire est de 512M-smp 4
4 Nucléaire-cpu cortex-a57
le processeur est cortex-a57-kernel
fichier image du noyau-append
Le paramètre cmdline passé au noyau. Parmi eux, rdinit spécifie le processus d'initialisation ; nokaslr interdit la randomisation de l'adresse de démarrage du noyau, ce qui est très important, sinon le débogage GDB peut avoir des problèmes ; console=ttyAMA0 spécifie le port série, sans cette étape, vous ne pouvez pas voir la sortie de linux ;-nographic
désactiver la sortie graphique-s
Écoutez le port gdb et le programme gdb peut être connecté via le port 1234.
Voici une explication du fonctionnement de console=ttyAMA0.
La visualisation du code source linux montre que ttyAMA0 correspond à AMBA_PL011
ce pilote :
config SERIAL_AMBA_PL011_CONSOLE
bool "Support for console on AMBA serial port"
depends on SERIAL_AMBA_PL011=y
select SERIAL_CORE_CONSOLE
select SERIAL_EARLYCON
help
Say Y here if you wish to use an AMBA PrimeCell UART as the system
console (the system console is the device which receives all kernel
messages and warnings and which allows logins in single user mode).
Even if you say Y here, the currently visible framebuffer console
(/dev/tty0) will still be used as the system console by default, but
you can alter that using a kernel command line option such as
"console=ttyAMA0". (Try "man bootparam" or see the documentation of
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
AMBA_PL011 est un périphérique de port série standard de bras, et la sortie de qemu est le port série simulé.
Dans le fichier de code source de qemu, vous pouvez également voir les fichiers associés de PL011 :
# bryant @ ubuntu in ~/Downloads/qemu-4.2.1 [1:46:54]
$ find . -name "*pl011*"
./hw/char/pl011.c
Après avoir démarré Linux avec succès, les impressions du port série sont les suivantes :
[ 3.401567] usbcore: registered new interface driver usbhid
[ 3.404445] usbhid: USB HID core driver
[ 3.425030] NET: Registered protocol family 17
[ 3.429743] 9pnet: Installing 9P2000 support
[ 3.435439] Key type dns_resolver registered
[ 3.440299] registered taskstats version 1
[ 3.443685] Loading compiled-in X.509 certificates
[ 3.461041] input: gpio-keys as /devices/platform/gpio-keys/input/input0
[ 3.473163] ALSA device list:
[ 3.474432] No soundcards found.
[ 3.485283] uart-pl011 9000000.pl011: no DMA platform data
[ 3.541376] Freeing unused kernel memory: 10752K
[ 3.545897] Run /linuxrc as init process
[ 3.548390] with arguments:
[ 3.550279] /linuxrc
[ 3.551073] nokaslr
[ 3.552216] with environment:
[ 3.554396] HOME=/
[ 3.555898] TERM=linux
[ 3.985835] 9pnet_virtio: no channels available for device kmod_mount
mount: mounting kmod_mount on /mnt failed: No such file or directory
/etc/init.d/rcS: line 8: can't create /proc/sys/kernel/hotplug: nonexistent directory
Please press Enter to activate this console.
[root@bryant ]#
[root@bryant ]#
VSCode + GDB
La fonction GDB est intégrée dans vscode, nous pouvons l'utiliser pour déboguer graphiquement le noyau linux
Nous ajoutons d'abord le fichier de configuration gdb de vscode (.vscode/launch.json) :
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "kernel debug",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/vmlinux",
"cwd": "${workspaceFolder}",
"MIMode": "gdb",
"miDebuggerPath":"/usr/bin/gdb-multiarch",
"miDebuggerServerAddress": "localhost:1234"
}
]
}
Voici quelques explications pour plusieurs paramètres clés :
program
: fichier de symboles pour le débogagemiDebuggerPath
: Le chemin de gdb. Il convient de noter ici que puisque nous sommes le noyau arm64, nous devons utiliser gdb-multiarch pour déboguermiDebuggerServerAddress
: Adresse homologue, qemu utilisera le port 1234 par défaut
Une fois la configuration terminée, vous pouvez directement démarrer GDB et vous connecter au noyau Linux
Dans vscode, vous pouvez définir des points d'arrêt pour le débogage en une seule étape
Déclaration de droit d'auteur : cet article est un article original écrit par le blogueur Zhihu "Jouer avec le noyau Linux". Il suit l'accord de droit d'auteur CC 4.0 BY-SA. Pour la réimpression, veuillez joindre le lien source original et cette déclaration.
Lien d'origine : https://zhuanlan.zhihu.com/p/639365490