下面我来分析 Linux 内核中 arch/arm64/kernel/debug-monitors.c 文件。这是 ARM64 架构下调试监视器(debug monitors)的核心实现文件。

文件概述

该文件实现了 ARM64 架构的调试异常处理机制,主要负责:

  • 断点(breakpoint)异常处理
  • 单步执行(single-step)控制
  • 调试监视器(debug monitor)的初始化和管理
  • 用户空间调试和内核调试的支持

关键数据结构

/* 每个 CPU 的调试状态 */
struct debug_info {
    int             enabled;      /* 调试是否启用 */
    int             bcr;          /* 断点控制寄存器 */
    int             bvr;          /* 断点值寄存器 */
    int             wcr;          /* 监视点控制寄存器 */
    int             wvr;          /* 监视点值寄存器 */
    u32             mdscr;        /* 监视器调试系统控制寄存器 */
};

/* 每个 CPU 的调试监视器状态 */
static DEFINE_PER_CPU(struct debug_info, debug_info);

核心函数分析

1. 调试异常处理入口

static int debug_exception_handler(struct pt_regs *regs, unsigned int esr)
{
    int ret = 0;
    unsigned int debug_esr = esr;
    
    /* 处理断点异常 */
    if (esr & ESR_ELx_EC_BRK) {
        ret = call_break_hook(regs, debug_esr);
        if (ret != DBG_HOOK_HANDLED)
            ret = call_user_break_hook(regs, debug_esr);
    }
    
    /* 处理单步异常 */
    if (esr & ESR_ELx_EC_SOFTSTP_LOW) {
        ret = call_step_hook(regs, debug_esr);
        if (ret != DBG_HOOK_HANDLED)
            ret = call_user_step_hook(regs, debug_esr);
    }
    
    return ret;
}

2. 断点注册机制

/* 断点钩子结构 */
struct break_hook {
    struct list_head node;
    int (*fn)(struct pt_regs *regs, unsigned int esr);
    u64 addr;
    u64 mask;
};

static LIST_HEAD(break_hooks);
static DEFINE_SPINLOCK(break_hook_lock);

/* 注册断点钩子 */
int register_break_hook(struct break_hook *hook)
{
    spin_lock(&break_hook_lock);
    list_add_tail(&hook->node, &break_hooks);
    spin_unlock(&break_hook_lock);
    return 0;
}

/* 调用断点钩子链 */
static int call_break_hook(struct pt_regs *regs, unsigned int esr)
{
    struct break_hook *hook;
    int ret = DBG_HOOK_NORMAL;
    
    spin_lock(&break_hook_lock);
    list_for_each_entry(hook, &break_hooks, node) {
        if ((regs->pc & hook->mask) == hook->addr) {
            ret = hook->fn(regs, esr);
            if (ret == DBG_HOOK_HANDLED)
                break;
        }
    }
    spin_unlock(&break_hook_lock);
    return ret;
}

3. 单步执行控制

/* 启用单步执行 */
void user_enable_single_step(struct task_struct *task)
{
    struct debug_info *info = &task->thread.debug;
    
    /* 设置 MDSCR 寄存器的 SS 位 */
    info->mdscr |= MDSCR_SS;
    set_tsk_thread_flag(task, TIF_SINGLESTEP);
}

/* 禁用单步执行 */
void user_disable_single_step(struct task_struct *task)
{
    struct debug_info *info = &task->thread.debug;
    
    info->mdscr &= ~MDSCR_SS;
    clear_tsk_thread_flag(task, TIF_SINGLESTEP);
}

/* 处理单步异常 */
static int call_step_hook(struct pt_regs *regs, unsigned int esr)
{
    struct step_hook *hook;
    int ret = DBG_HOOK_NORMAL;
    
    spin_lock(&step_hook_lock);
    list_for_each_entry(hook, &step_hooks, node) {
        ret = hook->fn(regs, esr);
        if (ret == DBG_HOOK_HANDLED)
            break;
    }
    spin_unlock(&step_hook_lock);
    return ret;
}

4. 内核调试支持

/* 内核断点处理 */
static int kernel_break_hook(struct pt_regs *regs, unsigned int esr)
{
    /* 检查是否在内核空间 */
    if (!user_mode(regs)) {
        /* 处理内核断点,如 KGDB */
        if (kgdb_handle_exception(1, SIGTRAP, 0, regs))
            return DBG_HOOK_HANDLED;
        
        /* 处理 oops 信息 */
        if (die("Breakpoint", regs, esr))
            return DBG_HOOK_HANDLED;
    }
    return DBG_HOOK_NORMAL;
}

/* 内核单步钩子 */
static int kernel_step_hook(struct pt_regs *regs, unsigned int esr)
{
    if (!user_mode(regs)) {
        if (kgdb_single_step(regs))
            return DBG_HOOK_HANDLED;
        
        if (die("Single step", regs, esr))
            return DBG_HOOK_HANDLED;
    }
    return DBG_HOOK_NORMAL;
}

5. 初始化函数

/* 调试监视器初始化 */
static int __init debug_monitors_init(void)
{
    /* 注册断点处理钩子 */
    register_break_hook(&kernel_break_hook);
    register_step_hook(&kernel_step_hook);
    
    /* 启用用户空间调试 */
    if (debug_user_access_enabled()) {
        /* 设置调试控制寄存器 */
        set_debug_regs();
        
        /* 启用 MDSCR 的 MDE 位 */
        enable_debug_monitors(DBG_ACTIVE_EL1);
    }
    
    return 0;
}
arch_initcall(debug_monitors_init);

关键特性

1. 异常级别支持

代码支持 EL0(用户空间)和 EL1(内核空间)的调试异常处理,通过 user_mode(regs) 区分。

2. 钩子机制

采用链表形式的钩子机制,允许不同子系统(如 KGDB、Kprobes、Perf)注册自己的调试处理函数。

3. 每 CPU 状态管理

使用 DEFINE_PER_CPU 维护每个 CPU 的调试状态,确保 SMP 系统下的正确性。

4. 调试寄存器管理

通过 debug_info 结构管理调试寄存器(DBGBVR、DBGBCR、DBGWVR、DBGWCR)的内容。

调试异常类型

根据 ESR(异常综合征寄存器)的值,可以区分以下调试异常:

  • 硬件断点ESR_ELx_EC_BRK
  • 硬件监视点ESR_ELx_EC_BRK(带特定标志)
  • 软件断点:通过 BRK 指令触发
  • 单步执行ESR_ELx_EC_SOFTSTP_LOW

使用场景

  1. GDB 调试:通过 ptrace 系统调用使用
  2. KGDB:内核源码级调试
  3. Kprobes:动态内核探测
  4. 性能监控:通过硬件断点收集性能数据

总结

debug-monitors.c 是 ARM64 调试架构的核心实现,通过统一的异常处理框架、灵活的钩子机制和精细的寄存器管理,为各种调试工具提供了底层支持。它的设计充分考虑了性能、安全性和可扩展性,是 ARM64 内核调试功能的基础。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐