Удаленная отладка в Linux при помощи связки GDB-gdbserver

в 11:48, , рубрики: gdb, gdbserver, linux, отладка, метки:

Как всем нам известно, процесс отладки это такая вещь, важность которой трудно переоценить. Причем, понимая важность таких методов как дебажное моргание светодиодами и вывод дебажных сообщений в порт, я остаюсь при мнении, что эффективнее пошаговой отладки пока ничего не придумано. Однако, задача пошаговой отладки становится не такой тривиальной в случае программирования под Linux на встраиваемых системах (таких как rasbery pi, virt2real или промышленные процессорные модули).

Данную задачу в Linux призвана решать стандартная связка программ GDB и gdbserver. Идея в том, что пишешь на компе программу (host в терминологии GDB), компилируешь её и заливаешь на целевое устройство (target). Далее запускаешь на целевом устройстве (target) отлаживаемый файл и gdbserver, а на хосте GDB и вперед.

Схему взаимодействия такой отладки кратко можно представить так:

image

Исполняемый файл — это та самая программа, которую мы хотим удаленно отлаживать. Для корректной работы, экземпляр программы должен находиться не только на целевом усройстве, но и на компе (host) с которого идет отладка.
GDB — программа, которая непосредственно выполняет процесс отладки. Обычно, входит в состав тулчейна GCC и находится там же где и компилятор.
gdbserver — ответная часть GDB, которая запускает исполняемый файл в режиме отладки. Поскольку gdbserver запускается на удаленной стороне (target), то он должен быть собран под целевое устройство, при помощи кросс-компилятора. Собственно, сборке gdbserver'а из исходников и посвящена в основном данная статья.

В моём распоряжении есть плата virt2real, а так же процессорный модуль на базе процессора от TI серии AM335x. Ниже будет показана последовательность действий на примере virt2real, однако, всё тоже самое мною было успешно (и что важно — аналогично) проделано с чипом AM335x.

Примечание: операционная система, установленная на host'e — Ubuntu.12.04.

Подготовка

Создаем в своей домашней директории папку gdb, в которой и будем производить все наши манипуляции. Внутри создаем подпапку downloads:

mkdir -p ~/gdb/downloads
cd ~/gdb/downloads

Скачиваем необходимые нам исходники и распаковываем их. Нам понадобится сам GDB, а так же библиотека termcap (её использует GDB).

wget ftp://ftp.gnu.org/gnu/termcap/termcap-1.3.1.tar.gz
wget ftp://ftp.gnu.org/gnu/gdb/gdb-7.8.tar.gz
tar xvzf termcap-1.3.1.tar.gz
tar xvzf gdb-7.8.tar.gz

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

Собираем библиотеку termcap

Соответственно, начинаем с библиотеки termcap, потому что она потребуется позднее при сборке самого gdbserver'a. Создаем папку builds, в которую будем собирать наши программы. Внутри создаем папку v2r, в неё поместим все результаты для платформы virt2real. Ну а там уже папку termcap для построения библиотеки termcap. Переходим в созданную директорию.

mkdir -p ~/gdb/builds/v2r/termcap
cd ~/gdb/builds/v2r/termcap

Указываем системе компилятор и ranlib которые будем использовать. В моем случае это:

export CC=/opt/virt2real-sdk/codesourcery/arm-2013.05/bin/arm-none-linux-gnueabi-gcc
export RANLIB=/opt/virt2real-sdk/codesourcery/arm-2013.05/bin/arm-none-linux-gnueabi-ranlib

Теперь конфигурируем. Потребуется указать 2 параметра: --host и --prefix.

Что указывать в качестве параметров

  • Признаюсь, я так и не понял имеет ли значение, что указать в --host. У меня сложилось впечатление, что нет. В любом случае, я не сумел найти внятного объяснения, что именно нужно указывать в данном параметре, поэтому указал префикс компилятора. Т.е. мой компилятор называется arm-none-linux-gnueabi-gcc, поэтому в качестве --host я указал arm-none-linux-gnueabi.
  • В качестве параметра --prefix указываем папку, в которую мы хотим поместить результаты работа (конфигурирования), т.е. ту папку в которой мы сейчас и находимся.

../../../downloads/termcap-1.3.1/configure --host=arm-none-linux-gnueabi --prefix=~/gdb/builds/v2r/termcap

Если все сделано правильно и прошло без ошибок, то в папке ~/gdb/builds/v2r/termcap будет создан Makefile.

Проверка Makefile

Рекомендую перестраховаться, открыть Makefile и поглядеть, что в качестве переменных CC и RANLIB указаны именно те, что мы указывали выше. Дело в том, что во время моих первых попыток собрать gdbserver, я на долго застрял из-за этого момента, ибо все вроде проходило нормально, но вот результирующий файл никак не хотел запускаться на целевой плате. Оказалось, что по неведомым мне причинам, в Makefile упорно использовался стандартный компилятор. Если обнаружите, что компилятор в Makefile указан не тот, можно просто заменить его руками (мне помогло). Хотя, когда я повторял всю процедуру перед написанием статьи, такой проблемы у меня не возникло.

Далее собираем библиотеку:

make
make install

Собираем gdbserver

Создаем папку в которой будем собирать сервер и переходим в неё:

mkdir -p ~/gdb/builds/v2r/gdbserver
cd ~/gdb/builds/v2r/gdbserver

Указываем где взять библиотеку termcap:

export LDFLAGS="-static -L~/gdb/builds/v2r/termcap/lib"
export CFLAGS="-g -O2 -I~/gdb/builds/v2r/termcap/include"

Конфигурируем аналогично termcap. Тут важно отметить, что мы собираем gdbserver (а не GDB), поэтому файл configure указываем именно из папки /gdb-7.8/gdb/gdbserver:

../../../downloads/gdb-7.8/gdb/gdbserver/configure --host=arm-none-linux-gnueabi --prefix=/home/den1s/gdb/builds/v2r/gdbserver --disable-werror # &&

Примечание

Как ни старался я нигде не указывать абсолютных путей, что бы инструкцию можно было использовать вообще без корректировки под себя, у меня не получилось. Т.к. файл configure ругается на попытку указать в качестве --prefix путь, начинающийся с ~. Поэтому, тут потребуется имя своего пользователя вписать.

Если все верно, будет создан Makefile. Далее стандартно:

make
make install

Пробуем

Что бы протестировать процесс отладки создадим короткий Hello world и скомпилируем его под целевую платформу.

mkdir -p ~/virt2real/hello_test/
cd ~/virt2real/hello_test/
/opt/virt2real-sdk/codesourcery/arm-2013.05/bin/arm-none-linux-gnueabi-g++ hello.cpp -O0 -g -o hello

Исходный код файла hello.cpp:

#include <iostream>

int main(int argc, char* argv[])
{
   std::cout << "Hello,n" << std::endl;
   std::cout << "debuggedn" << std::endl;
   std::cout << "World!!!" << std::endl;
   return 0;
}

Заливаем исполняемый файл hello и наш gdbserver на целевую плату.
Я заливаю в папку /usr/ используя SCP:

scp ~/gdb/builds/v2r/gdbserver/bin/gdbserver root@192.168.3.1:/usr/
scp ~/virt2real/hello_test/hello root@192.168.3.1:/usr/

Теперь запускаем второй экземпляр терминала и подключаемся к целевой плате по ssh и переходим в папку /usr/:

ssh root@192.168.3.1
cd /usr/

Запускаем на целевой плате gdbserver, и с его помощью наш исполняемый (отлаживаемый) файл hello. Затем открываем дебажную сессию на понравившемся нам порту:

[root@virt2real usr]# ./gdbserver :123 hello
Process hello created; pid = 5680;
listening on port 123

Возвращаемся на host и запускаем отлаживаемый файл hello с помощью GDB

/opt/virt2real-sdk/codesourcery/arm-2013.05/bin/arm-none-linux-gnueabi-gdb hello

Заголовок

Что бы иметь возможность не указывать всё время полный путь к GDB, можно прописать переменную окружения:

export PATH=$PATH:/opt/virt2real-sdk/codesourcery/arm-2013.05/bin/

В ответ должны увидеть приглашение от GDB:

GNU gdb (Sourcery CodeBench Lite 2013.05-24) 7.4.50.20120716-cvs
Copyright (C) 2012 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 "--host=i686-pc-linux-gnu --target=arm-none-linux-gnueabi".
For bug reporting instructions, please see:
<https://sourcery.mentor.com/GNUToolchain/>...
Reading symbols from /home/den1s/virt2real/hello_test/hello...done.
(gdb) 

Подключаемся к удаленному gdbserver'у используя указанный выше порт:

(gdb) target remote 192.168.3.1:123

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

Remote debugging using 192.168.3.1:123
warning: Unable to find dynamic linker breakpoint function.
GDB will be unable to debug shared library initializers
and track explicitly loaded dynamic code.
0x36fd7e30 in ?? ()
(gdb) 

При этом удаленная сторона (gdbserver на target в лице virt2real) должна увидеть установку дебажной сессии:

Remote debugging from host 192.168.3.10      # где 192.168.3.10 - это IP нашего host'а с которого ведется отладка

Комментарий host target
Ставим breakpoint на main
(gdb) break main
Cannot access memory at address 0x0
Breakpoint 1 at 0x8748: file hello.cpp, line 5.
(gdb)
Идем до Breakpoint'а
(gdb) cont
Continuing.
warning: Could not load shared library symbols for 5 libraries, e.g. /usr/lib/libstdc++.so.6.
Use the "info sharedlibrary" command to see the complete listing.
Do you need "set solib-search-path" or "set sysroot"?

Breakpoint 1, main (argc=1, argv=0x3efffb44) at hello.cpp:5
5       std::cout << "Hellon" << std::endl;
(gdb) 
Переходим к следующей строке
(gdb) next
6       std::cout << "debuggedn" << std::endl;
(gdb) 
Hello
Переходим к следующей строке
(gdb) next
7       std::cout << "World!!!" << std::endl;
(gdb)
debugged
Переходим к следующей строке
(gdb) next
8       return 0;
(gdb) 
World!!!                                                      
Переходим к следующей строке (которой, впрочем нет)
(gdb) next
Cannot access memory at address 0x0
Cannot access memory at address 0x0
0x36d1f040 in ?? ()
(gdb)
Запускаем программу до конца, что приводит к её завершению
(gdb) cont
Continuing.
[Inferior 1 (process 1514) exited normally]
(gdb) 
Child exited with status 0
GDBserver exiting
[root@virt2real usr]# 

На этом пример пошаговой отладки закончен. Отмечу:

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

Установка sysroot

Для корректной работы GDB, ему нужны так называемые debugging symbols, которые могут быть считаны из библиотек удаленной операционки (target). Их отсутствие является, например, причиной подобных сообщений:

(gdb) next
Cannot access memory at address 0x0
Cannot access memory at address 0x0
0x36d1f040 in ?? ()
Use the "info sharedlibrary" command to see the complete listing.
Do you need "set solib-search-path" or "set sysroot"?

А потенциально вызывать и другие неприятные проблемы отладки.
Для устранения проблемы GDB на host'е необходимо указать где взять эти самые библиотеки с помощью команды:

set sysroot [Directory]

Если у Вас на host'е где-то завалялся образ линукса Вашго target'а, то необходимо указать путь до папки с библиотеками.
Или предварительно эти библиотеки выкачать на host:

mkdir -p ~/virt2real/sysroot
cd ~/virt2real/sysroot
scp -r root@192.168.3.1:/lib lib

Возвращаемся в папку с проектом, снова запускаем GDB и указываем путь до библиотек:

cd ~/virt2real/hello_test/
arm-none-linux-gnueabi-gdb hello
(gdb) set sysroot ~/virt2real/hello_test/

Теперь, периодически будут появляться сообщения типа:

Reading symbols from ~/virt2real/sysroot/lib/libgcc_s.s...done.

Посмотреть список используемых в текущий момент библиотек можно так:

(gdb) info sharedlibrary
From        To          Syms Read   Shared Object Library
0x36fd77a0  0x36ff24d0  Yes (*)     ~/virt2real/sysroot/lib/ld-linux.so.3
                        No          /usr/lib/libstdc++.so.6
0x36e70e00  0x36ed107c  Yes (*)     ~/virt2real/sysroot/lib/libm.so.6
0x36e51028  0x36e63154  Yes (*)    ~/virt2real/sysroot/lib/libgcc_s.so.1
0x36d1cb00  0x36e11ae8  Yes (*)     ~/virt2real/sysroot/lib/libc.so.6
(*): Shared library is missing debugging information.

Список используемой «литературы»

P.S.: данная статья не претендует на научную новизну, но является попыткой собрать в одном месте подробную информации по сборке и настройке связки GDB-gdbserver. Вероятно, что для продвинутых линуксоидов материал покажется банальным и «корявым», однако для тех людей, кто больше является «железячником», или для программистов микроконтроллеров переход на линукс является делом не таким уж и простым (сужу по себе). Так что, надеюсь, материал кому-либо сослужит полезную службу.

Автор: den1s1

Источник

Поделиться

* - обязательные к заполнению поля