Я продолжаю задаваться вопросом, как работает отладчик? В частности, тот, который можно «прикрепить» к уже запущенному исполняемому файлу. Я понимаю, что компилятор переводит код на машинный язык, но тогда как отладчик «узнает», к чему он подключен?
Я продолжаю задаваться вопросом, как работает отладчик? В частности, тот, который можно «прикрепить» к уже запущенному исполняемому файлу. Я понимаю, что компилятор переводит код на машинный язык, но тогда как отладчик «узнает», к чему он подключен?
Ответы:
Детали того, как работает отладчик, будут зависеть от того, что вы отлаживаете, и что такое ОС. Для нативной отладки в Windows вы можете найти некоторые подробности по MSDN: Win32 Debugging API .
Пользователь сообщает отладчику, к какому процессу подключаться, по имени или по идентификатору процесса. Если это имя, то отладчик будет искать идентификатор процесса и инициировать сеанс отладки с помощью системного вызова; под Windows это будет DebugActiveProcess .
После подключения отладчик войдет в цикл событий, очень похожий на любой пользовательский интерфейс, но вместо событий, поступающих из системы управления окнами, ОС будет генерировать события на основе того, что происходит в отлаживаемом процессе - например, происходит исключение. Смотрите WaitForDebugEvent .
Отладчик может читать и записывать виртуальную память целевого процесса и даже корректировать значения его регистров через API, предоставляемые ОС. Смотрите список функций отладки для Windows.
Отладчик может использовать информацию из файлов символов для преобразования адресов в имена переменных и местоположения в исходном коде. Информация о файле символов представляет собой отдельный набор API и не является основной частью ОС как таковой. В Windows это осуществляется через Debug Interface Access SDK .
Если вы отлаживаете управляемую среду (.NET, Java и т. Д.), Процесс, как правило, будет выглядеть одинаково, но детали будут другими, поскольку среда виртуальной машины предоставляет API отладки, а не базовую ОС.
Как я понимаю:
Для программных точек останова на x86 отладчик заменяет первый байт инструкции на CC
( int3
). Это делается с помощью WriteProcessMemory
Windows. Когда ЦП получает эту инструкцию и выполняет ее int3
, это заставляет ЦП генерировать исключение отладки. ОС получает это прерывание, понимает, что процесс отлаживается, и уведомляет процесс отладчика о достижении точки останова.
После достижения точки останова и остановки процесса отладчик просматривает список точек останова и заменяет CC
его байтом, который был там изначально. Наборы отладчика TF
, ловушка Флаг в EFLAGS
(путем модификации CONTEXT
), и продолжает процесс. Флаг Trap заставляет ЦП автоматически генерировать одношаговое исключение ( INT 1
) для следующей инструкции.
Когда отлаживаемый процесс останавливается в следующий раз, отладчик снова заменяет первый байт инструкции точки останова CC
, и процесс продолжается.
Я не уверен, что это именно то, как это реализовано всеми отладчиками, но я написал программу для Win32, которая умеет отлаживать себя с помощью этого механизма. Совершенно бесполезно, но познавательно.
В Linux отладка процесса начинается с системного вызова ptrace (2) . Эта статья содержит большое руководство о том, как использовать ptrace
для реализации простых конструкций отладки.
(2)
нам что-то большее (или меньшее), чем «ptrace - системный вызов»?
(2)
номер раздела руководства. См. En.wikipedia.org/wiki/Man_page#Manual_sections для описания разделов руководства.
ptrace
это системный вызов.
(2)
говорит нам , что мы можем ввести man 2 ptrace
и получить нужную справочную страницу - здесь не важно , потому что нет никакого другого ptrace
к неоднозначности, но для сравнения man printf
с man 3 printf
на Linux.
Если вы работаете в операционной системе Windows, хорошим ресурсом для этого будет «Отладка приложений для Microsoft .NET и Microsoft Windows» Джона Роббинса:
(или даже более старая версия: «Отладка приложений» )
В книге есть глава о том, как работает отладчик, который включает код для пары простых (но работающих) отладчиков.
Поскольку я не знаком с деталями отладки Unix / Linux, этот материал может вообще не относиться к другим ОС. Но я предполагаю, что в качестве введения в очень сложную тему концепции - если не детали и API - должны «портировать» на большинство любых ОС.
Другим ценным источником для понимания отладки является руководство по процессору Intel (Руководство разработчика программного обеспечения Intel® 64 и IA-32 Architectures). В главе 3 тома 3А он представил аппаратную поддержку отладки, такую как специальные исключения и регистры аппаратной отладки. Вот из этой главы:
Флаг T (trap), TSS - Генерирует исключение отладки (#DB), когда делается попытка переключиться на задачу с флагом T, установленным в его TSS.
Я не уверен, использует ли Window или Linux этот флаг или нет, но очень интересно прочитать эту главу.
Надеюсь, это кому-нибудь поможет.
Я думаю, что здесь есть два основных вопроса:
1. Как отладчик знает, что произошло исключение?
Когда происходит исключение в отлаживаемом процессе, отладчик получает уведомление от ОС до того, как любые обработчики пользовательских исключений, определенные в целевом процессе, получат возможность ответить на исключение. Если отладчик решает не обрабатывать это (первое возможное) уведомление об исключении, последовательность диспетчеризации исключений продолжается дальше, и целевой поток затем получает возможность обработать исключение, если он хочет это сделать. Если исключение SEH не обрабатывается целевым процессом, отладчику затем отправляется другое событие отладки, называемое уведомлением второго шанса, чтобы сообщить ему, что в целевом процессе произошло необработанное исключение. Источник
2. Как отладчик знает, как остановиться на точке останова?
Упрощенный ответ : когда вы ставите точку останова в программе, отладчик заменяет ваш код в этой точке инструкцией int3, которая является программным прерыванием . В результате программа приостанавливается и вызывается отладчик.
Насколько я понимаю, когда вы компилируете приложение или файл DLL, все, что он компилирует, содержит символы, представляющие функции и переменные.
Когда у вас есть отладочная сборка, эти символы гораздо более подробные, чем при сборке релиза, что позволяет отладчику предоставлять вам больше информации. Когда вы присоединяете отладчик к процессу, он смотрит, к каким функциям в данный момент обращаются, и разрешает отсюда все доступные символы отладки (поскольку он знает, как выглядит внутренняя часть скомпилированного файла, он может определить, что может быть в памяти , с содержимым целых чисел, чисел с плавающей запятой, строк и т. д.). Как сказал первый автор, эта информация и то, как эти символы работают, сильно зависят от среды и языка.