Віддаленого налагодження в Linux за допомогою зв'язки GDB-gdbserver

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

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

Схему такої взаємодії налагодження коротко можна подати так:

image<habracut/>

Виконуваний файл — це та сама програма, яку ми хочемо віддалено налагоджувати. Для коректної роботи, примірник програми повинен перебувати не тільки на цільовому усройстве, але і на компі (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 << "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 © 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL версії 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

Отримуємо відповідь і запрошення для введення наступної команди:

using Remote debugging 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" або "set sysroot"?

Breakpoint 1, main (argc=1, argv=0x3efffb44) 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" або "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. Ймовірно, що для просунутих линуксоидов матеріал здасться банальним і «корявим», однак для тих людей, хто більше є «железячником», або для програмістів мікроконтролерів перехід на лінукс є справою не такою вже й простим (суджу по собі). Так що, сподіваюся, матеріал комусь послужить корисну службу.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.