首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >对于涉及rip的指令,register_kprobe返回EINVAL (-22)错误

对于涉及rip的指令,register_kprobe返回EINVAL (-22)错误
EN

Stack Overflow用户
提问于 2018-01-10 17:43:11
回答 1查看 425关注 0票数 1

我正在尝试在内核模块的函数中使用kprobes在不同的指令处插入探针。

但是register_kprobe从下面的汇编代码返回0xffffffffa33c1085指令地址和0xffffffffa33c109b的EINVAL(-22)错误(它为所有其他指令地址传递)。

给出错误的说明:

代码语言:javascript
复制
0xffffffffa33c1085 <test_increment+5>:  mov    0x21bd(%rip),%eax        # 0xffffffffa33c3248
0xffffffffa33c109b <test_increment+27>: mov    %esi,0x21a7(%rip)        # 0xffffffffa33c3248

观察到这两个指令都使用了rip寄存器。尝试使用其他模块的功能,观察到与使用rip寄存器的指令相同的错误。

为什么register_kprobe会失败?它有没有涉及到rip的任何约束?任何帮助都是非常感谢的。

系统在x86_64上安装了内核3.10.0-514。

kprobe函数:

代码语言:javascript
复制
kp = kzalloc(sizeof(struct kprobe), GFP_KERNEL);
kp->post_handler = exit_func;
kp->pre_handler = entry_func;
kp->addr = sym_addr;
atomic_set(&pcount, 0);
ret = register_kprobe(kp);
if ( ret != 0 ) {
    printk(KERN_INFO "register_kprobe returned %d for %s\n", ret, str);
    kfree(kp);
    kp=NULL;
    return ret;
}

探测函数:

代码语言:javascript
复制
int race=0;
void test_increment()
{
    race++;
    printk(KERN_INFO "VALUE=%d\n",race);
    return;
}

汇编代码:

代码语言:javascript
复制
crash> dis -l test_increment
0xffffffffa33c1080 <test_increment>:    nopl   0x0(%rax,%rax,1) [FTRACE NOP]
0xffffffffa33c1085 <test_increment+5>:  mov    0x21bd(%rip),%eax        # 0xffffffffa33c3248
0xffffffffa33c108b <test_increment+11>: push   %rbp
0xffffffffa33c108c <test_increment+12>: mov    $0xffffffffa33c2024,%rdi
0xffffffffa33c1093 <test_increment+19>: mov    %rsp,%rbp
0xffffffffa33c1096 <test_increment+22>: lea    0x1(%rax),%esi
0xffffffffa33c1099 <test_increment+25>: xor    %eax,%eax
0xffffffffa33c109b <test_increment+27>: mov    %esi,0x21a7(%rip)        # 0xffffffffa33c3248
0xffffffffa33c10a1 <test_increment+33>: callq  0xffffffff81659552 <printk>
0xffffffffa33c10a6 <test_increment+38>: pop    %rbp
0xffffffffa33c10a7 <test_increment+39>: retq

谢谢

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-01-11 17:45:53

事实证明,register_kprobe在使用x86_64的rip相对寻址指令方面确实存在局限性。

以下是导致错误的__copy_instruction函数代码片段(register_kprobe -> prepare_kprobe -> arch_prepare_kprobe -> arch_copy_kprobe -> __copy_instruction )

代码语言:javascript
复制
#ifdef CONFIG_X86_64
    if (insn_rip_relative(&insn)) {
        s64 newdisp;
        u8 *disp;
        kernel_insn_init(&insn, dest);
        insn_get_displacement(&insn);
        /*
         * The copied instruction uses the %rip-relative addressing
         * mode.  Adjust the displacement for the difference between
         * the original location of this instruction and the location
         * of the copy that will actually be run.  The tricky bit here
         * is making sure that the sign extension happens correctly in
         * this calculation, since we need a signed 32-bit result to
         * be sign-extended to 64 bits when it's added to the %rip
         * value and yield the same 64-bit result that the sign-
         * extension of the original signed 32-bit displacement would
         * have given.
         */
        newdisp = (u8 *) src + (s64) insn.displacement.value - (u8 *) dest;
        if ((s64) (s32) newdisp != newdisp) {
            pr_err("Kprobes error: new displacement does not fit into s32 (%llx)\n", newdisp);
            pr_err("\tSrc: %p, Dest: %p, old disp: %x\n", src, dest, insn.displacement.value);
            return 0;
        }
        disp = (u8 *) dest + insn_offset_displacement(&insn);
        *(s32 *) disp = (s32) newdisp;
    }
#endif

http://elixir.free-electrons.com/linux/v3.10/ident/__copy_instruction

基于新的指令地址(其中orig insn被复制)来计算新的位移值。如果该值不适合32位,则返回0,从而导致EINVAL错误。这就是失败的原因。

作为一种变通方法,我们可以根据需要设置kprobe处理程序post上一条指令或pre next指令(对我有效)。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48184413

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档