Quelques trucs avec gdb

J’avais jusqu’à cette semaine des compétences vraiment basiques en gdb. Je savais me connecter à ma cible, charger mon binaire, l’exécuter, et cela s’arrêtait presque là. Je devais savoir mettre un breakpoint sur une fonction. Cette semaine, j’ai eu besoin d’aller bien plus loin, j’ai appris plein de choses et j’écris donc cet article pour garder une trace de ces nouveaux savoirs.

gdb logo

RTFM

La documentation de gdb est disponible ici : Debugging with GDB.

Un GDB Quick Reference existe aussi sur Internet, il est pratique pour parcourir rapidement une grande liste de commandes. On peut ainsi découvrir des commandes intéressantes et retourner dans la documentation complète pour approfondir. Elle est également bien pratique quand on débogue pour tout avoir sous le coude. La version la plus récente semble être ici : GDB QUICK REFERENCE GDB Version 5.

Gestion des sources

Certains binaires gardent le chemin absolu des fichiers sources. Cela permet lorsqu’on rencontre un breakpoint de connaitre la ligne en C sur laquelle on est arrêté. Si ce n’est pas le cas ou si ce chemin est devenu invalide (par exemple, lorsqu’on débogue sur une autre machine), il faut ajouter les dossiers de sources à l’aide de la commande directory dirname. Sous Windows (au moins), il faut bien mettre des \\ dans les chemins. La commande directory sans paramètre permet de restaurer la valeur par défaut du source path.

Pour plus de détails : 9.5 Specifying Source Directories.

Utilisation des breakpoints

gdb propose plusieurs commandes classiques pour placer, activer, désactiver et supprimer des breakpoints. Ces breakpoints peuvent créer à partir d’une adresse, du nom d’une fonction, d’un numéro de ligne dans un fichier source, etc. Je vous renvoie à cette section du manuel pour une liste complète des commandes : 5.1 Breakpoints, Watchpoints, and Catchpoints.

Il existe une commande très pratique pour connaitre l’ensemble des breakpoints existants : info break. On obtient ainsi la liste des breakpoints et les informations les concernant. Chaque breakpoint étant numéroté, cette liste est pratique pour trouver les valeurs à passer en paramètre des commandes disable, enable ou encore delete.

Les breakpoints peuvent être conditionnels, pour ne bloquer l’exécution que sous certaines conditions: 5.1.6 Break Conditions.

Si tout cela est très classique, j’ai trouvé une fonctionnalité que vraiment puissante et un poil plus originale : la possibilité d’exécuter automatiquement des commandes lorsqu’un breakpoint est rencontré et que donc l’exécution du programme est interrompue. Voir : 5.1.7 Breakpoint Command Lists.

Correspondance entre une adresse et une fonction

Il m’est souvent arrivé lorsque d’obtenir une adresse à laquelle un crash du programme s’est produit, par exemple 0x8001b270. Comment savoir quelle est la fonction qui englobe cette adresse ? Pendant longtemps, je me servais du fichier map en essayant de trouver l’adresse immédiatement inférieure correspondant à une fonction. Jusqu’à cette semaine, j’utilisais le résultat d’objdump -d. Maintenant, j’utilise gdb et la commande output /a 0x2001b270. Le retour de cette commande est le nom de la fonction englobante et l’offset à partir de son adresse de début. Très, très pratique. En plus, vous n’avez pas besoin de déboguer vraiment. Il suffit de lancer gdb avec le fichier exécutable en paramètre pour pouvoir obtenir ces informations. Si l’un des registres du processeur contient une adresse (un pointeur de fonction par exemple), on peut ainsi directement retrouver la fonction correspondante. Par exemple sur ARM Cortex-M, avec le registre r1: output /a $r1.

Afficher les registres

On peut afficher l’ensemble des registres du processeur d’un seul coup avec info registers, ou alors au compte goutte, par exemple info registers r1.

Fichier de log

Il est possible de rediriger ce qu’il s’affiche dans la console vers un fichier. Il suffit de faire set logging on. On peut aussi choisir son fichier si le nom par défaut ne nous convient pas. Voir : 2.4 Logging Output.

 Fichiers de commandes

Lorsqu’on lance gdb en ligne de commande, on peut lui passer en paramètre un fichier de commandes à exécuter, via l’option --command=filename. Il exécutera alors l’ensemble des commandes contenues dans ce fichier. Pratique pour automatiser une phase d’initialisation (par exemple, se connecter à un gdb server, flasher la carte avec le binaire et enfin lancer l’exécution) ou même une exécution complète. Il est possible de faire charger un tel fichier de commande une fois gdb lancé avec la commande source filename. Cela aura le même effet.

On peut définir ses propres commandes gdb dans un tel fichier pour ensuite s’en servir soit dans la suite de ce fichier, soit de manière interactive (source devrait vous rappeler un mot-clé de bash et autre shells).

Le chapitre correspondant dans la documentation : 23.1.3 Command Files.

Un exemple…

Pour finir, voici un petit exemple d’utilisation où vous verrez comment on peut tracer les appels à une fonction et arrêter l’exécution dans le cas où on détecte quelque chose d’anormal (tout du moins de non souhaité). Il s’agirait presque plus d’une exécution conditionnelle automatisée que d’une session de débogue ^^ Cela peut vous laissez imaginer les possibilités offertes.

La première étape est d’écrire un petit programme dans fichier test.c :

#include <stdio.h>

int mult(int n, int m)
{
	int res = n * m;
	return res;
}

int main()
{
	int a = mult(1, 2);
	int b = mult(2, 2);
	return mult(a, b);
}

On compile ce programme :

$ gcc -std=c99 -Wall -g -O0 test.c

On écrit un fichier de command pour gdb :

$ cat command.gdb
# Activate logging
set logging on

# Do not prompt for confirmation
set confirm off

# Break when mult() is called, print parameters, and continue
break mult 
command
silent
printf "mult(%d, %d) has been called\n", n, m
c
end

# Break when the result of mult() is greater than 4
break test.c:6 if res > 4
command
printf "Result is greater than 4 (res = %d)\n", res
print "Backtrace:"
bt
print "Abort debugging session!"
quit
end

# Start program
run
quit

On lance gdb en lui fournissant l’exécutable et le fichier de commandes :

gdb ./a.out --command=command.gdb

Je vous passe la sortie console. Elle est quasiment équivalent au contenu du fichier de log :

$ cat gdb.txt
Breakpoint 1 at 0x80483ba: file test.c, line 5.
Breakpoint 2 at 0x80483c4: file test.c, line 6.
mult(1, 2) has been called
mult(2, 2) has been called
mult(2, 4) has been called

Breakpoint 2, mult (n=2, m=4) at test.c:6
6		return res;
Result is greater than 4 (res = 8)
$1 = "Backtrace:"
#0  mult (n=2, m=4) at test.c:6
#1  0x0804840f in main () at test.c:14
$2 = "Abort debugging session!"

Publicités

Une Réponse

  1. On peut utiliser aussi un fichier .gdbinit dans le repertoire home pour que les commandes soient lancees automatiquement au demarrage de GDB.

    Il existe des configurations toutes faites, par exemple celle ci est interessante: https://github.com/gdbinit/Gdbinit . Je ne l’utilise pas telle quelle mais il y a dedans plein de bonnes idees, comme par exemple l’affichage automatique du desassemblage et des valeurs de registres lors d’une execution pas a pas (attention, quand on travaille sur une cible connectee par JTAG et pas un programme local, ça peut etre lent).

    A decouvrir aussi, le mode « TUI » (Text User Interface) de GDB. On la lance avec gdb -tui. La fenetre est separee en deux, une partie avec la console habituelle, et une avec plein d’informations sur le programme en cours de debug. Ça evite de tout devoir retenir dans sa tete et d’avoir un peu de contexte a l’ecran.

    Penser a activer le demangling quand on travaille sur du C++ ou tout autre langage que GDB peut traiter pour avoir les noms de classes et de methodes en clair (non, ça marche pas pour IceTea!)

    J'aime

    29 juillet 2014 à 6:47

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s