LIMB
Вход Регистрация

тема

Процесс установки аппаратных точек останова через ptrace в arm64

zsmp0 · · 1 ответов · 805 просмотров

Недавно меня затронули вопросы, связанные с операциями установки аппаратных точек останова, при которых возникает ошибка и аварийный выход пользовательского процесса. Поэтому здесь мы по исходному коду вспомним, как зарегистрировать аппаратную точку останова на машине arm64.
Представьте, что вы отладчик (как GDB). Вы хотите следить за программой и остановить её выполнение, когда она:
  • Выполнит инструкцию по определённому адресу (breakpoint).​
  • Прочитает или запишет данные по определённому адресу (watchpoint).​
Для этого в процессорах ARM64 есть специальные отладочные регистры (debug registers). Вы, как отладчик, говорите ядру: "Эй, поставь точку останова вот тут!". Ядро настраивает эти регистры, а когда программа натыкается на точку, процессор генерирует исключение.

Задача ядра - перехватить это исключение и уведомить об этом отладчик.

Установка точки останова ​

Отладчик использует системный вызов 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
Сложно
👍1

Гостевой просмотр. Чтобы отвечать и ставить реакции, войдите или зарегистрируйтесь.