Работа с gdb linux

Как отлаживать программы на языке C в Linux с помощью отладчика GDB

Оригинал: How to debug C programs in Linux using gdb
Автор: Himanshu Arora
Дата публикации: 16 января 2017 г.
Перевод: А.Панин
Дата перевода: 7 марта 2017 г.

Вне зависимости от вашего опыта программирования, все разработанное вами программное обеспечение просто не может не содержать ошибок. По этой причине поиск и устранение ошибок являются одними из наиболее важных задач, выполняемых в рамках цикла разработки программного обеспечения. Хотя и существует несколько способов выявления ошибок в коде (тестирование, самостоятельный аудит кода и другие), для этой цели создан отдельный тип программного обеспечения под названием «отладчики», позволяющего вам найти проблемный фрагмент кода и максимально быстро исправить его.

Если вы разрабатываете программное обеспечение на языках C/C++ или пользуетесь такими более редкими языками программирования, как Fortran и Modula-2, вам будет полезно знать о существовании отличного отладчика под названием GDB , который позволяет достаточно просто отлаживать ваш код, помогая устранять ошибки и различные проблемные конструкции. В рамках данной статьи мы постараемся обсудить основные приемы работы с GDB, включая некоторые полезные функции/параметры данного инструмента.

Но перед тем, как двинуться дальше, стоит упомянуть о том, что все инструкции и примеры, приведенные в данной статье, были протестированы в системе Ubuntu 14.04 LTS. В статье был использован пример кода на языке C; в качестве командной оболочки использовалась командная оболочка Bash (версии 4.3.11); также стоит сказать о том, что для отладки тестовой программы использовался отладчик GDB версии 7.7.1.

Основные аспекты использования GDB

По сути, GDB позволяет вам заглянуть внутрь программы в процессе ее исполнения, способствуя тем самым идентификации и локализации проблемы. Мы обсудим методику использования отладчика GDB на основе примера в следующем разделе, но перед этим стоит рассмотреть несколько основных аспектов использования отладчика, которые окажутся полезными в будущем.

Во-первых, для успешного использования таких отладчиков, как GDB, вам придется компилировать вашу программу таким образом, чтобы компилятор генерировал отладочную информацию, необходимую отладчикам. Например, в случае компилятора GCC, который будет впоследствии использоваться для компиляции примера программы на языке C, вам придется дополнительно передать параметр -g на этапе компиляции кода.

Вы можете получить дополнительную информацию о данном параметре компилятора на его странице руководства .

На следующем шаге следует убедиться в том, что отладчик GDB установлен в вашей системе. Если он не установлен и вы используете основанный на Debian дистрибутив, такой, как Ubuntu, вы можете установить данный инструмент с помощью следующей команды:

Инструкции по установке отладчика в других дистрибутивах приведены на данной странице .

Теперь, когда вы скомпилировали вашу программу со специальным параметром компилятора для ее подготовки к отладке и установили в систему отладчик GDB, вы можете выполнить программу в режиме отладки с помощью следующей команды:

Хотя данная команда и инициирует запуск отладчика GDB, ваша программа не начнет исполняться сразу же после его запуска. В этот момент у вас имеется возможность задать параметры отладки. Например, вы можете установить точку останова, сообщающую отладчику GDB о том, что следует приостановить исполнение программы на строке с определенным номером или функции.

Для того, чтобы инициировать исполнение вашей программы, вам придется выполнить следующую команду GDB:

Стоит упомянуть и о том, что в том случае, если вашей программе нужно передать некие аргументы командной строки, вы можете передать их вместе с данной командной. Например:

GDB поддерживает большое количество полезных команд, которые находят свое применение в процессе отладки программных продуктов. Мы обсудим некоторые из них в следующем разделе.

Читайте также:  Установка драйверов вместе с установкой windows

Пример использования GDB

Теперь вы имеете базовое представление об отладчике GDB и принципе его использования. Поэтому предлагаю рассмотреть пример и применить полученные знания на практике. Это код примера:

В общем, данный код берет каждое значение из массива val , устанавливает это значение в качестве значения целочисленной переменной out , после чего рассчитывает значение переменной tot путем суммирования ее предыдущего значения и результата вычисления 0xffffffff/out .

Проблема заключается в том, что после запуска скомпилированного кода выводится следующее сообщение об ошибке:

Итак, для отладки кода в первую очередь следует скомпилировать код программы с использованием параметра компилятора -g . Это команда компиляции:

Далее предлагаю запустить отладчик GDB и сообщить ему имя исполняемого файла, который мы хотим отлаживать. Для этого используется следующая команда:

Теперь следует обратить внимание на то, что в процессе исполнения программы выводится сообщение об ошибке «floating point exception» и, как многие из вас наверняка знают, данная ошибка обычно связана с делением произвольного значения на ноль. Помня об этом, я помещаю точку останова в строку под номером 11, где осуществляется деление. Это делается следующим образом:

Обратите внимание на приветствие отладчика (gdb) , после которого я ввел команду break 11 .

Теперь я прошу GDB начать исполнение программы:

При достижении точки останова в первый раз GDB выводит следующую информацию:

Как несложно заметить, отладчик вывел код строки, в которой была установлена точка останова. Теперь давайте выведем текущее значение переменной out . Это делается следующим образом:

Очевидно, что отладчик вывел значение 5 . На основе этой информации можно сделать вывод, что в текущее время программа работает корректно. Поэтому я прошу отладчик продолжить исполнение программы до достижения следующей точки останова путем ввода команды c .

Я продолжаю выполнять аналогичные действия до того момента, пока не вижу нулевое значение переменной out .

Теперь для подтверждения предположения о том, что имеется проблема, я использую команду GDB s (или step ) вместо c . Это делается потому, что я хочу, чтобы строка 11, на которой на данный момент остановилось исполнение программы, была исполнена, в результате чего исполнение программы может аварийно завершиться.

В результате случается следующее:

Да, приведенный выше вывод подтверждает, что системный сигнал генерируется именно в этой строке. Окончательное подтверждение происходит при попытке повторного исполнения команды s :

Вы можете отлаживать свои программы с помощью GDB аналогичным образом.

Заключение

В данной статье мы рассмотрели лишь малую толику возможностей отладчика GDB, доступных для изучения и использования. Обратитесь к странице руководства GDB для получения дополнительной информации о данном инструменте и попытайтесь самостоятельно использовать его для отладки своего кода. Рассматриваемый отладчик не является дружелюбным, но трата времени на его изучение вполне оправдана.

Источник

Основы консольного отладчика GDB

Введение

gdb — наиболее популярный отладчик в системе linux.

Его используют как непосредственно из командной строки, так и из специальных графических оболочек (frontends)

  • запускать программу в режиме отладки с параметрами и без;
  • останавливать программу при возникновении определённых событий;
  • исследовать окружение программы в момент остановки;
  • изменять состояние программы в момент остановки

Компиляция

Перед отладкой необходимо скомпилировать программу с ключом -g или -gdb:

Есть также возможность использовать несколько уровней полноты отладочной информации (от 0 до 3):

Запуск

Для запуска программы под отладчиком используют следующие команды:

в этом случае после старта GDB мы увидим служебную информацию и результат чтения символьной таблицы из программы, например:

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

предназначена для передачи исследуемой программе параметров командной строки

Выход

Выход из отладчика осуществляется командами q или quit

Запуск команд shell

Находяcь внутри отладчика можно запустить команду shell:

Запуск программы под отладчиком

После загрузки программы мы можем запустить её с помощью run и выполнить всю, а также с помощью start и выполнить её по шагам.

Пример выполнения программы:

Читайте также:  Принтер перенаправлено windows server

Это другой пример выполнения программы (используется start и n (next) для пошагового выполнения:

Точки останова

устанавливает точку останова на строке с номером N. Если запустить программу с помощью run, то выполнение остановится на N-ой строке.

в любой момент выводит на экран список точек останова

Точки останова могут содержать условные выражения

break . if cond

Следующая команда заставит остановиться на 25 строке, если значение переменной age больше 15:

Точки просмотра

Помимо точек останова breakpoints в GDB есть »точки просмотра» watchpoints. Их назначение — останавливать выполнение программы, если заданная переменная изменяется.

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

Продолжить выполнение программы можно командой c (continue)

Кроме команды watch есть аналогичная rwatch, позволяющая задать точку просмотра для переменной, из которой читаются данные.

Точки останова и просмотра можно сделать неактивными с помощью команды disable

Листинг программы

Команда list позволяет просмотреть исходный код программы вместе с номерами строк, не покидая отладчик

Просмотр и изменение переменных

Команды print и set используются для просмотра и изменения значений переменных

Пример с set

Можно просмотреть содержимое локальных переменных с помощью

Источник

Использование отладчика GDB по максимуму

В нашей повседневной работе, как и всем, требуется много пользоваться отладчиком. В силу специфики работы: (разработка ОС, использование технологий виртуализации наподобие Intel-VT, ит.д.) нам часто требуется использовать отладчик для работы со специфическими случаями: отладка кода загрузчика ядра, отладка загрузчиков виртуальных машин, а так же в принципе обеспечение возможности отлаживать ОС собственной разработки. Именно эти особые случаи так пафосно названы в заголовке ”по максимуму”.

Для решения всех этих задач (и конечно, многих других) мы используем gdb. Возможно использование и таких оболочек как DDD, но лично я предпочитаю использовать cgdb как оптимальный выбор, особенно для случая работы с отладчиком по ssh.
В этой статье мы расскажем о том, как можно использовать gdb для отладки кода загрузочных секторов и загрузчиков.

В начале разработки любой новой ОС, требуется обеспечить и наличие банального загрузчика для этой ОС. «Правда жизни» разработчика ОС заключается в первую очередь в том, что процессор начинает выполнять код загрузочного сектора срезу после BIOS Post в Real Mode. На этот момент никакой ОС еще не загружено (только 512 байт загрузочного сектора), в то время как для полноценной поддержки отладчика нам требуется реализация особого модуля в ядре ОС (об этом подробнее в части 2). Возникает вопрос: как же отлаживать код бут-сектора и загрузчика? Ведь до начала работы с основным отладчиком ОС (через специальный модуль) необходимо провести загрузку в оперативную память “загрузчика системы”, загрузку ядра и его базовых модулей, базовую инициализацию аппаратуры (для работы с gdb нужен по крайней мере Serial Port), и только потом работа с полноценным отладчиком ОС становится возможной.
Решить эту проблему можно, как оказывается, довольно просто: нужно начать загрузку ОС внутри какой-нибудь виртуальной машины, которая поддерживает встроенные функции отладки.

В качестве примера приведем описание использования qemu:
1. Запускаем виртуальную машину (для примера используется простая загрузка ОС с дискеты):
qemu -fda ./boot.fdd -s -S -vnc none &
(Запускаем qemu виртуальную машину, которая будет загружаться с дискеты, образ которой находится в файле “./boot.fdd”; “-s –S” означает, что машина запустится в приостановленном режиме и в режиме отладки; “-vnc none” означает, что машина запустится без активного терминала (то есть, в фоновом режиме – особенно удобно при работе через ssh, да и для отладки загрузчика и бут сектора видеть экран компьютера редко требуется); “&” — запускаем виртуалку в фоновом режиме).
2. Теперь, запускаем сам отладчик gdb:
$ gdb
GNU gdb (Ubuntu/Linaro 7.2-1ubuntu11) 7.2
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
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 «i686-linux-gnu».
For bug reporting instructions, please see:
.
(gdb)
3. В отладчике gdb коннектимся к порту qemu:
(Gdb) target remote localhost: 1234

Читайте также:  Загрузка linux по сети tftp

Кстати этот порт виден в netstat:
$ netstat –tlpn
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0: 1234 0.0.0.0:* LISTEN 4014/qemu

Обратите внимание на адрес, с которого мы начинаем отладку:
(gdb) target remote localhost: 1234
Remote debugging using localhost: 1234
0x0000fff0 in ?? ()
(gdb)

С этого адреса начинает свою работу любой процессор при включении питания. Другими словами мы оказались прямо в начале работы BIOS (внутри виртуальной машины, естественно).
4. Далее нам надо подготовить gdb к отладке Real Mode кода:
(gdb) set arch i8086
The target architecture is assumed to be i8086
(gdb)

5. Теперь нам нужно поставить первый брэйкпоинт на начало нашего загрузочного сектора:
(gdb) break * 0x7c00
Breakpoint 1 at 0x7c00
(gdb)

(BIOS загрузит первые 512 байт с указанного устройства (с дискеты) в память по адресу 0x7c00 и передаст управление на эту область памяти).

6. Прыгаем в наш загрузочный сектор:
(gdb) c
Continuing.

Breakpoint 1, 0x00007c00 in ?? ()
(gdb)

7. Готово! Можно отлаживать наш код!
Существует ряд особенностей отладки кода отладчика, с которыми приходится работать.
Во-первых, нужно учитывать особенности адресации данных в режиме RealMode. Любая информация адресуется как segment:offset, при этом адрес можно посчитать как: segment * 16 + offset. Это означает, что для того, чтобы прочитать первые 10 инструкций кода, начиная с текущей инструкции, нужно использовать команду: (gdb) x/10i $cs*16+$eip . Неудобство в данном случае заключается в том, что текущую инструкцию gdb показывает без учета базы сегмента кода:
(gdb) x/10i $cs*16+$eip
0x80000: jmp 0x90013
0x80002: (bad)
0x80003: mov $0x8,%di
0x80006: add $0xff,%al
0x80008: aas
0x80009: add %al,(%bx,%si)
0x8000b: add %al,(%bx,%si)
0x8000d: add %al,(%bx,%si)
0x8000f: add %al,(%bx,%si)
0x80011: add %al,(%bx,%si)
(gdb)

В то время как адрес инструкции равен:
Program received signal SIGTRAP, Trace/breakpoint trap.
0x00000000 in ?? ()
(gdb)

Во-вторых, приходится вручную устанавливать и снимать брэйкпоинты. Это означает, что если вы поставили брэйкпоинт на адрес:
(gdb) break *0x80017
Breakpoint 4 at 0x800 17

…затем попали на него.
(gdb) c
Continuing.

Program received signal SIGTRAP, Trace/breakpoint trap.
0x000000 17 in ?? ()
(gdb)

…а потом сделаете stepi, то вы опять попадете на брэйкпоинт, вместо выполнения самой инструкции и перехода на следующую:
Program received signal SIGTRAP, Trace/breakpoint trap.
0x000000 17 in ?? ()
(gdb) stepi
0x000000 17 in ?? ()
(gdb)

Поэтому, процесс отладки может выглядеть примерно следующим образом:
1. где-то в коде загрузчика:
Program received signal SIGTRAP, Trace/breakpoint trap.
0x00000023 in ?? ()
(gdb) x/10i $cs*16+$eip
0x80023: mov %ebx,0xb
0x80028: movl $0x0,0xf
0x80031: call 0x80c15
0x80034: or %ax,%ax
0x80036: je 0x80087
0x80038: call 0x809af
0x8003b: call 0x80193
0x8003e: or %ax,%ax
0x80040: je 0x80268
0x80042: mov $0x3f4,%dx
(gdb)
2. ставим брэйкпоинт, срезу после call:
(gdb) break *0x8003e
Breakpoint 6 at 0x8003e

3. запускаем программу до этого брэйкпоинта:
(gdb) c
Continuing.

Program received signal SIGTRAP, Trace/breakpoint trap.
0x0000003e in ?? ()
(gdb)
4. удаляем брэйкпоинт:
(gdb) delete 6
(gdb)

5. проверяем возвращаемое значение:
(gdb) print /x $ax
$2 = 0x8000
(gdb)

6. делаем шаг далее:
(gdb) stepi
0x00000040 in ?? ()
(gdb)

А вот так можно прочитать значение одного из параметров функции:
(gdb) x/1w $ss*16+0x12
0x70012: 0x00b8fa00
(gdb)

Мы часто пользуемся этим методом. Правда, приходится его использовать совместно с еще парочкой других (например, отладка виртуальной машины в виртуальной машине), что немного усложняет работу, но суть остается прежней.

Источник

Оцените статью