Recherche de site Web

Introduction à GNU Binutils : Guide du débutant


Si vous avez déjà travaillé avec Linux ou compilé un logiciel à partir du code source, vous avez probablement utilisé GNU Binutils sans même vous en rendre compte. La suite GNU Binutils est essentielle pour l'assemblage, la liaison et la gestion des fichiers binaires. Que vous soyez un débutant essayant de comprendre comment un logiciel se compile ou un développeur expérimenté déboguant un exécutable, Binutils est là pour vous.

Dans ce guide, je vais vous expliquer ce qu'est GNU Binutils, ses avantages, les outils clés, et comment utiliser GNU Binutils pour déboguer des exécutables sous Linux.

Qu'est-ce que GNU Binutils ?

GNU Binutils (abréviation de "Binary Utilities") est un ensemble d'outils en ligne de commande pour la gestion des fichiers objets, des exécutables et des données binaires. Ces outils aident à des tâches telles que l'assemblage de code, la liaison de bibliothèques, le désassemblage d'exécutables et l'analyse de programmes compilés.

Le paquet Binutils inclut certains des outils les plus importants dans la boîte à outils d’un développeur, tels que :

  • as (l'assembleur)
  • ld (l'éditeur de liens)
  • objdump (pour inspecter les fichiers binaires)
  • nm (pour lister les symboles dans les fichiers objets)
  • readelf (pour lire des informations sur ELF)
  • chaînes (pour extraire du texte lisible à partir de fichiers binaires)
  • strip (pour supprimer les données inutiles des binaires)
  • taille (pour afficher les tailles des sections)
  • objcopy (pour copier et modifier des fichiers objets)
  • addr2line (pour mapper les adresses au code source)
  • ar (pour créer et gérer des archives)

Avantages

Si vous vous intéressez à la programmation (en particulier dans des langages comme C ou C++) et à l'administration système, GNU Binutils est quelque chose que vous rencontrerez probablement tôt ou tard.

  • Si vous êtes développeur, Binutils est un incontournable pour compiler et déboguer des logiciels.
  • Si vous êtes un administrateur système, ces outils aident à analyser les exécutables pour la sécurité et la compatibilité.
  • Si vous vous intéressez à l'ingénierie inverse, Binutils vous permet d'inspecter et de modifier des binaires.
  • Pour les amateurs, Binutils aide à apprendre comment les exécutables fonctionnent en coulisses.
  • De nombreux environnements de développement et systèmes d'exploitation populaires dépendent de Binutils en coulisses.
  • Binutils sont open source. Vous pouvez utiliser, modifier et distribuer ces outils sans vous soucier des frais de licence.

En résumé, comprendre Binutils peut faire de vous un programmeur et un administrateur système plus efficace et efficient.

Comment utiliser les outils GNU Binutils

Décomposons les outils les plus importants de GNU Binutils et voyons comment ils peuvent vous aider.

1. as : L'assembleur GNU

L'outil as convertit le code assembleur en code machine.

Exemple :

as hello.s -o hello.o

Cela compile un fichier source assembleur (hello.s) en un fichier objet (hello.o).

2. ld : Le lieur GNU

L'outil ld lie les fichiers objets pour créer un exécutable.

Exemple :

ld -o hello hello.o

Cela lie hello.o dans un exécutable appelé hello.

3. objdump : Inspecter les fichiers exécutables

La commande objdump affiche des informations détaillées sur les fichiers objets et les fichiers exécutables.

Exemple :

objdump -d /bin/ls | less

Cela désassemble la commande ls, montrant ses instructions en assembleur.

4. nm : Lister les symboles dans un binaire

La commande nm liste les symboles (fonctions et variables) à l'intérieur d'un fichier objet ou exécutable.

Exemple :

nm hello.o | head

Ceci montre les premiers symboles dans le fichier objet hello.o.

5. readelf : Lire les en-têtes ELF

L'outil readelf fournit des informations détaillées sur les fichiers ELF (Executable and Linkable Format).

Exemple :

readelf -h /bin/ls

Cela affiche l'en-tête ELF de la commande ls.

Lecture connexe :

  • Introduction à ELF sous Linux : Un guide simple des fichiers exécutables
  • Comment analyser des binaires ELF avec Binsider sous Linux

6. strings : Extraire du texte lisible à partir de fichiers binaires

L'outil strings trouve et extrait le texte lisible des fichiers binaires.

Exemple :

strings /bin/ls | grep "Usage"

Cela recherche les messages d'utilisation à l'intérieur du binaire ls.

7. strip : Réduire la taille binaire en supprimant les symboles

La commande strip supprime les symboles inutiles des exécutables, réduisant ainsi leur taille.

Exemple :

strip myprogram

Cela réduit la taille de myprogram en supprimant les symboles de débogage.

8. size : Afficher la taille des sections

L'outil size montre l'utilisation de la mémoire des différentes sections dans un fichier objet.

Exemple :

size /bin/ls

Ceci affiche les tailles des segments texte, données et bss (données non initialisées) de ls.

9. objcopy : Copier et modifier des fichiers objet

L'outil objcopy vous permet de manipuler des fichiers objets.

Exemple :

objcopy --only-keep-debug myprogram myprogram.debug

Cela extrait les symboles de débogage dans un fichier séparé.

10. addr2line : Mapper les adresses au code source

La commande addr2line aide à mapper une adresse mémoire à la ligne de code source originale.

Exemple :

addr2line -e /bin/ls 0x4005a0

Cela vous indique quelle ligne dans le code source de ls correspond à l'adresse mémoire 0x4005a0.

11. ar : L'Archiver

L'outil ar est utilisé pour créer et gérer des archives de fichiers objets. Ces archives, souvent appelées bibliothèques statiques, peuvent être liées à vos programmes pour réutiliser du code.

Exemple:

Vous pouvez créer une bibliothèque avec ar rcs libmylib.a file1.o file2.o puis la lier à votre programme.

Exemple pratique : Débogage d'un programme C simple

Comme nous l'avons déjà mentionné, GNU Binutils est une collection d'outils binaires essentiels pour travailler avec des fichiers exécutables, des fichiers objets et des bibliothèques sous Linux. Ces outils sont utiles pour le débogage, le désassemblage et l'analyse des binaires.

Dans l'exemple suivant, nous nous concentrerons sur l'utilisation de objdump, nm et readelf à des fins de débogage.

Aperçu des outils :

  1. objdump : Désassemble les fichiers binaires, affiche les sections des fichiers objets et extrait des informations des exécutables.
  2. nm : Liste les symboles (fonctions, variables, etc.) à partir des fichiers objets.
  3. readelf : Affiche des informations détaillées sur les fichiers ELF (Executable and Linkable Format).

Créer un exemple de programme C

Commençons par écrire un programme C simple, le compiler, puis utiliser Binutils pour l'analyser.

Étape 1 : Écrire un programme C simple

Créez un fichier nommé example.c avec le contenu suivant :

#include <stdio.h>

int add(int a, int b) {
    return a + b;
}

int main() {
    int result = add(3, 4);
    printf("Result: %d\n", result);
    return 0;
}

Étape 2 : Compiler le programme

Compilez le programme avec les symboles de débogage inclus. Cela facilitera l'analyse :

gcc -g -o example example.c

L'option -g indique au compilateur d'inclure des informations de débogage dans l'exécutable.

Étape 3 : Analyser l'exécutable avec Binutils

1. Utilisation de objdump pour désassembler le binaire

Pour afficher le code désassemblé de l'exécutable, exécutez :

objdump -d example

Cela affichera le code assembleur de l'exécutable. Recherchez les fonctions main et add dans la sortie. Par exemple :

example:     file format elf64-x86-64


Disassembly of section .init:

0000000000001000 <_init>:
    1000:    48 83 ec 08             sub    $0x8,%rsp
    1004:    48 8b 05 c5 2f 00 00    mov    0x2fc5(%rip),%rax        # 3fd0 <__gmon_start__@Base>
    100b:    48 85 c0                test   %rax,%rax
    100e:    74 02                   je     1012 <_init+0x12>
    1010:    ff d0                   call   *%rax
    1012:    48 83 c4 08             add    $0x8,%rsp
    1016:    c3                      ret

Disassembly of section .plt:

0000000000001020 <printf@plt-0x10>:
    1020:    ff 35 ca 2f 00 00       push   0x2fca(%rip)        # 3ff0 <_GLOBAL_OFFSET_TABLE_+0x8>
    1026:    ff 25 cc 2f 00 00       jmp    *0x2fcc(%rip)        # 3ff8 <_GLOBAL_OFFSET_TABLE_+0x10>
    102c:    0f 1f 40 00             nopl   0x0(%rax)

0000000000001030 <printf@plt>:
    1030:    ff 25 ca 2f 00 00       jmp    *0x2fca(%rip)        # 4000 <printf@GLIBC_2.2.5>
    1036:    68 00 00 00 00          push   $0x0
    103b:    e9 e0 ff ff ff          jmp    1020 <_init+0x20>

Disassembly of section .plt.got:

0000000000001040 <__cxa_finalize@plt>:
    1040:    ff 25 9a 2f 00 00       jmp    *0x2f9a(%rip)        # 3fe0 <__cxa_finalize@GLIBC_2.2.5>
    1046:    66 90                   xchg   %ax,%ax

Disassembly of section .text:

0000000000001050 <_start>:
    1050:    31 ed                   xor    %ebp,%ebp
    1052:    49 89 d1                mov    %rdx,%r9
    1055:    5e                      pop    %rsi
    1056:    48 89 e2                mov    %rsp,%rdx
    1059:    48 83 e4 f0             and    $0xfffffffffffffff0,%rsp
    105d:    50                      push   %rax
    105e:    54                      push   %rsp
    105f:    45 31 c0                xor    %r8d,%r8d
    1062:    31 c9                   xor    %ecx,%ecx
    1064:    48 8d 3d e2 00 00 00    lea    0xe2(%rip),%rdi        # 114d <main>
    106b:    ff 15 4f 2f 00 00       call   *0x2f4f(%rip)        # 3fc0 <__libc_start_main@GLIBC_2.34>
    1071:    f4                      hlt
    1072:    66 2e 0f 1f 84 00 00    cs nopw 0x0(%rax,%rax,1)
    1079:    00 00 00 
    107c:    0f 1f 40 00             nopl   0x0(%rax)
[...]

Cette sortie montre les instructions d'assemblage pour les fonctions add et main.

2. Utilisation de nm pour lister les symboles

Pour lister les symboles (fonctions et variables) dans l'exécutable, exécutez :

nm example

Vous verrez une sortie comme celle-ci :

0000000000401136 T add
000000000040114a T main
                 U printf@@GLIBC_2.2.5

Ici, T indique que le symbole se trouve dans la section de texte (code), et U indique un symbole non défini (par exemple, printf, qui est lié depuis la bibliothèque C).

3. Utilisation de readelf pour inspecter le fichier ELF

Pour afficher des informations détaillées sur le fichier ELF, utilisez readelf. Par exemple, pour voir les en-têtes de section, exécutez :

readelf -S example

Cela affichera des informations sur les sections de l'exécutable, telles que .text (code), .data (données initialisées) et .debug (informations de débogage).

Pour afficher la table des symboles, exécutez :

readelf -s example

Ceci affichera tous les symboles, y compris leurs adresses et types.

Débogage avec objdump et gdb

Si vous voulez déboguer le programme, vous pouvez utiliser gdb (GNU Debugger) en combinaison avec objdump.

Remarque : si gdb n'est pas disponible, vous pouvez l'installer en utilisant le gestionnaire de paquets par défaut, par exemple : sudo apt install gdb.

1. Démarrez gdb avec l'exécutable :

gdb ./example

Vous serez maintenant dans le shell gdb :

GNU gdb (Debian 13.1-3) 13.1
Copyright (C) 2023 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-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./example...
(gdb) 

2. Placez un point d'arrêt à la fonction main :

break main

3. Exécutez le programme :

run

4. Utilisez disassemble dans gdb pour voir le code désassemblé :

disassemble main

Cela affichera le code assembleur pour la fonction main, similaire à ce que objdump a affiché plus tôt.

En combinant ces outils, vous pouvez analyser et déboguer efficacement des exécutables sous Linux.

Conclusion

GNU Binutils est un ensemble d'outils indispensable pour quiconque travaille avec des logiciels compilés. Même si vous êtes nouveau sur Linux, apprendre les bases de ces outils peut vous aider à comprendre ce qui se passe en coulisses lorsque le logiciel s'exécute.