[Из песочницы] Удаленная отладка в Linux при помощи связки GDB-gdbserver
Как всем нам известно, процесс отладки это такая вещь, важность которой трудно переоценить. Причем, понимая важность таких методов как дебажное моргание светодиодами и вывод дебажных сообщений в порт, я остаюсь при мнении, что эффективнее пошаговой отладки пока ничего не придумано. Однако, задача пошаговой отладки становится не такой тривиальной в случае программирования под Linux на встраиваемых системах (таких как rasbery pi, virt2real или промышленные процессорные модули).Данную задачу в Linux призвана решать стандартная связка программ GDB и gdbserver. Идея в том, что пишешь на компе программу (host в терминологии GDB), компилируешь её и заливаешь на целевое устройство (target). Далее запускаешь на целевом устройстве (target) отлаживаемый файл и gdbserver, а на хосте GDB и вперед.Схему взаимодействия такой отладки кратко можно представить так:
Исполняемый файл — это та самая программа, которую мы хотим удаленно отлаживать. Для корректной работы, экземпляр программы должен находиться не только на целевом усройстве, но и на компе (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
int main (int argc, char* argv[])
{
std: cout << "Hello,\n" << std::endl;
std::cout << "debugged\n" << 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
Breakpoint 1, main (argc=1, argv=0×3efffb44) at hello.cpp:5 5 std: cout << "Hello\n" << std::endl; (gdb) Переходим к следующей строке (gdb) next 6 std::cout << "debugged\n" << 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. Вероятно, что для продвинутых линуксоидов материал покажется банальным и «корявым», однако для тех людей, кто больше является «железячником», или для программистов микроконтроллеров переход на линукс является делом не таким уж и простым (сужу по себе). Так что, надеюсь, материал кому-либо сослужит полезную службу.