Présentation
Se retrouver face à une erreur de segmentation brutale (Segmentation Fault) ou à une trace de pile brute ne laissant entrevoir que des adresses mémoire hexadécimales est le quotidien de tout développeur C/C++. Sans repère clair, identifier la cause racine s’avère fastidieux. C’est précisément là qu’intervient l’utilitaire addr2line : il fait le pont entre un pointeur d’instruction machine et la ligne exacte du fichier source responsable de l’incident. Ce tutoriel pratique détaille la mise en œuvre de cet outil indispensable de la suite GNU, depuis la génération des tables de débogage (-g) jusqu’au démangling C++ (-C) et au traçage d’appels imbriqués (-i).
1. Mise en place de l’environnement : installation de binutils et de gcc
Pour disposer du binaire addr2line ainsi que de la chaîne de compilation minimale requise par notre atelier pratique, nous déployons les paquets binutils et gcc via le gestionnaire APT :
sudo apt-get update -qq && sudo apt-get install -y binutils gcc

Validons la présence de l’exécutable tout en inspectant sa version courante :
addr2line -v
Cet utilitaire agit comme un traducteur inverse : en ingérant une adresse virtuelle issue d’un rapport de crash, il consulte les sections DWARF de l’exécutable pour restituer le nom du fichier source et la ligne correspondante.
⚙️ [Aperçu des indicateurs essentiels]
-e : Spécifie le chemin vers l’exécutable binaire cible contenant les symboles.-f : Affiche l’identifiant de la fonction concernée en plus de la localisation source.-C : Décode les noms de symboles décorés (demangling), indispensable pour la lisibilité du C++.-p : Produit une sortie compacte et élégante en assemblant fonction, fichier et ligne sur un seul enregistrement.-a : Préfixe le diagnostic par l’adresse mémoire soumise en entrée.-i : Remonte la chaîne d’expansion des fonctions intégrées directement dans le code (inlined).

2. Construction d’un cas d’école C instrumenté pour le débogage (-g)
Isolons notre espace d’expérimentation en préparant un répertoire dédié au sein de l’arborescence utilisateur :
mkdir -p ~/addr2line_lab && cd ~/addr2line_lab

Afin d’obtenir une signature mémoire concrète, nous rédigeons un programme C minimaliste structuré autour d’une routine nommée faulty_function :
echo -e '#include <stdio.h>\n\nvoid faulty_function() {\n printf("Crash simulation\\n");\n}\n\nint main() {\n faulty_function();\n return 0;\n}' > test_crash.c

L’incorporation des métadonnées DWARF est indispensable au bon fonctionnement d’addr2line. Nous invoquons gcc avec le drapeau -g tout en désactivant les optimisations via -O0 pour conserver une correspondance d’instructions stricte :
gcc -g -O0 test_crash.c -o test_crash

Interrogeons la table des symboles du binaire produit à l’aide de l’outil nm afin de relever l’adresse virtuelle attribuée à faulty_function :
nm test_crash | grep faulty_function
L’inspection met en évidence l’emplacement offset de notre sous-programme, localisé ici à l’adresse 0000000000001149.

3. Retrouver le fichier et le numéro de ligne à partir de l’adresse (-e, -f)
Pour faciliter les requêtes suivantes dans notre terminal, capturons l’adresse brute de la fonction au sein d’une variable shell :
FUNC_ADDR=$(nm test_crash | grep faulty_function | awk '{print $1}')

En fournissant simultanément le binaire cible via le paramètre -e et notre variable d’adresse, addr2line résout immédiatement l’origine du code : test_crash.c:3.
addr2line -e ./test_crash $FUNC_ADDR

Pour enrichir le retour d’information avec le nom de l’entité logicielle incriminée, adjoignons simplement le commutateur -f :
addr2line -f -e ./test_crash $FUNC_ADDR
La sortie affiche sans ambiguïté faulty_function au-dessus de sa position dans le fichier source.

4. Démangling C++ (-C) et analyse des fonctions inlinées (-i, -p)
Les binaires C++ introduisent une complexité supplémentaire : la surcharge et les espaces de noms mènent au name mangling, tandis que l’optimisation compresse fréquemment les fonctions courtes via inlining. Préparons le compilateur approprié :
sudo apt-get install -y g++

Concevons à présent un scénario d’empilement où une fonction déclarée inline (deep_inline) est invoquée en cascade :
echo -e '#include <iostream>\n\ninline void deep_inline() {\n std::cout << "Inside inline" << std::endl;\n}\n\nvoid process() {\n deep_inline();\n}\n\nint main() {\n process();\n return 0;\n}' > test_cpp.cpp

Lors du passage au compilateur, associons les symboles de débogage (-g) à un niveau d’optimisation modéré (-O2) afin d’inciter l’inlining réel des blocs d’instructions :
g++ -g -O2 test_cpp.cpp -o test_cpp

Extrayons le point d’ancrage mémoire du point d’entrée main pour notre variable d’analyse :
MAIN_ADDR=$(nm test_cpp | grep 'main' | head -n 1 | awk '{print $1}')

En combinant le démangling (-C), la résolution d’expansion interne (-i) et la mise en forme lisible sur une ligne (-p), interrogeons le binaire compilé :
addr2line -C -i -p -e ./test_cpp $MAIN_ADDR
[!NOTE] L’obtention d’une séquence
??:0traduit couramment l’absence de table DWARF valide pour l’adresse transmise, une réorganisation agressive du code sous l’effet du compilateur, ou un décalage d’adressage propre aux binaires PIE (Position Independent Executables) nécessitant un calcul d’offset relatif.

Maîtriser addr2line transforme l’analyse d’incidents sous Linux : une simple adresse extraite de vos journaux système ou d’un rapport GDB suffit désormais pour pointer du doigt l’instruction fautive. Intégrez ce réflexe à vos procédures de diagnostic pour diviser drastiquement le temps passé sur vos dysfonctionnements mémoire !