为何信号处理程序的首个参数通过ax寄存器而非di寄存器传递?
为什么信号处理程序的首个参数通过ax寄存器传递?
你看到的这段__setup_frame()和__setup_rt_frame()代码,是Linux内核为32位用户程序提供的信号帧设置逻辑,并非原生x86_64(64位)程序的处理流程,这就是和entry_64.S里64位入口寄存器描述不一致的核心原因:
32位兼容模式的特征:
- 代码中使用
VDSO32_SYMBOL,指向32位VDSO的符号; - 系统调用采用
int $0x80(32位系统调用入口),而非64位的syscall指令; - 寄存器操作采用32位命名(
ax、dx、cx),而非64位的rdi、rsi、rdx。
- 代码中使用
32位信号处理的参数约定:
针对32位x86程序,内核设置信号处理上下文时,会将信号编号sig放入ax寄存器,同时也会把sig写入栈帧的frame->sig字段——这是历史兼容设计,早期32位Linux信号处理的入口约定就是通过寄存器传递第一个参数,后续保留该逻辑以兼容旧程序。原生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
相关产品推荐
相关产品推荐

