do_undefinstr分析

\linux-4.4.202\arch\arm\kernel\traps.c

do_undefinstr在__und_fault中被调用
__und_fault:
@纠正PC指针,使其指向
@导致故障的指令。如果发生故障的指令是ARM
@电脑将指向下一条指令,并且PC指针必须
@减去4。如果它是Thumb,PC指针将
@指向Thumb指令的后半部分。并且
@必须减去2。
ldr r2, [r0, #S_PC]
sub r2, r2, r1
str r2, [r0, #S_PC]
b do_undefinstr
ENDPROC(__und_fault)

asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
{
unsigned int instr;
siginfo_t info;
void __user *pc;

pc = (void __user *)instruction_pointer(regs);

if (processor_mode(regs) == SVC_MODE) {

#ifdef CONFIG_THUMB2_KERNEL
if (thumb_mode(regs)) {
instr = __mem_to_opcode_thumb16(((u16 *)pc)[0]);
if (is_wide_instruction(instr)) {
u16 inst2;
inst2 = __mem_to_opcode_thumb16(((u16 )pc)[1]);
instr = __opcode_thumb32_compose(instr, inst2);
}
} else
#endif
instr = __mem_to_opcode_arm(
(u32 *) pc);
} else if (thumb_mode(regs)) {
if (get_user(instr, (u16 __user *)pc))
goto die_sig;
instr = __mem_to_opcode_thumb16(instr);
if (is_wide_instruction(instr)) {
unsigned int instr2;
if (get_user(instr2, (u16 __user *)pc+1))
goto die_sig;
instr2 = __mem_to_opcode_thumb16(instr2);
instr = __opcode_thumb32_compose(instr, instr2);
}
} else {
if (get_user(instr, (u32 __user *)pc))
goto die_sig;
instr = __mem_to_opcode_arm(instr);
}

if (call_undef_hook(regs, instr) == 0)
	return;

die_sig:
#ifdef CONFIG_DEBUG_USER
if (user_debug & UDBG_UNDEFINED) {
pr_info(“%s (%d): undefined instruction: pc=%p\n”,
current->comm, task_pid_nr(current), pc);
__show_regs(regs);
dump_instr(KERN_INFO, regs);
}
#endif

info.si_signo = SIGILL;
info.si_errno = 0;
info.si_code  = ILL_ILLOPC;
info.si_addr  = pc;

arm_notify_die("Oops - undefined instruction", regs, &info, 0, 6);

}
NOKPROBE_SYMBOL(do_undefinstr)

get_user:

get_user()函数
//x:驱动程序中的变量;
//ptr:用户空间的地址;

#define get_user(x, ptr)
({
const void *__p = (ptr);
might_fault();
access_ok(VERIFY_READ, __p, sizeof(ptr)) ?
__get_user((x), (typeof(
(ptr)) )__p) :
((x) = (typeof(
(ptr)))0,-EFAULT);
})

理解方法和put_user()函数一样,不同的是数据的流向,get_user()函数是从用户空间ptr处读取sizeof(*ptr)个字节数据方法放到驱动程序的x变量中;

__mem_to_opcode_arm:将指定地址转换为操作码,可能会32位反转

call_undef_hook:
// 查找列表,对已经注册的函数,进行回调
static int call_undef_hook(struct pt_regs *regs, unsigned int instr)
{
struct undef_hook *hook;
unsigned long flags;
int (*fn)(struct pt_regs *regs, unsigned int instr) = NULL;
// 遍历list(hook),查看哪个注册的指令值与当前的一致
list_for_each_entry(hook, &undef_hook, node)
// instr 与注册的要一致的
// kgdb , ptrace , kprobe 的都不一致
if ((instr & hook->instr_mask) == hook->instr_val &&
(regs->ARM_cpsr & hook->cpsr_mask) == hook->cpsr_val)
//回调函数
fn = hook->fn;

return fn ? fn(regs, instr) : 1;  

}

list_for_each_entry应用:

它实际上是一个 for 循环,利用传入的 pos 作为循环变量,从表头 head 开始,逐项向后(next 方向)移动 pos,直至又回head(prefetch() 可以不考虑,用于预取以提高遍历速度 )。

在程序中的使用如下:

list_for_each_entry(pos , head,member)

{
………………
addr = pos; //对返回值pos的操作,这样更容易去理解list_for_each_entry,可以把它看作for()循环
………………
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

l531798151

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值