Недавно меня затронули вопросы, связанные с операциями установки аппаратных точек останова, при которых возникает ошибка и аварийный выход пользовательского процесса. Поэтому здесь мы по исходному коду вспомним, как зарегистрировать аппаратную точку останова на машине arm64.
Представьте, что вы отладчик (как GDB). Вы хотите следить за программой и остановить её выполнение, когда она:
Задача ядра - перехватить это исключение и уведомить об этом отладчик.
Исходный код в kernel/ptrace.c показывает, что при фактическом вызове PTRACE_SETREGSET используется кроссплатформенная запись ptrace_regset:
Здесь task_user_regset_view возвращает информацию, связанную с архитектурой. Внутренне используется
Для регистров серии dbg определены
→
→
→
→
→
→
→
Таким образом, аппаратная точка останова в рамках процесса ptrace устанавливается. При срабатывании в пользовательском процессе автоматически вызывается
Трассировка обработки обратного вызова по исходному коду:
Начальная и конечная точки обратного вызова
Попадание на аппаратную точку останова при выполнении инструкции (breakpoint) или доступе к памяти (watchpoint). Точка входа исключения находится в обработчике прерываний entry.S, позиция el0_dbg (что соответствует отладочному исключению в пользовательском режиме). Цепочка вызовов:
Можно видеть, что интерфейс ptrace довольно удобно преобразует синхронные исключения набора инструкций в сигналы в трассируемом процессе, которые асинхронно обрабатываются tracer'ом. Довольно элегантно, обладает сильной кроссплатформенной универсальностью, хотя общая эффективность не очень высока. Если бы при обработке синхронного исключения вместо приостановки и отправки сигнала происходил непосредственный переход к обработчику на уровне пользовательского или kernel-mode в том же потоке, можно было бы достичь лучшей эффективности.
В настоящее время для решения проблемы гибкости этого обработчика можно использовать интерфейс Lua ядра, привязав к commonly used интерфейсам чтения регистров и ядра на C. Заинтересованные друзья могут следить за обновлениями.
Представьте, что вы отладчик (как GDB). Вы хотите следить за программой и остановить её выполнение, когда она:
- Выполнит инструкцию по определённому адресу (breakpoint).
- Прочитает или запишет данные по определённому адресу (watchpoint).
Задача ядра - перехватить это исключение и уведомить об этом отладчик.
Установка точки останова
Отладчик использует системный вызов ptrace с запросом PTRACE_SETREGSET. Это значит: "установи значения для набора регистров". В данном случае набор регистров — NT_ARM_HW_BREAK (аппаратные точки останова для ARM).Исходный код в kernel/ptrace.c показывает, что при фактическом вызове PTRACE_SETREGSET используется кроссплатформенная запись ptrace_regset:
C:
const struct user_regset_view *view = task_user_regset_view(task);
const struct user_regset *regset = find_regset(view, type);
Здесь task_user_regset_view возвращает информацию, связанную с архитектурой. Внутренне используется
user_aarch64_view, определённый в arch/arm64/ptrace.c, который содержит aarch64_regsets.Для регистров серии dbg определены
hw_break_get/hw_break_set для регистрации обратных вызовов точек останова. Основной поток вызовов:ptrace_hbp_set_addr→
ptrace_hbp_get_initialised_bp→
ptrace_hbp_create→
ptrace_breakpoint_init (создание новой структуры точки останова)→
register_user_hw_breakpoint (из hw_breakpoint.h, регистрация события точки останова, включая обратный вызов ptrace_hbptriggered)→
ptrace_hbp_set_event (запись в отладочные регистры потока)
C:
tsk->thread.debug.hbp_break[idx] = bp;
tsk->thread.debug.hbp_watch[idx] = bp;
→
modify_user_hw_breakpoint (из hw_breakpoint.h, управление изменением информации о точке останова)→
ptrace_hbp_set_ctrl (запись в регистр управления dbg)Таким образом, аппаратная точка останова в рамках процесса ptrace устанавливается. При срабатывании в пользовательском процессе автоматически вызывается
ptrace_hbptriggered для обработки сигнала. Далее посмотрим, как вызывается этот обратный вызов.Трассировка обработки обратного вызова по исходному коду:
Начальная и конечная точки обратного вызова
ptrace_hbptriggered:Попадание на аппаратную точку останова при выполнении инструкции (breakpoint) или доступе к памяти (watchpoint). Точка входа исключения находится в обработчике прерываний entry.S, позиция el0_dbg (что соответствует отладочному исключению в пользовательском режиме). Цепочка вызовов:
do_debug_exception,- Ключ 1: Получение информации о fault:
const struct fault_info *inf = debug_fault_info + DBG_ESR_EVT(esr); - Ключ 2: Точка входа функции обработки fault:
if (!inf->fn(addr_if_watchpoint, esr, regs)). В arch/arm64/hw_breakpoint.c это настроено с помощьюhook_debug_fault_code(DBG_ESR_EVT_HWBP, breakpoint_handler, SIGTRAP, TRAP_HWBKPT, "hw-breakpoint handler"); на использование обработчика breakpoint_handler.
→perf_bp_event(bp, regs)(точка входа для вызова события обратного вызова perf)
→ После множественных переходов достигается вызов __perf_event_overflow(event, throttle, data, regs))
→READ_ONCE(event->overflow_handler)(event, data, regs); (эквивалентно) здесь вызываетсяevent->overflow_handler, то есть зарегистрированная функцияptrace_hbptriggered. Ключевой момент:
Установка сигнала trap для пользовательского процесса, чтобы tracer мог его перехватить:force_sig_info(SIGTRAP, &info, current);. info содержит адрес срабатывания точки останова(void __user *)(bkpt->trigger)в качестве параметра триггера.
Можно видеть, что интерфейс ptrace довольно удобно преобразует синхронные исключения набора инструкций в сигналы в трассируемом процессе, которые асинхронно обрабатываются tracer'ом. Довольно элегантно, обладает сильной кроссплатформенной универсальностью, хотя общая эффективность не очень высока. Если бы при обработке синхронного исключения вместо приостановки и отправки сигнала происходил непосредственный переход к обработчику на уровне пользовательского или kernel-mode в том же потоке, можно было бы достичь лучшей эффективности.
В настоящее время для решения проблемы гибкости этого обработчика можно использовать интерфейс Lua ядра, привязав к commonly used интерфейсам чтения регистров и ядра на C. Заинтересованные друзья могут следить за обновлениями.
👍71
❤️9
🔥19
😁11
😢11
LIMB