我想更改程序计数器(指令指针)的内容。我想通过覆盖C中的系统信号处理程序,我可以在系统堆栈帧中获取指针。从那里我可以得到函数的返回地址并进行更改。
但是,我在堆栈中得到了指针,但我不确切知道返回地址存储在堆栈帧的确切位置。
void signal_handler(int signal){
char* ptr = (char*) & signal;
// As, signal is stored on the paramter list of the stack,
// I get the address in the current stack frame. From , here
// I want to change the return address(that is stored in the
// current stack frame).
}
答案 0 :(得分:3)
在段错之后,程序处于不确定状态。您不能再依赖具有正确值的任何内容(例如寄存器)。
即使你能够在故障后返回到下一条指令,除非你的信号处理程序反汇编违规指令[在上下文中],并更改寄存器值以进行补偿,你现在已经得到了一个程序这是不值得信任的,并且可能继续以更加灾难性的结果进行分段或[更糟糕的]操作(例如错误的文件上的unlink
等)。
但是,由于信号“trampoline”,你无法这样做。有关详细信息,请参见sigreturn
手册页。给你的信号处理程序的堆栈帧不一定是正常的。
您必须将程序恢复到已知的“安全”状态。唯一的方法是[正如我在上面的评论中提到的]设置一个恢复点sigsetjmp
并在信号处理程序中执行siglongjmp
。 附注:使用这些类似于在C++
中使用例外情况,但您必须手动完成更多工作。
我已经完成了大量具有恢复功能的segfault捕获信号处理程序,但它们都涉及使用sigsetjmp/siglongjmp
。
这也引出了一个问题:为什么不调试你的程序,以便它首先不会出现段错误?您有什么特殊需要排除这种情况?
答案 1 :(得分:0)
如果只是想要跳转到某个地址,而没有为您要跳转到的上下文设置堆栈,或者从您要离开的上下文中清除它,则可以使用GNU C computed-goto。
void *ptr;
/* ... */
ptr = &&foo;
// then somewhere else
goto *ptr;
据我了解,这几乎相当于
asm volatile("jmp *ptr"); // AT&T syntax for memory-indirect jump
尝试从段错误中恢复的正确方法是to use setjmp
/ longjmp
。 强烈建议不要在此时做很多事情。使用此功能可以打印自定义消息,记录某些内容并退出。如果您的代码导致了段错误,那么在尝试访问一些不允许的内存之前,它可能会对您的某些程序数据进行潦草地写入。这甚至可能破坏了堆栈上的返回地址。
即使asm指令将1:1映射到代码行(它们没有),如果跳过一行代码,大多数程序都会崩溃。更改一点,如果您尝试恢复执行的数据或代码存在问题,您将只会出现段错误。