我是靠谱客的博主 高高荷花,最近开发中收集的这篇文章主要介绍ARM架构kprobe应用及实现分析(8.0 register_kprobe实现),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

 

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实现)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(35)

评论列表共有 0 条评论

立即
投稿
返回
顶部