linux 为什么信号处理器的第一个参数由寄存器ax传递而不是di传递?

ojsjcaue  于 2023-05-06  发布在  Linux
关注(0)|答案(1)|浏览(141)

根据Registers on entry描述,signal handler的第一个参数sig应该由di传递。(rdi)寄存器,但是我看到传递给信号处理程序的第一个参数sig通过代码regs->ax = (unsigned long)sig而不是下面代码中预期的di分配给寄存器regs->ax,我想知道为什么信号处理程序的第一个参数是由寄存器ax传递的,而不是寄存器条目中描述的di?
下面的代码来自signal. c的__setup_frame()。

static int
__setup_frame(int sig, struct k_sigaction *ka, sigset_t *set,
          struct pt_regs *regs)
{
    struct sigframe __user *frame;
    void __user *restorer;
    int err = 0;
    void __user *fpstate = NULL;

    frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);

    if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
        return -EFAULT;

    if (__put_user(sig, &frame->sig))
        return -EFAULT;

    if (setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0]))
        return -EFAULT;

    if (_NSIG_WORDS > 1) {
        if (__copy_to_user(&frame->extramask, &set->sig[1],
                   sizeof(frame->extramask)))
            return -EFAULT;
    }

    if (current->mm->context.vdso)
        restorer = VDSO32_SYMBOL(current->mm->context.vdso, sigreturn);
    else
        restorer = &frame->retcode;
    if (ka->sa.sa_flags & SA_RESTORER)
        restorer = ka->sa.sa_restorer;

    /* Set up to return from userspace.  */
    err |= __put_user(restorer, &frame->pretcode);

    /*
     * This is popl %eax ; movl $__NR_sigreturn, %eax ; int $0x80
     *
     * WE DO NOT USE IT ANY MORE! It's only left here for historical
     * reasons and because gdb uses it as a signature to notice
     * signal handler stack frames.
     */
    err |= __put_user(*((u64 *)&retcode), (u64 *)frame->retcode);

    if (err)
        return -EFAULT;

    /* Set up registers for signal handler */
    regs->sp = (unsigned long)frame;
    regs->ip = (unsigned long)ka->sa.sa_handler;
    regs->ax = (unsigned long)sig;
    regs->dx = 0;
    regs->cx = 0;

    regs->ds = __USER_DS;
    regs->es = __USER_DS;
    regs->ss = __USER_DS;
    regs->cs = __USER_CS;

    return 0;
}

static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
                sigset_t *set, struct pt_regs *regs)
{
    struct rt_sigframe __user *frame;
    void __user *restorer;
    int err = 0;
    void __user *fpstate = NULL;

    frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);

    if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
        return -EFAULT;

    put_user_try {
        put_user_ex(sig, &frame->sig);
        put_user_ex(&frame->info, &frame->pinfo);
        put_user_ex(&frame->uc, &frame->puc);
        err |= copy_siginfo_to_user(&frame->info, info);

        /* Create the ucontext.  */
        if (cpu_has_xsave)
            put_user_ex(UC_FP_XSTATE, &frame->uc.uc_flags);
        else
            put_user_ex(0, &frame->uc.uc_flags);
        put_user_ex(0, &frame->uc.uc_link);
        put_user_ex(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
        put_user_ex(sas_ss_flags(regs->sp),
                &frame->uc.uc_stack.ss_flags);
        put_user_ex(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
        err |= setup_sigcontext(&frame->uc.uc_mcontext, fpstate,
                    regs, set->sig[0]);
        err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));

        /* Set up to return from userspace.  */
        restorer = VDSO32_SYMBOL(current->mm->context.vdso, rt_sigreturn);
        if (ka->sa.sa_flags & SA_RESTORER)
            restorer = ka->sa.sa_restorer;
        put_user_ex(restorer, &frame->pretcode);

        /*
         * This is movl $__NR_rt_sigreturn, %ax ; int $0x80
         *
         * WE DO NOT USE IT ANY MORE! It's only left here for historical
         * reasons and because gdb uses it as a signature to notice
         * signal handler stack frames.
         */
        put_user_ex(*((u64 *)&rt_retcode), (u64 *)frame->retcode);
    } put_user_catch(err);

    if (err)
        return -EFAULT;

    /* Set up registers for signal handler */
    regs->sp = (unsigned long)frame;
    regs->ip = (unsigned long)ka->sa.sa_handler;
    regs->ax = (unsigned long)sig;
    regs->dx = (unsigned long)&frame->info;
    regs->cx = (unsigned long)&frame->uc;

    regs->ds = __USER_DS;
    regs->es = __USER_DS;
    regs->ss = __USER_DS;
    regs->cs = __USER_CS;

    return 0;
}
hfwmuf9z

hfwmuf9z1#

您的代码段是信号代码的32位版本。如果你再往上看,你会发现它在#ifdef CONFIG_X86_32下面。(对int 0x80的引用也是一个提示,因为这是x86-32的系统调用机制,而x86-64使用syscall
在32位x86代码中,参数在堆栈上传递,因此信号号的实际传递是通过

if (__put_user(sig, &frame->sig))
        return -EFAULT;

如果查看struct sigframe的定义(已#defined为struct sigframe_ia32),您将看到sigpretcode之后的第二个元素。堆栈指针将指向struct sigframe,因此处理程序将返回地址视为堆栈的顶部元素,sig参数作为第二个元素,就在第一个参数所属的位置。
我不知道为什么信号号也放在eax中。正如您所说,用C编写的普通信号处理程序将忽略eax的内容,因此regs->ax = (unsigned long)sig看起来无用且多余。也许这是程序集编写的处理程序的另一种约定,或者是为了其他某种兼容性,或者只是一个错误。
对于x86-64(又名x86长模式),请在此文件中进一步查看,在#else下面。在那里,您将看到__setup_rt_frame执行regs->di = sig;,因此信号参数确实在rdi中。

相关问题