提交 1d299bc7 编写于 作者: D David S. Miller

sparc: Fix handling of orig_i0 wrt. debugging when restarting syscalls.

Although we provide a proper way for a debugger to control whether
syscall restart occurs, we run into problems because orig_i0 is not
saved and restored properly.

Luckily we can solve this problem without having to make debuggers
aware of the issue.  Across system calls, several registers are
considered volatile and can be safely clobbered.

Therefore we use the pt_regs save area of one of those registers, %g2,
as a place to save and restore orig_i0.

Debuggers transparently will do the right thing because they save and
restore this register already.
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
上级 9ff03b39
...@@ -822,21 +822,23 @@ static inline void syscall_restart32(unsigned long orig_i0, struct pt_regs *regs ...@@ -822,21 +822,23 @@ static inline void syscall_restart32(unsigned long orig_i0, struct pt_regs *regs
* want to handle. Thus you cannot kill init even with a SIGKILL even by * want to handle. Thus you cannot kill init even with a SIGKILL even by
* mistake. * mistake.
*/ */
void do_signal32(sigset_t *oldset, struct pt_regs * regs, void do_signal32(sigset_t *oldset, struct pt_regs * regs)
int restart_syscall, unsigned long orig_i0)
{ {
struct k_sigaction ka; struct k_sigaction ka;
unsigned long orig_i0;
int restart_syscall;
siginfo_t info; siginfo_t info;
int signr; int signr;
signr = get_signal_to_deliver(&info, &ka, regs, NULL); signr = get_signal_to_deliver(&info, &ka, regs, NULL);
/* If the debugger messes with the program counter, it clears restart_syscall = 0;
* the "in syscall" bit, directing us to not perform a syscall orig_i0 = 0;
* restart. if (pt_regs_is_syscall(regs) &&
*/ (regs->tstate & (TSTATE_XCARRY | TSTATE_ICARRY))) {
if (restart_syscall && !pt_regs_is_syscall(regs)) restart_syscall = 1;
restart_syscall = 0; orig_i0 = regs->u_regs[UREG_G2];
}
if (signr > 0) { if (signr > 0) {
if (restart_syscall) if (restart_syscall)
......
...@@ -519,10 +519,16 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0) ...@@ -519,10 +519,16 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
siginfo_t info; siginfo_t info;
int signr; int signr;
/* It's a lot of work and synchronization to add a new ptrace
* register for GDB to save and restore in order to get
* orig_i0 correct for syscall restarts when debugging.
*
* However, we luckily can use the fact that several registers
* are volatile across system calls. One such register is
* %g2, so use that as a place to save away orig_i0.
*/
if (pt_regs_is_syscall(regs) && (regs->psr & PSR_C)) if (pt_regs_is_syscall(regs) && (regs->psr & PSR_C))
restart_syscall = 1; regs->u_regs[UREG_G2] = orig_i0;
else
restart_syscall = 0;
if (test_thread_flag(TIF_RESTORE_SIGMASK)) if (test_thread_flag(TIF_RESTORE_SIGMASK))
oldset = &current->saved_sigmask; oldset = &current->saved_sigmask;
...@@ -535,8 +541,12 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0) ...@@ -535,8 +541,12 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
* the software "in syscall" bit, directing us to not perform * the software "in syscall" bit, directing us to not perform
* a syscall restart. * a syscall restart.
*/ */
if (restart_syscall && !pt_regs_is_syscall(regs)) restart_syscall = 0;
restart_syscall = 0; if (pt_regs_is_syscall(regs) && (regs->psr & PSR_C)) {
restart_syscall = 1;
orig_i0 = regs->u_regs[UREG_G2];
}
if (signr > 0) { if (signr > 0) {
if (restart_syscall) if (restart_syscall)
......
...@@ -529,11 +529,17 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0) ...@@ -529,11 +529,17 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
siginfo_t info; siginfo_t info;
int signr; int signr;
/* It's a lot of work and synchronization to add a new ptrace
* register for GDB to save and restore in order to get
* orig_i0 correct for syscall restarts when debugging.
*
* However, we luckily can use the fact that several registers
* are volatile across system calls. One such register is
* %g2, so use that as a place to save away orig_i0.
*/
if (pt_regs_is_syscall(regs) && if (pt_regs_is_syscall(regs) &&
(regs->tstate & (TSTATE_XCARRY | TSTATE_ICARRY))) { (regs->tstate & (TSTATE_XCARRY | TSTATE_ICARRY)))
restart_syscall = 1; regs->u_regs[UREG_G2] = orig_i0;
} else
restart_syscall = 0;
if (current_thread_info()->status & TS_RESTORE_SIGMASK) if (current_thread_info()->status & TS_RESTORE_SIGMASK)
oldset = &current->saved_sigmask; oldset = &current->saved_sigmask;
...@@ -542,22 +548,20 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0) ...@@ -542,22 +548,20 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0)
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
if (test_thread_flag(TIF_32BIT)) { if (test_thread_flag(TIF_32BIT)) {
extern void do_signal32(sigset_t *, struct pt_regs *, extern void do_signal32(sigset_t *, struct pt_regs *);
int restart_syscall, do_signal32(oldset, regs);
unsigned long orig_i0);
do_signal32(oldset, regs, restart_syscall, orig_i0);
return; return;
} }
#endif #endif
signr = get_signal_to_deliver(&info, &ka, regs, NULL); signr = get_signal_to_deliver(&info, &ka, regs, NULL);
/* If the debugger messes with the program counter, it clears restart_syscall = 0;
* the software "in syscall" bit, directing us to not perform if (pt_regs_is_syscall(regs) &&
* a syscall restart. (regs->tstate & (TSTATE_XCARRY | TSTATE_ICARRY))) {
*/ restart_syscall = 1;
if (restart_syscall && !pt_regs_is_syscall(regs)) orig_i0 = regs->u_regs[UREG_G2];
restart_syscall = 0; }
if (signr > 0) { if (signr > 0) {
if (restart_syscall) if (restart_syscall)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册