Как проверить наличие секции .debug и зачем она нужна разработчику

Секция .debug — это раздел в исполняемом файле или бинарнике, который хранит отладочную информацию: имена функций, номера строк исходного кода, типы переменных, имена файлов. Если вы столкнулись с крашем программы и видите в стеке вызовов только адреса вроде 0x00007ff6a3b11234, а не нормальные имена функций — скорее всего, секция .debug отсутствует или была удалена. Разберёмся, зачем она нужна, как проверить её наличие и что делать, когда её нет.

Что вообще такое секция .debug

Когда компилятор превращает исходный код в машинный код, он может добавить туда дополнительные данные — отладочную информацию. В формате ELF (Linux, большинство Unix-систем) эта информация живёт в именованных секциях, и .debug — это общее название для целой группы таких секций. В PE-файлах (Windows) отладочная информация хранится по-другому, но суть та же.

Конкретно в формате ELF вы встретите не одну секцию .debug, а несколько:

  • .debug_info — основная информация: имена функций, переменных, типы, номера строк.
  • .debug_line — таблица соответствия между машинными адресами и строками исходного кода.
  • .debug_abbrev — таблица сокращений, которая описывает структуру записей в .debug_info.
  • .debug_str — строковые данные (имена файлов, имена переменных).
  • .debug_ranges — диапазоны адресов для функций, разбитых на несколько частей.

Все эти секции вместе образуют то, что называется DWARF — формат отладочной информации. GDB, LLDB и другие отладчики читают именно эти данные, чтобы показывать вам осмысленные имена вместо голых адресов.

Зачем разработчику знать о .debug

Практических сценариев несколько, и они все реальные:

  1. Анализ крашей. Программа упала, вы открываете core dump в GDB, а стек вызовов состоит из адресов. Без .debug вы не поймёте, в какой функции произошла ошибка, не увидите значения локальных переменных.
  2. Профилирование и трассировка. Инструменты вроде perf, valgrind, bpftrace показывают адресовый профиль. Чтобы сопоставить его с исходным кодом, нужна отладочная информация.
  3. Проверка сборки. Вы собрали проект и хотите убедиться, что отладочная информация действительно попала в бинарник. Особенно актуально в CI/CD, где могут быть нюансы с флагами компилятора.
  4. Оптимизация размера. Отладочная информация может занимать значительный объём. Иногда её нужно оставить, иногда — удалить. Но для этого сначала нужно проверить, есть ли она.
  5. Reverse engineering. Если вы анализируете чужой бинарник, наличие .debug — подарок. Имена функций и перварные типы сильно упрощают понимание кода.

Как проверить наличие секции .debug

Способ 1: команда readelf (Linux)

Это самый прямой и информативный способ. Утилита readelf входит в состав binutils и есть практически в любом Linux-дистрибутиве.

Чтобы посмотреть все секции файла:

readelf -S /path/to/your/binary

В выводе найдите строки, начинающиеся с .debug:

[26] .debug_info         PROGBITS        0000000000000000 001a4c 0000000000000000 0     0     1
[27] .debug_abbrev       PROGBITS        0000000000000000 001b2a 0000000000000000 0     0     1
[28] .debug_line         PROGBITS        0000000000000000 001c89 0000000000000000 0     0     1
[29] .debug_str          PROGBITS        0000000000000000 001d10 0000000000000000 0     0     1

Если таких строк нет — отладочная информация отсутствует. Если есть, но размер (Size) равен нулю — секция пустая, что тоже говорит о проблеме.

Способ 2: команда objdump

Более короткий вариант проверки — искать секции по имени:

objdump -h /path/to/your/binary | grep debug

Если вывод пуст — секций .debug нет. Если есть — вы увидите имена, размеры и виртуальные адреса каждой секции.

Способ 3: команда file

Команда file не показывает конкретные секции, но может подсказать, есть ли отладочная информация в файле:

file /path/to/your/binary

Ищите строку with debug_info, not stripped — это значит, что отладочная информация присутствует. Если написано просто not stripped — символы есть, но DWARF-секций может не быть. Если stripped — всё вырезано.

Способ 4: GDB

Загрузите файл в отладчик и посмотрите, что он скажет:

gdb /path/to/your/binary

GDB при загрузке выведет что-то вроде:

Reading symbols from /path/to/your/binary...
(No debugging symbols found in /path/to/your/binary)

Или, если символы есть:

Reading symbols from /path/to/your/binary...

Без предупреждения — значит, отладочная информация загружена успешно.

Способ 5: для Windows (PE-файлы)

В Windows отладочная информация хранится в секции .debug PE-файла или в отдельном PDB-файле. Для проверки можно использовать dumpbin:

dumpbin /headers /path/to/your/binary.exe | findstr debug

Или PowerShell:

[System.IO.File]::ReadAllBytes("binary.exe")[...]  # анализ PE-заголовков вручную

Проще всего использовать утилиту dbghelp или посмотреть в CFF Explorer — это GUI-инструмент, который наглядно показывает все секции PE-файла.

Сравнение способов проверки

Способ Что показывает Удобство Где работает
readelf -S Полный список секций с размерами Высокое — максимум информации Linux, любые ELF-системы
objdump -h | grep Только секции .debug (отфильтрованно) Среднее — быстрый чек Linux, кросс-компиляция
file Общий признак наличия debug info Низкое — только факт, без деталей Linux, macOS (для Mach-O)
gdb Загрузка символов и диагностика Среднее — если уже работаете с GDB Linux, частично Windows
dumpbin Секции PE, включая .debug Среднее — нужно знать формат Windows

Что делать, если секции .debug нет

Здесь всё зависит от вашей ситуации.

Если вы собираете проект сами

Проверьте флаги компилятора. Для GCC и Clang нужен флаг -g:

gcc -g -O0 main.c -o myprogram

Важные нюансы:

  • -g без указания уровня — генерирует DWARF последней поддерживаемой версии.
  • -g1 — минимальная информация: имена функций и номера строк, но без локальных переменных. Полезно, когда нужно экономить место.
  • -g3 — максимум информации, включая макросы препроцессора.
  • Флаги оптимизации (-O2, -O3) не удаляют отладочную информацию, но могут сделать отладку неудобной — переменные оптимизируются, строки переставляются.
  • Если в вашем Makefile или CMakeLists.txt есть strip на этапе установки — он удаляет все символы и секции .debug. Проверьте этапы сборки.

Если бинарник собран в конвейере CI/CD

Частая ситуация: разработчик собирает с -g, а перед публикой запускает strip --strip-debug или strip --strip-all. Первый удаляет только DWARF-секции, второй — всё, включая таблицу символов.

Если вам нужны символы для анализа крашей, не удаляйте их. Вместо этого сохраняйте debug info отдельно:

objcopy --only-keep-debug myprogram myprogram.debug
strip --strip-debug myprogram
objcopy --add-gnu-debuglink=myprogram.debug myprogram

После этого в GDB можно загрузить myprogram.debug отдельно, и отладчик подхватит его автоматически.

Если вы аналилируете чужой бинарник

Если отладочной информации нет и взять её неоткуда — остаётся работать с тем, что есть. Инструменты вроде Ghidra, IDA Pro, Binary Ninja могут восстановить часть информации: имена импортируемых функций, строки, структуры по шаблонам использования. Но это уже совсем другой уровень работы.

Частые ошибки при работе с .debug

Путаница между символами и отладочной информацией. Таблица символов (.symtab) и отладочные секции (.debug_info) — разные вещи. strip --strip-symbols удаляет таблицу символов, но не DWARF. strip --strip-debug удаляет DWARF, но оставляет символы. Если вы видите имена функций в nm, но GDB не показывает номера строк — значит, удалена именно .debug_info.

Сборка с -g, но линковка без неё. Если вы компилируете объектные файлы с -g, а линковщик запускается с флагом -S или -s, отладочная информация потеряется на этапе линковки. Проверяйте все этапы сборки.

Использование -g в продакшн-сборке без разделения. Не отправляйте бинарники с полной отладочной информацией в продакшн. Размер файла может быть в 5–10 раз больше. Используйте objcopy для разделения: рабочий бинарник — без debug, файл с отладочной информацией — отдельно, в защищённом хранилище.

Несовместимость версий DWARF. Если компилятор генерирует DWARF 5, а отладчик поддерживает только DWARF 4, вы получите сообщение об ошибке при загрузке. Это редкость в свежих дистрибутивах, но встречается в корпоративных средах со старым инструментарием.

Как лучше организовать работу с отладочной информацией

Здесь зависит от масба проекта, но есть общие рекомендации:

  • Для разработки и тестирования: собирайте с -g -O0. Никакой оптимизации, полная отладочная информация. Это ваша рабочая среда.
  • Для бета-сборок и стейджинга: -g -O2. Отладочная информация есть, но код уже с оптимизацией — ближе к боевому поведению.
  • Для продакшна: собирайте с -g, затем сохраняйте debug info через objcopy, а в продакшн отправляйте уже strip --strip-debug версию.
  • Хранение debug info: храните файлы .debug или debug packages в артефактории (Nexus, Artifactory, S3). При получении core dump вы сможете подтянуть нужную версию и получить читаемый стек.

Итог

Проверить наличие секции .debug — дело одной команды: readelf -S или objdump -h | grep debug. Если секций нет — пересоберите с флагом -g и проверьте, что линковщик и strip не удалили отладочную информацию. Если есть — решите, что с ней делать: оставить в бинарнике, вынести в отдельный файл или удалить для уменьшения размера.

Главное правило: отладочная информация — это не мусор, а ценный ресурс для диагностики проблем. Не удаляйте её бездумно и не тащите в продакшн целиком. Разделяйте — и будете получать читаемые стектрейсы вместо наборов адресов.

Оцените статью
PEFile — Безопасность и технологии простым языком