GDB

Материал из Русский WINE
Перейти к: навигация, поиск

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

Настройка оборудования

Поскольку мы используем возможности "удалённой" работы GDB, нам необходимо создать соединение через последовательный порт от проверочной машины (той, где запущен GDB) до целевой (где запущен ReactOS) при помощи нуль-модемного кабеля. Вы можете использовать кабель со следующей разводкой (разъёмы DB9):

  • Соедините между собой контакты 1, 4 и 6 (DTR, CD, DSR) на каждом разъёме
  • Контакты 2 и 3 одного разъёма соедините с контактами 3 и 2 другого разъёма (Rx, Tx)
  • Соедините контакты 5 обоих разъёмов (GND)
  • Контакты 7 и 8 одного разъёма соедините с контактами 8 и 7 другого разъёма (RTS, CTS)

Целевой машиной может быть другой компьютер или виртуальная машина VMWare. Для использования виртуальной машины VMWare установите VmwareGateway со страницы http://l4ka.org/tools/vmwaregateway.php и подключите GDB к порту tcp\ip:

target remote localhost:567

Установка CygWin

Загрузите CygWin с http://www.cygwin.com/setup.exe и запустите его. Вы можете использовать настройки по умолчанию, убедитесь лишь, что выбрали "gdb" из категории "Devel" в диалоге выбора пакетов.

Хотя стандартный CygWin gdb/insight должен работать и так, существует несколько модифицированная версия, которую можно скачать по адресу http://svn.reactos.org/downloads/gdb/gdb_reactos.zip

Изменения в модифицированной версии:

  • Исправление, позволяющее устанавливать точки контроля кода в удалённых машинах архитектуры i386
  • Добавлена возможность производить обратную трассировку стека из режима ядра в обратно в пользовательский режим
  • Файл insight.exe помечен как исполняемый файл подсистемы Windows (ранее он являлся консольным приложением)

Вы можете загрузить файл .zip и заменить файлы gdb.exe, gdbtui.exe и insight.exe в CygWin (по умолчанию они находятся в папке C:\cygwin\bin) на аналогичные, находящиеся в архиве. Даже при использовании модифицированной версии файлов, вам всё равно необходимо установить пакет gdb из CygWin, поскольку в нём имеется несколько дополнительных файлов, которых нет в zip-архиве.

Инструкции по самостоятельной сборке модифицированной версии из исходного кода можно найти в самом конце этой статьи.

Подготовка ReactOS

На данный момент, для поддержки удалённой отладки в ReactOS можно использовать только порт COM2. Если вам необходимо использовать порт COM1, то отредактируйте reactos\ntoskrnl\kd\wrappers\gdbstub.c, найдите GdbPortInfo и произведите необходимые изменения (2 -> 1). Обычно ReactOS собирается с ключом оптимизации -Os, что означает, что компилятор может переупорядочивать код и сохранять переменные в регистры. Мы рекомендуем проводить сборку без использования оптимизаций. Установите значение GDB в файле config.rbuild в "1".

Для GDB необходима отладочная информация "stabs", обычно генерируемая при сборке исполняемых файлов. Для избежания этого, задайте переменной "ROS_BUILDNOSTRIP" значение "yes". Затем выполните команды "make clean" и "make".

Если вы зайдёте, например, в папку output-i386\ntoskrnl, вы увидите файлы "ntoskrnl.exe" и "ntoskrnl.nostrip.exe". Файл "ntoskrnl.exe" аналогичен тому, который содержится в образе диска ReactOS, а "ntoskrnl.nostrip.exe" это файл, созданный для работы GDB. "make install" знает, что он используется "ntoskrnl.exe".

Для активации отладки в GDB вам необходимо изменить файл freeldr.ini. Волшебное слово "Options=/DEBUGPORT=GDB", хотя лично я предпочитаю даже при использовании GDB производить запись в протокол отладки, поэтому я использую "Options=/DEBUGPORT=COM1 /DEBUGPORT=GDB". Виртуальный порт COM1 моей виртуальной машины VMware переназначен на запись в файл, а виртуальный порт COM2 переназначен в физический порт.

Запуск отладочной сессии

Теперь всё готово для запуска отладочной сессии. Загрузите ReactOS и во freeldr выберите пункт, для которого задан параметр загрузки "/DEBUGPORT=GDB". В самом начале процесса загрузки на экране должна появиться надпись "Waiting for GDB to attach".

Это сигнал для запуска Insight (insight.exe из c:\cygwin\bin). Откройте окно его консоли (пункт Console из меню View, горячей клавишей Ctrl+N или кнопкой "C:\" на панели кнопок). Теперь необходимо указать GDB расположение файлов источника отладки. Поскольку Insight является приложением CygWin, вам необходимо сделать это в "представлении CygWin", т.е. если корневой папкой дерева исходного кода является папка "H:\ros\reactos", то путь необходимо задавать "/cygdrive/h/ros/reactos". Наберите следующие команды в окне команд (в зависимости от ситуации):

directory /cygdrive/h/ros/reactos
symbol-file /cygdrive/h/ros/reactos/output-i386/ntoskrnl/ntoskrnl.nostrip.exe

Первая команда сообщает GDB расположение дерева исходного кода, вторая заставляет его загружать символы из файла ntoskrnl.nostrip.exe. Теперь сообщаем GDB, что отладка будет производиться удалённо:

set remotebaud 115200
target remote /dev/ttyS0

(Скорость обмена 115200 бод тоже жёстко прописана в код, вы можете изменить её в файле reactos/ntoskrnl/kd/gdbstub.c)

/dev/ttyS0 это эквивалент COM1 в CygWin, если вам необходимо использовать в GDB порт COM2, соответственно пишите /dev/ttyS1.

После выполнения этих команд должна появиться надпись, сообщающая вам о том, что вы находитесь в DbgBreakPointWithStatus@4(). На этой стадии GDB становится активным и вы можете воспользоваться, например командой "where" для получения обратной трассировки стека, хотя лично я пользуюсь встроенной в Insight возможностью просмотра стека чаще, чем командой "where". Обратите внимание, что статус программы отображается как "Program not running. Click on run icon to start." ("Программа не запущена. Для запуска, щёлкните на значок Запуск.", однако это не так. Воспользуйтесь командой "continue" (или просто "c") для продолжения выполнения. После подачи этой команды, загрузка ReactOS должна продолжиться. GDB не будет вмешиваться в работу до тех пор, пока не встретит точку останова или не произойдёт исключительная ситуация. Вы можете активировать/деактивировать GDB, нажав сочетание клавиш Tab+K в ReactOS.

Четыре первые приведённые выше команды вам придётся вводить при каждом запуске системы, проэтому будет проще сохранить их в файле .gdbinit. У меня файл .gdbinit сохранён в C:\cygwin\bin и я использую ярлык на C:\cygwin\bin\insight.exe в качестве рабочей папки у которого C:\cygwin\bin.

Дополнительная настройка GDB

В то время, пока ReactOS ожидает присоединения GDB, в память загружены только ntoskrnl и hal. Это означает,что на этом этапе вы можете ставить точки останова только в ntoskrnl и hal (хотя установить точку останова в hal не так-то и просто). Самым простейшим способом обхода этого ограничения является установка точки останова в отлаживаемом компоненте простой вставкой строки

__debugbreak();

в исходный код компонента, и последующая его перекомпиляция/установка. При прохождении этой точки останова происходит активация GDB. Однако существует небольшая проблема, мы сообщили GDB лишь о символах в ntoskrnl, а не о символах других компонентов. Это можно легко поправить, отдав следующую команду (пример):

add-symbol-file /cygdrive/h/ros/reactos/output-i386/dll/win32/kernel32/kernel32.nostrip.dll

Она заставляет GDB дополнительно загрузить символы для kernel32.dll, что будет весьма полезно, если вы отлаживаете kernel32.dll. Кстати, вы можете загрузить эти символы во время запуска GDB, когда сам модуль ещё не загружен в ReactOS. Другими словами, вы можете добавить все эти команды add-symbol-file в файл .gdbinit.

Это (чаще всего) отлично работает для компонентов пользовательского режима. Однако, проблемы могут произойти, когда ReactOS потребуется переместить .DLL по причине того, что её пространство памяти перекрывается пространством памяти другого компонента. До тех пор, пока вы не настроите иначе, GDB будет полагать, что компонент загружен по адресу, указанному в PE-заголовке файла. Если в действительности компонент загружен по другому адресу, то необходимо сообщить об этом GDB.

Особенно это касается компонентов режима ядра. Правильный адрес загрузки имеется лишь в заголовке ntoskrnl (0x80000000), все остальные компоненты загружаются в первый же доступный слот памяти ядра. Хуже того, это означает, что компоненты ядра могут быть загружены в другие адреса при следующей загрузке системы. Для устранения этой проблемы, я использую следующий патч ntoskrnl/ldr/loader.c:

Index: ntoskrnl/ldr/loader.c
===================================================================
--- ntoskrnl/ldr/loader.c       (revision 20628)
+++ ntoskrnl/ldr/loader.c       (working copy)
@@ -747,13 +747,26 @@

     /*  Allocate a virtual section for the module  */
     DriverBase = NULL;
+    if (0 == wcscmp(L"\\??\\C:\\ReactOS\\system32\\win32k.sys", FileName->Buffer))
+    {
+      DriverBase = (PVOID) 0xe8000000;
+    }
+    if (0 == wcscmp(L"\\SystemRoot\\system32\\drivers\\afd.sys", FileName->Buffer))
+    {
+      DriverBase = (PVOID) 0xe8100000;
+    }
+    if (0 == wcscmp(L"\\SystemRoot\\system32\\drivers\\tcpip.sys", FileName->Buffer))
+    {
+      DriverBase = (PVOID) 0xe8300000;
+    }
+
     DriverBase = MmAllocateSection(DriverSize, DriverBase);
     if (DriverBase == 0)
     {
         CPRINT("Failed to allocate a virtual section for driver\n");
         return STATUS_UNSUCCESSFUL;
     }
-    DPRINT("DriverBase for %wZ: %x\n", FileName, DriverBase);
+    DPRINT1("DriverBase for %wZ: %x\n", FileName, DriverBase);

     /*  Copy headers over */
     memcpy(DriverBase, ModuleLoadBase, PENtHeaders->OptionalHeader.SizeOfHeaders);

Он приводит к тому, что win32k.sys, afd.sys и tcpip.sys загружаются по адресам 0xe8000000, 0xe8100000 и 0xe8300000 соответственно. Затем, я добавляю следующие строки в файл .gdbinit:

add-symbol-file /cygdrive/h/ros/reactos/output-i386/subsystems/win32/win32k/win32k.nostrip.sys 0xe8001000
add-symbol-file /cygdrive/h/ros/reactos/output-i386/drivers/network/afd/afd.nostrip.sys 0xe8101000
add-symbol-file /cygdrive/h/ros/reactos/output-i386/drivers/network/tcpip/tcpip.nostrip.sys 0xe8301000

Обратите внимание на несоответствие между адресами в патче загрузчика и адресами в файле .gdbinit: в .gdbinit адреса на 0x1000 выше, чем в патче загрузчика. Причиной этого является то, что в загрузчике мы задаём начало модуля, а в файле .gdbinit мы задаём начало кодовой секции. Поскольку в начале модуля находится заголовок размером 4096 (0x1000) байт, то кодовая секция начинается через 4096 байт после начала модуля.

Экспериментальный способ автоматической загрузки символов в GDB

[arty] -> Я реализовал способ автоматического способа поиска адресов модулей. Посмотрите здесь (для linux, обратите внимание: этот скрипт необходимо обновить для использования LDR_DATA_TABLE_ENTRY как его аналога в windows) rossym.gdb или (для windows) roswin.gdb.

Известные проблемы

Хотя отладка в GDB, как правило, работает очень хорошо, есть еще несколько проблем. Во-первых, нельзя проверить значение статической переменной в компонентах режима ядра. Это также вызвано возможностью перемещения модуля. Команда "add-symbol-file <модуль> <адрес-кода>" сообщает GDB, что код был перемещён в новый адрес, не сообщая при этом, данные были также перемещены. Вообще, возможно заставить GDB переместить и другие секции, но чаще всего мне просто лень заморачиваться с этим и просто делаю небольшое изменение в исходном коде, устанавливая локальную (на основе стека) переменную-указатель в статические данные, а затем проверяю значение, на указываемое этой локальной переменной.

Ещё одна проблема заключается в том, что GDB вызывается по исключениям "first-chance". Это означает, что GDB перехватит управление даже в том случае, если код сам по себе может поддерживать обработку исключений. Похоже, что необходимо изменить GDB таким образом, чтобы он перехватывал только исключения второго шанса (second-chance) (он должен пробуждаться, если в коде не имеется поддержки исключений).

Когда поток возбуждает исключение, работа других потоков не блокируется. Это означает, что другие потоки могут изменить состояние машины, а вы рискуете этого даже не заметить. Это также означает, что другой поток может вызвать исключение, и тогда у нас появится два потока, конкурирующих за внимание gdb. Для избежания этого используется быстрый мьютекс, позволяющий исключить попадание второго потока в заглушку GDB. Поскольку при этом возбуждается IRQL уровня APC_LEVEL, это создаёт уже другие проблемы.

GDB "знает" о setjmp/longjmp. Это проблема, поскольку он будет пытаться самостоятельно установить точку останова на вызове longjmp() в MSVCRT. Если MSVCRT загружен, то никаких проблем нет. Если же вы отлаживаете процесс, который не использует MSVCRT, но у вас имеется строка "add-symbol-file msvcrt" в файле .gdbinit, то начнутся проблемы. GDB попытается установить точку останова, однако это ему сделать не удастся, и выполнение команды "continue" станет невозможно. Для избежания возникновения этой проблемы не помещайте в файл .gdbinit соответствующую строку для загрузки символов msvc до тех пор, пока вам это не будет действительно необходимо.

Сборка модифицированной версии GDB из исходного кода

Для сборки из исходного кода вам потребуются следующие пакеты CygWin:

Категория Devel

  • bison
  • flex
  • gcc
  • gdb (с исходным кодом)
  • make
  • patchutils

Библиотеки

  • libncurses-devel
  • tcltk (с исходным кодом)

После их загрузки и установки, загрузите архив gdb_reactos_patch.zip и распакуйте его в /usr/src (C:\cygwin\usr\src если вы распаковываете из Windows).

Запустите командную оболочку Cygwin и проверьте наличие файлов /usr/src/tcltk.patch и /usr/src/gdb.patch. Затем произведите сборку модифицированного GDB при помощи следующих команд (из командной оболочки):

cd /usr/src/tcltk-20030901-1; patch -p 0 < ../tcltk.patch
cd /usr/src/gdb-20041228-3; patch -p 0 < ../gdb.patch
ln -s ../tcltk-20030901-1/tk tk
ln -s ../tcltk-20030901-1/tcl tcl
ln -s ../tcltk-20030901-1/itcl itcl
cd /usr/src; mkdir gdb-build; cd gdb-build
../gdb-20041228-3/configure --with-prefix=/usr
make
cd gdb
strip gdb.exe; strip gdbtui.exe; strip insight.exe
ReactOS
Search.png
Доклады
О ReactOSARWINSSЧеЗа
Информация Новости Выпуски новостейПереводы блоговНовости проектаВидеоReactOS на ХабреUSB от Вадима Галянта
Разработка Руководство по программированиюОтсутствующая функциональностьВетви разработкиКомпоненты системыReactOS и WineПлан работRoadmap ядра by VgalРазработчикиСовместимость с dll WindowsНаиболее значимые изменения за годИспользуемые проектыGoogle Summer of CodeИзвестные проблемы
Порты AMD64ARMXboxPowerPC
Компоненты Файловые системыРежим совместимостиОтчеты об ошибкахПечатьUSBЯдро
Загрузчик Восстановление MBRЗагрузка из GRUBПараметры загрузки
Прочее ARWINSSПриложения в ReactOSОформление ReactOSКоординаторы"Пасхальные яйца"Монетизация
Другое Типы ядерFreeWin95
Помощь
RAM-диск ReactOS по PXEс жесткого диска
Разработка Стиль написания кодаСтандарты RC-файловРабота с документациейВенгерская нотацияGNU Indent • [ Subversion : ветвислияниеиспользование TortoiseSVN ] • Основы переводаОтправка патчей
Репорты Отладка в VirtualBoxОтладка на экранДобавление программы в менеджер приложенийОтправка отчетов
Отладка Com0comGDBKdbgRossym.gdbRoswin.gdbWinDBGРуководство по WinDBGВключение трассировки ядраКоды DPRINTУдалённый отладчик ReactOS
Сборка CMakeRBuildФайлы RBuildАвтоматическое копирование файловСборка MINGW-w64Сборка модулейСреда сборки
Тестирование VirtualBoxVMwareQEMUHyper-VНеобходимый объём дискаПеренос файлов на виртуальный дискУстановка ReactOSУстановка драйверов
Сеть Общие папкиSambaNFS
Игры Установка DirectPlay
Обновление ReactOSЗагрузочная флешкаЧем можно помочь проектуСоздание нового пользователяЗвук и сеть в VirtualBoxСъемка и публикация видеоIRC-каналСторонние компонентыFAQReactOS как рабочая станцияReactOS и UEFI
Обзоры ОболочкаNTVDMWOWCommunity EditionИстория сайтаReactOS ServerКриптографияПО времен XP