概述
int __kprobes register_kprobe(struct kprobe *p)
{
int ret = 0;
struct kprobe *old_p;
struct module *probed_mod;
kprobe_opcode_t *addr;
//返回要探测的决定地址
addr = kprobe_addr(p);
if (IS_ERR(addr))
return PTR_ERR(addr);
p->addr = addr;
// 检查此地址是否已经注册过,不能重复注册
ret = check_kprobe_rereg(p);
if (ret)
return ret;
//检查地址合法性
//是否在代码段
//是否在kprobes 本身的代码段中,呵呵,不能监守自盗
//或者代码时候在黑名单中
if (!kernel_text_address((unsigned long) p->addr) ||
in_kprobes_functions((unsigned long) p->addr) ||
ftrace_text_reserved(p->addr, p->addr) ||
jump_label_text_reserved(p->addr, p->addr)) {
ret = -EINVAL;
goto cannot_probe;
}
// 平台相关注册(ARM)
// 每种平台的break,exception,trap等指令不一样,单步时候的回调函数
ret = arch_prepare_kprobe(p);
// 把break指令写入到探测的地址中
if (!kprobes_all_disarmed && !kprobe_disabled(p))
__arm_kprobe(p);
......
}
参考: kernel/Documentation/kprobes.txt 有如下一段话:
3. Specify either the kprobe "symbol_name" OR the "addr". If both are
specified, kprobe registration will fail with -EINVAL.
static kprobe_opcode_t __kprobes *kprobe_addr(struct kprobe *p)
{
kprobe_opcode_t *addr = p->addr;
//假如同时设置函数名和函数地址的话,注册kprobe会失败
//其实个人认为不太合理,尤其是支持KALLSYM的情况下
//应该两个都都设置的话,应该以函数名的优先级高
if ((p->symbol_name && p->addr) ||
(!p->symbol_name && !p->addr))
goto invalid;
if (p->symbol_name) {
// 参考 kallsyms.c
// 输入函数名,返回函数的地址
kprobe_lookup_name(p->symbol_name, addr);
if (!addr)
return ERR_PTR(-ENOENT);
}
//最终返回的地址,应该是函数地址加上指定的偏移量
addr = (kprobe_opcode_t *)(((char *)addr) + p->offset);
if (addr)
return addr;
invalid:
return ERR_PTR(-EINVAL);
}
// 在HASH数组中,得到该HASH值的表头
// 已函数地址为检测值,检查是否重复注册
struct kprobe __kprobes *get_kprobe(void *addr)
{
struct hlist_head *head;
struct hlist_node *node;
struct kprobe *p;
head = &kprobe_table[hash_ptr(addr, KPROBE_HASH_BITS)];
hlist_for_each_entry_rcu(p, node, head, hlist) {
if (p->addr == addr)
return p;
}
return NULL;
}
大概查找形式如下图:(mm slab 等地方都用到了此算法)
唯一的缺点就是当HASH值比较大的时候,会占用比较多的内存
太小的话,算法的时间复杂度又退化成list
// 判断地址是否在代码段
// 假如是数据段或者其它段的话,插入断点是毫无意义的,且会破坏数据
int core_kernel_text(unsigned long addr)
{
if (addr >= (unsigned long)_stext &&
addr <= (unsigned long)_etext)
return 1;
if (system_state == SYSTEM_BOOTING &&
init_kernel_text(addr))
return 1;
return 0;
}
//检测地址是否在kprobe自身中
static int __kprobes in_kprobes_functions(unsigned long addr)
{
struct kprobe_blackpoint *kb;
if (addr >= (unsigned long)__kprobes_text_start &&
addr < (unsigned long)__kprobes_text_end)
return -EINVAL;
for (kb = kprobe_blacklist; kb->name != NULL; kb++) {
if (kb->start_addr) {
if (addr >= kb->start_addr &&
addr < (kb->start_addr + kb->range))
return -EINVAL;
}
}
return 0;
}
// 检测地址在黑名单中,目前的黑名单如下:
static struct kprobe_blackpoint kprobe_blacklist[] = {
{"preempt_schedule",},
{"native_get_debugreg",},
{"irq_entries_start",},
{"common_interrupt",},
{"mcount",}, /* mcount can be called from everywhere */
{NULL} /* Terminator */
};
//在函数init_kprobes 会初始化每个黑名单函数的起始地址及地址范围
for (kb = kprobe_blacklist; kb->name != NULL; kb++) {
kprobe_lookup_name(kb->name, addr);
if (!addr)
continue;
kb->start_addr = (unsigned long)addr;
symbol_name = kallsyms_lookup(kb->start_addr,
&size, &offset, &modname, namebuf);
if (!symbol_name)
kb->range = 0;
else
kb->range = size;
}
最后
以上就是高高荷花为你收集整理的ARM架构kprobe应用及实现分析(8.0 register_kprobe实现)的全部内容,希望文章能够帮你解决ARM架构kprobe应用及实现分析(8.0 register_kprobe实现)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复