c - 通过 setcontext 从信号处理程序返回

标签 c linux assembly signals x86-64

我正在尝试使用 SA_SIGINFO sigaction 的第三个参数直接跳转到中断的上下文。
这么想的:

void action(int Sig, siginfo_t *Info, void *Uctx) { 
    ucontext_t *uc = Uctx; setcontext(uc); 
}
将具有与以下相同的效果:
void action(int Sig, siginfo_t *Info, void *Uctx) { 
    return; 
}
但奇怪的是它接受三个信号(调用 setcontext-calling 处理程序),然后它出现段错误
setcontext :
Dump of assembler code for function setcontext:
   0x00007ffff7a34180 <+0>:     push   %rdi
   0x00007ffff7a34181 <+1>:     lea    0x128(%rdi),%rsi
   0x00007ffff7a34188 <+8>:     xor    %edx,%edx
   0x00007ffff7a3418a <+10>:    mov    $0x2,%edi
   0x00007ffff7a3418f <+15>:    mov    $0x8,%r10d
   0x00007ffff7a34195 <+21>:    mov    $0xe,%eax
   0x00007ffff7a3419a <+26>:    syscall
   0x00007ffff7a3419c <+28>:    pop    %rdi
   0x00007ffff7a3419d <+29>:    cmp    $0xfffffffffffff001,%rax
   0x00007ffff7a341a3 <+35>:    jae    0x7ffff7a34200 <setcontext+128>
   0x00007ffff7a341a5 <+37>:    mov    0xe0(%rdi),%rcx
--Type <RET> for more, q to quit, c to continue without paging--
   0x00007ffff7a341ac <+44>:    fldenv (%rcx)
=> 0x00007ffff7a341ae <+46>:    ldmxcsr 0x1c0(%rdi)
   0x00007ffff7a341b5 <+53>:    mov    0xa0(%rdi),%rsp
   0x00007ffff7a341bc <+60>:    mov    0x80(%rdi),%rbx
并且 strace 显示的故障地址为 0(可捕获的 SIGSEGV)。
这是一个使用计时器发送三个信号的示例程序:
#include <unistd.h>
#include <sys/time.h>
#include <ucontext.h>
#include <signal.h>
  
void action(int Sig, siginfo_t *Info, void *Uctx) { 
    ucontext_t *uc = Uctx; setcontext(uc); 
}
int main(void) {
    char ch[100];
    sigaction(SIGALRM, &(struct sigaction){.sa_sigaction = action, .sa_flags = SA_SIGINFO}, 0);
    setitimer(ITIMER_REAL, &(struct itimerval){.it_interval.tv_sec = 1,.it_value.tv_sec = 1}, 0);
    write(1, "enter\n", 6);
    for (;;) {
        write(1, "{\n", 2);
        read(0, &ch[0], sizeof(ch));
        write(1, "}\n", 2);
    }
}
在这种情况下发生了什么?

最佳答案

我认为这根本不适合工作:您应该只拨打 setcontext使用从 getcontext 获得的上下文或 makecontext ,而不是传递给信号处理程序的上下文。
The man page暗示这一点:

If the context was obtained by a call to a signal handler, then old standard text says that "program execution continues with the program instruction following the instruction interrupted by the signal". However, this sentence was removed in SUSv2, and the present verdict is "the result is unspecified".


另外,the glibc source of setcontext 有一个评论:

This implementation is intended to be used for synchronous context switches only. Therefore, it does not have to restore anything other than the PRESERVED state.


实际上,它不会尝试恢复任何浮点寄存器,而是将 rax 清零。 (至于 getcontext 返回 0)。这对于尝试恢复不期望其寄存器自发改变的代码来说是非常糟糕的。
用户空间中的抢占式多任务处理需要异步上下文切换。我认为这个想法是因为 pthreads 现在已经牢固建立,人们应该不需要这个,所以它不受支持。 getcontext/setcontext日期来自更早的时代,实际上已经从 POSIX 规范中删除,前提是应该使用 pthreads 来代替。

这个特定的崩溃似乎是由 struct ucontext_t 的内核布局之间的不匹配引起的。 ,以及 libc 所期望的。特别是,libc 期望浮点状态,包括 mxcsr 的保存值。 , 在 struct ucontext_t 内的特定偏移量处.然而,内核将浮点状态推送到堆栈上的一个单独位置(恰好与 libc 期望的位置重叠),并在 struct ucontext_t 中包含一个指向它的指针。 .所以libc的setcontext尝试将一些垃圾值加载到 mxcsr ,其中设置了一些保留位 16-31,这会导致一般保护故障。
然而,如上所述,这种不匹配是最少的问题。

关于c - 通过 setcontext 从信号处理程序返回,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69237910/

相关文章:

c - 在C中为单链表声明main函数中的变量

c - 从结构访问 union 成员

c - 将信息放入列表中

python - '!<' 过滤 Pandas 的语法错误

arrays - 在汇编程序中初始化数组

根据 Makefile 的说明选择不同的头文件

linux - Golang : What is etext?

linux - 有没有一种简单的方法可以在引导过程中向引导控制台写入内容?

0xFFE700DE >0xA 的汇编代码比较失败

assembly - 编写指令集模拟器的主要步骤是什么?