Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

GDB

Глава 4 из книги "Архитектура приложений с открытым исходным кодом", том 2.
Оригинал: GDB
Автор: Stan Shebs
Перевод: А.Панин

4.3. Блочная диаграмма

Обобщенная структура GDB
Рисунок 4.1: Обобщенная структура GDB

При отдаленном рассмотрении можно сделать вывод о том, что приложение GDB состоит из двух частей:
  1. Часть, ответственная за обработку символов, занимается работой с информацией о символах программы. Информация о символах включает имена функций и переменных, информацию о типах данных, номера строк, информацию об использовании машинных регистров и другую подобную информацию. В части, ответственной за обработку символов, происходит извлечение этой информации из исполняемого файла программы, разбор полученных выражений, поиск адресов в памяти для заданного номера строки, поиск в исходном коде и общая работа с программой по мере написания ее исходного кода разработчиком.
  2. Часть, ответственная за работу в целевой системе, занимается непосредственным взаимодействием с целевой системой. Она позволяет запустить и остановить исполнение программы, считать данные из памяти и регистров, изменить их, перехватить сигналы, а также выполнить другие аналогичные действия. Специфика реализации этих операций значительно отличается для различных систем; большинство Unix-подобных систем предоставляет специальный системный вызов с именем ptrace, который дает возможность одному процессу читать и записывать данные состояния другого процесса. Таким образом, часть GDB, ответственная за работу в целевой системе, в большинстве случаев использует системный вызов ptrace и интерпретирует получаемые результаты. Однако, в случае кросс-отладки встраиваемой системы, часть, ответственная за работу в целевой системе, формирует пакеты сообщений для отправки по сети и ожидает пакетов, отправленных в ответ.

Эти две части в каком-то смысле независимы друг от друга; вы можете исследовать код вашей программы, просматривать типы переменных, и.т.д., без непосредственного запуска программы. С другой стороны, в также можете заниматься отладкой необработанного машинного кода даже в том случае, если информация о символах приложения не доступна.

Объединяющим две рассмотренные части и находящимся посередине звеном является командный интерпретатор и главный управляющий цикл исполнения.

4.4. Примеры работы

В качестве простого примера взаимодействия описанных частей рассмотрим команду print, описанную выше. Командный интерпретатор ищет функцию, соответствующую команде print и предназначенную для разбора выражения и преобразования его в простую древовидную структуру, которая в последствии подвергнется обработке путем обхода дерева. В какой-то момент будет осуществлено обращение к таблице символов для установления того, что positive_variable является целочисленной глобальной переменной, которая хранится, скажем, по адресу 0x601028 в памяти. После этого осуществляется вызов функции из части, ответственной за работу в целевой системе, для чтения четырех байт из памяти по указанному адресу с последующей передачей этих байт функции форматированного вывода, которая выведет их в формате числа в десятичной системе счисления.

Для вывода исходного кода и его скомпилированной версии GDB осуществляет комбинацию операций чтения данных из файла исходного кода и целевой системы, после чего использует сгенерированную компилятором информацию о номере строки для объединения двух представлений. В приведенном здесь примере строка 232 имеет адрес 0x4004be, строка 233 находится по адресу 0x4004ce, и.т.д.
[...]
232  result = positive_variable * arg1 + arg2;
0x4004be <+10>:  mov  0x200b64(%rip),%eax  # 0x601028 <positive_variable>
0x4004c4 <+16>:  imul -0x14(%rbp),%eax
0x4004c8 <+20>:  add  -0x18(%rbp),%eax
0x4004cb <+23>:  mov  %eax,-0x4(%rbp)

233  return result;
0x4004ce <+26>:  mov  -0x4(%rbp),%eax
[...]

Команда пошаговой отладки step скрывает запутанные переходы, происходящие уровнем ниже. В момент, когда пользователь хочет перейти к следующей строке программы, части, ответственной за работу в целевой системе, отправляется запрос на выполнение единственной инструкции программы и повторной остановки (это одна из операций, которую можно выполнить с помощью вызова ptrace). После получения информации об остановке выполнения программы, GDB запрашивает значение из регистра программного счетчика (program counter - PC) (другая операция, выполняемая частью, ответственной за работу в целевой системе), после чего происходит сравнение этого значения с диапазоном адресов, который ассоциирован с данной строкой в части, ответственной за обработку символов. Если значение программного счетчика находится за пределами этого диапазона, GDB оставляет программу в остановленном состоянии, устанавливает новый номер строки исходного кода и сообщает об этом пользователю. Если же значение программного счетчика все еще находится в диапазоне адресов текущей строки, GDB переходит к выполнению следующей инструкции с последующим повторением проверки, повторяя эту последовательность действий до тех пор, пока программный счетчик не станет указывать на другую строку. Преимущество этого простого алгоритма заключается в том, что он всегда работает правильно независимо от того, имеются ли в строке переходы, вызовы подпрограмм, и.т.д., при этом он не требует от GDB интерпретации всех всех деталей, относящихся к используемому набору машинных инструкций. Недостаток же заключается в большом количестве взаимодействий с целевой системой в ходе каждого отдельного шага, что может привести к заметному замедлению отладки при использовании некоторых встраиваемых целевых систем.


Продолжение статьи: Переносимость