You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何信号处理程序的首个参数通过ax寄存器而非di寄存器传递?

为什么信号处理程序的首个参数通过ax寄存器传递?

你看到的这段__setup_frame()和__setup_rt_frame()代码,是Linux内核为32位用户程序提供的信号帧设置逻辑,并非原生x86_64(64位)程序的处理流程,这就是和entry_64.S里64位入口寄存器描述不一致的核心原因:

  1. 32位兼容模式的特征:

    • 代码中使用VDSO32_SYMBOL,指向32位VDSO的符号;
    • 系统调用采用int $0x80(32位系统调用入口),而非64位的syscall指令;
    • 寄存器操作采用32位命名(ax、dx、cx),而非64位的rdi、rsi、rdx。
  2. 32位信号处理的参数约定:
    针对32位x86程序,内核设置信号处理上下文时,会将信号编号sig放入ax寄存器,同时也会把sig写入栈帧的frame->sig字段——这是历史兼容设计,早期32位Linux信号处理的入口约定就是通过寄存器传递第一个参数,后续保留该逻辑以兼容旧程序。

  3. 原生64位程序的处理:
    原生x86_64程序的信号帧设置在专门的函数中(比如__setup_frame64或__setup_rt_frame64),这些函数严格遵循entry_64.S的约定,将第一个参数sig放入rdi寄存器,符合64位System V AMD64调用规范。


对应的代码实现(来自Linux v5.0内核signal.c)

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;
}

内容的提问来源于stack exchange,提问作者YuFeng Shen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 04:52:12