ИТ Блог. Администрирование серверов на основе Linux (Ubuntu, Debian, CentOS, openSUSE)
Четверг, 27 февраля, 2025

Введение в GNU Binutils: Руководство для начинающих

Введение в GNU Binutils: Руководство для начинающих

Если вы когда-либо работали с Linux или компилировали программное обеспечение из исходного кода, то, скорее всего, использовали GNU Binutils, даже не подозревая об этом. Набор GNU Binutils необходим для сборки, компоновки и управления двоичными файлами. Независимо от того, являетесь ли вы новичком, пытающимся понять, как компилируется программное обеспечение, или опытным разработчиком, отлаживающим исполняемый файл, Binutils поможет вам.

В этой статье мы расскажем вам, что такое GNU Binutils, о его преимуществах, основных инструментах и о том, как использовать GNU Binutils для отладки исполняемых файлов в Linux.

 

Что такое GNU Binutils?

GNU Binutils (сокращение от «бинарные утилиты») — это набор инструментов командной строки для работы с объектными файлами, исполняемыми файлами и двоичными данными. Эти инструменты помогают выполнять такие задачи, как сборка кода, связывание библиотек, дизассемблирование исполняемых файлов и анализ скомпилированных программ.

Пакет Binutils включает в себя некоторые из наиболее важных инструментов в наборе разработчика, такие как:

 

Преимущества

Если вы увлекаетесь программированием (особенно на таких языках, как C или C++) и системным администрированием, то рано или поздно столкнётесь с GNU Binutils.

 

Короче говоря, понимание Binutils поможет вам стать более эффективным программистом и системным администратором.

 

Как использовать инструменты GNU Binutils

Давайте рассмотрим наиболее важные инструменты GNU Binutils и то, как они могут вам помочь.

1. as : Ассемблер GNU

Инструмент as преобразует ассемблерный код в машинный.

Пример:

as hello.s -o hello.o

 

Это компилирует исходный файл сборки (hello.s) в объектный файл (hello.o).

 

2. ld : Компоновщик GNU

Инструмент ld связывает объектные файлы для создания исполняемого файла.

 

Пример:

ld -o hello hello.o

 

Это связывает hello.o с исполняемым файлом с именем hello.

 

3. objdump : Проверьте исполняемые файлы

Команда objdump отображает подробную информацию об объектных файлах и исполняемых файлах.

 

Пример:

ld -o hello hello.o

 

При этом команда ls разбирается, показывая инструкции по ее сборке.

 

4. nm : Список символов в двоичном файле

Команда nm выводит список символов (функций и переменных) внутри объектного файла или исполняемого файла.

 

Пример:

objdump -d /bin/ls | less

 

Здесь показаны первые несколько символов в объектном файле hello.o.

 

5. readelf : Прочитайте заголовки ELF

Инструмент readelf предоставляет подробную информацию о файлах ELF (исполняемый и компонуемый формат).

 

Пример:

readelf -h /bin/ls

 

При этом отображается заголовок ELF команды ls.

 

6. strings : Извлечение читаемого текста из двоичных файлов

Инструмент strings находит и извлекает читаемый текст из двоичных файлов.

 

Пример:

strings /bin/ls | grep "Usage"

 

При этом выполняется поиск сообщений об использовании внутри двоичного файла ls.

 

7. strip : Уменьшите размер двоичного файла, удалив символы

Команда strip удаляет ненужные символы из исполняемых файлов, уменьшая их размер.

Пример:

strip myprogram

 

Это сводит к минимуму размер myprogram за счет удаления символов отладки.

 

8. size: Отображение размеров секций

Инструментsize показывает использование памяти различными разделами объектного файла.

 

Пример:

size /bin/ls

 

Здесь отображаются размеры сегментов текста, данных и bss (неинициализированных данных) для ls.

 

9. objcopy : Копирование и изменение объектных файлов

Инструментobjcopy позволяет вам манипулировать объектными файлами.

 

Пример:

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

 

При этом символы отладки извлекаются в отдельный файл.

 

10. addr2line : Сопоставление адресов с исходным кодом

Команда addr2line помогает сопоставить адрес памяти с исходной строкой исходного кода.

 

Пример:

addr2line -e /bin/ls 0x4005a0

 

Это показывает, какая строка в исходном коде ls соответствует адресу памяти 0x4005a0.

 

11. ar : Архиватор

Инструмент ar используется для создания архивов объектных файлов и управления ими. Эти архивы, часто называемые статическими библиотеками, можно подключать к программам для повторного использования кода.

 

Пример:

Вы можете создать библиотеку с помощью ar rcs libmylib.a file1.o file2.o, а затем связать ее со своей программой.

 

Практический пример: Отладка простой программы на языке Си

Как мы уже упоминали, GNU Binutils — это набор двоичных инструментов, необходимых для работы с исполняемыми, объектными файлами и библиотеками в Linux. Эти инструменты полезны для отладки, дизассемблирования и анализа двоичных файлов.

В следующем примере мы сосредоточимся на использовании objdumpnm и readelf для целей отладки.

 

Обзор инструментов:

  1. objdump: Разбирает двоичные файлы, отображает разделы объектных файлов и извлекает информацию из исполняемых файлов.
  2. nm: Перечисляет символы (функции, переменные и т.д.) Из объектных файлов.
  3. readelf: Отображает подробную информацию о файлах ELF (исполняемый и компонуемый формат).

 

Создайте пример программы на языке Си

Давайте начнём с написания простой программы на C, её компиляции, а затем анализа с помощью Binutils.

 

Шаг 1: Напишите простую программу на C

Создайте файл с именем example.c со следующим содержимым:

#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;
}

 

Шаг 2: Скомпилируйте программу

Скомпилируйте программу с включёнными символами отладки. Это упростит анализ:

gcc -g -o example example.c

 

Флаг -g указывает компилятору включить отладочную информацию в исполняемый файл.

 

Шаг 3: Проанализируйте исполняемый файл с помощью Binutils

1. Использование objdump для дизассемблирования двоичного файла

Чтобы просмотреть дизассемблированный код исполняемого файла, запустите:

objdump -d example

 

Это отобразит код сборки исполняемого файла. Найдите в выводе функции main и add. Например:

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)
[...]

 

В этом выводе показаны инструкции по сборке для функций add и main.

 

2. Использование nm для перечисления символов

Чтобы составить список символов (функций и переменных) в исполняемом файле, выполните:

nm example

 

Вы увидите результат, подобный этому:

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

 

Здесь T указывает на то, что символ находится в текстовом (кодовом) разделе, а U указывает на неопределённый символ (например, printf, который связан с библиотекой C).

 

3. Использование readelf для проверки файла ELF

Чтобы просмотреть подробную информацию о файле ELF, используйте readelf. Например, чтобы увидеть заголовки разделов, выполните:

readelf -S example

 

Это отобразит информацию о разделах исполняемого файла, таких как .text (код), .data (инициализированные данные) и .debug (информация для отладки).

Чтобы просмотреть таблицу символов, выполните:

readelf -s example

 

При этом будут показаны все символы, включая их адреса и типы.

 

Отладка с помощью objdump и gdb

Если вы хотите отладить программу, вы можете использовать gdb (GNU Debugger) в сочетании с objdump.

Примечание: если gdb недоступен, вы можете установить его с помощью менеджера пакетов по умолчанию, например:

sudo apt install gdb

 

1. Начните gdb с исполняемого файла:

gdb ./example

 

Теперь вы будете находиться в оболочке 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. Установите точку останова в функции main:

break main

 

3. Запустите программу:

run

 

4. Используйте disassemble in gdb для просмотра дизассемблированного кода:

disassemble main

 

Здесь будет показан код сборки для функции main, аналогичный тому, что был показан ранее для objdump

Комбинируя эти инструменты, вы можете эффективно анализировать и отлаживать исполняемые файлы в Linux.

 

Заключение

GNU Binutils — это обязательный к изучению набор инструментов для всех, кто работает с компилированным программным обеспечением. Даже если вы новичок в Linux, изучение основ этих инструментов поможет вам понять, что происходит за кулисами при запуске программного обеспечения.

Exit mobile version