【发布时间】:2021-03-29 14:20:19
【问题描述】:
我想在我的内核模块中使用register_wide_hw_breakpoint 来观察我正在使用的内存页面的页面结构的变化(用于调试目的)。
但是,似乎我只能在模块初始化函数中注册断点。如果我从 ioctl 处理程序中使用该函数,则该函数返回 -1(EPERM?)。 This blogpost 让我认为这应该是可能的。
我在 Intel(R) Xeon(R) Silver 4215 上运行 5.1.0 内核。
示例代码:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>
#include <linux/kallsyms.h>
static uint32_t val = 0;
void inc_val(void)
{
val++;
}
struct perf_event * __percpu *sample_hbp;
static void sample_hbp_handler(struct perf_event *bp, struct perf_sample_data *data, struct pt_regs *regs)
{
pr_info("My module: val changed!");
}
int test_hw_breakpoint(void)
{
struct perf_event_attr attr;
hw_breakpoint_init(&attr);
attr.bp_addr = (unsigned long)&val;
attr.bp_len = HW_BREAKPOINT_LEN_4;
attr.bp_type = HW_BREAKPOINT_W;
pr_info("My module: HW breakpoint at 0x%llx\n", attr.bp_addr);
sample_hbp = register_wide_hw_breakpoint(&attr, sample_hbp_handler, NULL);
if (IS_ERR((void __force *)sample_hbp))
{
int ret = PTR_ERR((void __force *)sample_hbp);
pr_info("My module: Breakpoint registration failed: %d\n", ret);
return ret;
}
inc_val();
pr_info("My module: val: %d", val);
inc_val();
pr_info("My module: val: %d", val);
unregister_wide_hw_breakpoint(sample_hbp);
return 0;
}
static int mod_open(struct inode *inode, struct file *file)
{
(void)inode;
(void)file;
pr_info("My module: Opening...\n");
return 0;
}
static int mod_release(struct inode *inode, struct file *file)
{
(void)inode;
(void)file;
pr_info("My module: Releasing...\n");
return 0;
}
static long mod_ioctl(struct file *file, unsigned num, uintptr_t param)
{
(void)file;
(void)num;
(void)param;
pr_info("My module: Ioctl...\n");
test_hw_breakpoint();
return 0;
}
static struct file_operations fops = {
.open = mod_open,
.release = mod_release,
.unlocked_ioctl = mod_ioctl,
};
static struct miscdevice mod_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "mymod!io",
.fops = &fops,
.mode = 0666,
};
int mod_init(void)
{
int ret;
ret = misc_register(&mod_dev);
if (ret != 0)
return ret;
test_hw_breakpoint();
return 0;
}
void mod_exit(void)
{
misc_deregister(&mod_dev);
}
static int __init od_init(void)
{
int ret;
pr_info("My module: Initializing...\n");
ret = mod_init();
if (ret != 0)
return -1;
return 0;
}
static void __exit od_exit(void)
{
pr_info("My module: Terminating...\n");
mod_exit();
}
module_init(od_init)
module_exit(od_exit)
MODULE_LICENSE("GPL");
在 dmesg 中产生以下输出:
[ +0.031269] My module: Initializing...
[ +0.000086] My module: HW breakpoint at 0xffffffffc04ae4c8
[ +0.000191] My module: val changed!
[ +0.000002] My module: val: 1
[ +0.000003] My module: val changed!
[ +0.000001] My module: val: 2
[ +0.002405] My module: Opening...
[ +0.000003] My module: Ioctl...
[ +0.000003] My module: HW breakpoint at 0xffffffffc04ae4c8
[ +0.000019] My module: Breakpoint registration failed: -1
[ +0.000003] My module: Releasing...
我不知道在模块初始化时要观察的内存地址。有什么方法可以在运行时添加数据断点?
【问题讨论】:
-
调用
ioctl的进程可能没有设置硬件断点的权限。尝试使用sudo运行它。 -
嗨@IanAbbott,感谢您的提示!用 sudo 运行调用进程确实解决了这个问题。我不明白为什么。我希望在切换到内核上下文时,调用者的权限不再重要。你能详细说明一下吗?如果您在答案中这样做,我会将其标记为解决方案:)
标签: linux-kernel x86-64 breakpoints kernel-module