]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /********************************************************************** |
2 | proxy.c | |
3 | ||
4 | Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensing | |
5 | terms and conditions. | |
6 | ||
7 | Jeff Dike (jdike@karaya.com) : Modified for integration into uml | |
8 | **********************************************************************/ | |
9 | ||
10 | /* XXX This file shouldn't refer to CONFIG_* */ | |
11 | ||
12 | #include <errno.h> | |
13 | #include <stdio.h> | |
14 | #include <stdlib.h> | |
15 | #include <unistd.h> | |
16 | #include <signal.h> | |
17 | #include <string.h> | |
18 | #include <termios.h> | |
19 | #include <sys/wait.h> | |
20 | #include <sys/types.h> | |
21 | #include <sys/ioctl.h> | |
22 | #include <asm/unistd.h> | |
23 | #include "ptrace_user.h" | |
24 | ||
25 | #include "ptproxy.h" | |
26 | #include "sysdep.h" | |
27 | #include "wait.h" | |
28 | ||
1da177e4 LT |
29 | #include "user.h" |
30 | #include "os.h" | |
31 | #include "tempfile.h" | |
32 | ||
33 | static int debugger_wait(debugger_state *debugger, int *status, int options, | |
34 | int (*syscall)(debugger_state *debugger, pid_t child), | |
35 | int (*normal_return)(debugger_state *debugger, | |
36 | pid_t unused), | |
37 | int (*wait_return)(debugger_state *debugger, | |
38 | pid_t unused)) | |
39 | { | |
40 | if(debugger->real_wait){ | |
41 | debugger->handle_trace = normal_return; | |
42 | syscall_continue(debugger->pid); | |
43 | debugger->real_wait = 0; | |
44 | return(1); | |
45 | } | |
46 | debugger->wait_status_ptr = status; | |
47 | debugger->wait_options = options; | |
48 | if((debugger->debugee != NULL) && debugger->debugee->event){ | |
49 | syscall_continue(debugger->pid); | |
50 | wait_for_stop(debugger->pid, SIGTRAP, PTRACE_SYSCALL, | |
51 | NULL); | |
52 | (*wait_return)(debugger, -1); | |
53 | return(0); | |
54 | } | |
55 | else if(debugger->wait_options & WNOHANG){ | |
56 | syscall_cancel(debugger->pid, 0); | |
57 | debugger->handle_trace = syscall; | |
58 | return(0); | |
59 | } | |
60 | else { | |
61 | syscall_pause(debugger->pid); | |
62 | debugger->handle_trace = wait_return; | |
63 | debugger->waiting = 1; | |
64 | } | |
65 | return(1); | |
66 | } | |
67 | ||
68 | /* | |
69 | * Handle debugger trap, i.e. syscall. | |
70 | */ | |
71 | ||
72 | int debugger_syscall(debugger_state *debugger, pid_t child) | |
73 | { | |
74 | long arg1, arg2, arg3, arg4, arg5, result; | |
75 | int syscall, ret = 0; | |
76 | ||
77 | syscall = get_syscall(debugger->pid, &arg1, &arg2, &arg3, &arg4, | |
78 | &arg5); | |
79 | ||
80 | switch(syscall){ | |
81 | case __NR_execve: | |
82 | /* execve never returns */ | |
83 | debugger->handle_trace = debugger_syscall; | |
84 | break; | |
85 | ||
86 | case __NR_ptrace: | |
87 | if(debugger->debugee->pid != 0) arg2 = debugger->debugee->pid; | |
88 | if(!debugger->debugee->in_context) | |
89 | child = debugger->debugee->pid; | |
90 | result = proxy_ptrace(debugger, arg1, arg2, arg3, arg4, child, | |
91 | &ret); | |
92 | syscall_cancel(debugger->pid, result); | |
93 | debugger->handle_trace = debugger_syscall; | |
94 | return(ret); | |
95 | ||
96 | #ifdef __NR_waitpid | |
97 | case __NR_waitpid: | |
98 | #endif | |
99 | case __NR_wait4: | |
100 | if(!debugger_wait(debugger, (int *) arg2, arg3, | |
101 | debugger_syscall, debugger_normal_return, | |
102 | proxy_wait_return)) | |
103 | return(0); | |
104 | break; | |
105 | ||
106 | case __NR_kill: | |
107 | if(!debugger->debugee->in_context) | |
108 | child = debugger->debugee->pid; | |
109 | if(arg1 == debugger->debugee->pid){ | |
110 | result = kill(child, arg2); | |
111 | syscall_cancel(debugger->pid, result); | |
112 | debugger->handle_trace = debugger_syscall; | |
113 | return(0); | |
114 | } | |
115 | else debugger->handle_trace = debugger_normal_return; | |
116 | break; | |
117 | ||
118 | default: | |
119 | debugger->handle_trace = debugger_normal_return; | |
120 | } | |
121 | ||
122 | syscall_continue(debugger->pid); | |
123 | return(0); | |
124 | } | |
125 | ||
126 | /* Used by the tracing thread */ | |
127 | static debugger_state parent; | |
128 | static int parent_syscall(debugger_state *debugger, int pid); | |
129 | ||
130 | int init_parent_proxy(int pid) | |
131 | { | |
132 | parent = ((debugger_state) { .pid = pid, | |
133 | .wait_options = 0, | |
134 | .wait_status_ptr = NULL, | |
135 | .waiting = 0, | |
136 | .real_wait = 0, | |
137 | .expecting_child = 0, | |
138 | .handle_trace = parent_syscall, | |
139 | .debugee = NULL } ); | |
140 | return(0); | |
141 | } | |
142 | ||
143 | int parent_normal_return(debugger_state *debugger, pid_t unused) | |
144 | { | |
145 | debugger->handle_trace = parent_syscall; | |
146 | syscall_continue(debugger->pid); | |
147 | return(0); | |
148 | } | |
149 | ||
150 | static int parent_syscall(debugger_state *debugger, int pid) | |
151 | { | |
152 | long arg1, arg2, arg3, arg4, arg5; | |
153 | int syscall; | |
154 | ||
155 | syscall = get_syscall(pid, &arg1, &arg2, &arg3, &arg4, &arg5); | |
156 | ||
157 | if((syscall == __NR_wait4) | |
158 | #ifdef __NR_waitpid | |
159 | || (syscall == __NR_waitpid) | |
160 | #endif | |
161 | ){ | |
162 | debugger_wait(&parent, (int *) arg2, arg3, parent_syscall, | |
163 | parent_normal_return, parent_wait_return); | |
164 | } | |
165 | else ptrace(PTRACE_SYSCALL, pid, 0, 0); | |
166 | return(0); | |
167 | } | |
168 | ||
169 | int debugger_normal_return(debugger_state *debugger, pid_t unused) | |
170 | { | |
171 | debugger->handle_trace = debugger_syscall; | |
172 | syscall_continue(debugger->pid); | |
173 | return(0); | |
174 | } | |
175 | ||
176 | void debugger_cancelled_return(debugger_state *debugger, int result) | |
177 | { | |
178 | debugger->handle_trace = debugger_syscall; | |
179 | syscall_set_result(debugger->pid, result); | |
180 | syscall_continue(debugger->pid); | |
181 | } | |
182 | ||
183 | /* Used by the tracing thread */ | |
184 | static debugger_state debugger; | |
185 | static debugee_state debugee; | |
186 | ||
187 | void init_proxy (pid_t debugger_pid, int stopped, int status) | |
188 | { | |
189 | debugger.pid = debugger_pid; | |
190 | debugger.handle_trace = debugger_syscall; | |
191 | debugger.debugee = &debugee; | |
192 | debugger.waiting = 0; | |
193 | debugger.real_wait = 0; | |
194 | debugger.expecting_child = 0; | |
195 | ||
196 | debugee.pid = 0; | |
197 | debugee.traced = 0; | |
198 | debugee.stopped = stopped; | |
199 | debugee.event = 0; | |
200 | debugee.zombie = 0; | |
201 | debugee.died = 0; | |
202 | debugee.wait_status = status; | |
203 | debugee.in_context = 1; | |
204 | } | |
205 | ||
206 | int debugger_proxy(int status, int pid) | |
207 | { | |
208 | int ret = 0, sig; | |
209 | ||
210 | if(WIFSTOPPED(status)){ | |
211 | sig = WSTOPSIG(status); | |
212 | if (sig == SIGTRAP) | |
213 | ret = (*debugger.handle_trace)(&debugger, pid); | |
214 | ||
215 | else if(sig == SIGCHLD){ | |
216 | if(debugger.expecting_child){ | |
217 | ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig); | |
218 | debugger.expecting_child = 0; | |
219 | } | |
220 | else if(debugger.waiting) | |
221 | real_wait_return(&debugger); | |
222 | else { | |
223 | ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig); | |
224 | debugger.real_wait = 1; | |
225 | } | |
226 | } | |
227 | else ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig); | |
228 | } | |
229 | else if(WIFEXITED(status)){ | |
230 | tracer_panic("debugger (pid %d) exited with status %d", | |
231 | debugger.pid, WEXITSTATUS(status)); | |
232 | } | |
233 | else if(WIFSIGNALED(status)){ | |
234 | tracer_panic("debugger (pid %d) exited with signal %d", | |
235 | debugger.pid, WTERMSIG(status)); | |
236 | } | |
237 | else { | |
238 | tracer_panic("proxy got unknown status (0x%x) on debugger " | |
239 | "(pid %d)", status, debugger.pid); | |
240 | } | |
241 | return(ret); | |
242 | } | |
243 | ||
244 | void child_proxy(pid_t pid, int status) | |
245 | { | |
246 | debugee.event = 1; | |
247 | debugee.wait_status = status; | |
248 | ||
249 | if(WIFSTOPPED(status)){ | |
250 | debugee.stopped = 1; | |
251 | debugger.expecting_child = 1; | |
252 | kill(debugger.pid, SIGCHLD); | |
253 | } | |
254 | else if(WIFEXITED(status) || WIFSIGNALED(status)){ | |
255 | debugee.zombie = 1; | |
256 | debugger.expecting_child = 1; | |
257 | kill(debugger.pid, SIGCHLD); | |
258 | } | |
259 | else panic("proxy got unknown status (0x%x) on child (pid %d)", | |
260 | status, pid); | |
261 | } | |
262 | ||
263 | void debugger_parent_signal(int status, int pid) | |
264 | { | |
265 | int sig; | |
266 | ||
267 | if(WIFSTOPPED(status)){ | |
268 | sig = WSTOPSIG(status); | |
269 | if(sig == SIGTRAP) (*parent.handle_trace)(&parent, pid); | |
270 | else ptrace(PTRACE_SYSCALL, pid, 0, sig); | |
271 | } | |
272 | } | |
273 | ||
274 | void fake_child_exit(void) | |
275 | { | |
276 | int status, pid; | |
277 | ||
278 | child_proxy(1, W_EXITCODE(0, 0)); | |
279 | while(debugger.waiting == 1){ | |
280 | CATCH_EINTR(pid = waitpid(debugger.pid, &status, WUNTRACED)); | |
281 | if(pid != debugger.pid){ | |
282 | printk("fake_child_exit - waitpid failed, " | |
283 | "errno = %d\n", errno); | |
284 | return; | |
285 | } | |
286 | debugger_proxy(status, debugger.pid); | |
287 | } | |
288 | CATCH_EINTR(pid = waitpid(debugger.pid, &status, WUNTRACED)); | |
289 | if(pid != debugger.pid){ | |
290 | printk("fake_child_exit - waitpid failed, " | |
291 | "errno = %d\n", errno); | |
292 | return; | |
293 | } | |
294 | if(ptrace(PTRACE_DETACH, debugger.pid, 0, SIGCONT) < 0) | |
295 | printk("fake_child_exit - PTRACE_DETACH failed, errno = %d\n", | |
296 | errno); | |
297 | } | |
298 | ||
299 | char gdb_init_string[] = | |
300 | "att 1 \n\ | |
301 | b panic \n\ | |
302 | b stop \n\ | |
303 | handle SIGWINCH nostop noprint pass \n\ | |
304 | "; | |
305 | ||
306 | int start_debugger(char *prog, int startup, int stop, int *fd_out) | |
307 | { | |
308 | int slave, child; | |
309 | ||
310 | slave = open_gdb_chan(); | |
311 | child = fork(); | |
312 | if(child == 0){ | |
313 | char *tempname = NULL; | |
314 | int fd; | |
315 | ||
316 | if(setsid() < 0) perror("setsid"); | |
317 | if((dup2(slave, 0) < 0) || (dup2(slave, 1) < 0) || | |
318 | (dup2(slave, 2) < 0)){ | |
319 | printk("start_debugger : dup2 failed, errno = %d\n", | |
320 | errno); | |
321 | exit(1); | |
322 | } | |
323 | if(ioctl(0, TIOCSCTTY, 0) < 0){ | |
324 | printk("start_debugger : TIOCSCTTY failed, " | |
325 | "errno = %d\n", errno); | |
326 | exit(1); | |
327 | } | |
328 | if(tcsetpgrp (1, os_getpid()) < 0){ | |
329 | printk("start_debugger : tcsetpgrp failed, " | |
330 | "errno = %d\n", errno); | |
331 | #ifdef notdef | |
332 | exit(1); | |
333 | #endif | |
334 | } | |
335 | fd = make_tempfile("/tmp/gdb_init-XXXXXX", &tempname, 0); | |
336 | if(fd < 0){ | |
337 | printk("start_debugger : make_tempfile failed," | |
338 | "err = %d\n", -fd); | |
339 | exit(1); | |
340 | } | |
341 | os_write_file(fd, gdb_init_string, sizeof(gdb_init_string) - 1); | |
342 | if(startup){ | |
343 | if(stop){ | |
344 | os_write_file(fd, "b start_kernel\n", | |
345 | strlen("b start_kernel\n")); | |
346 | } | |
347 | os_write_file(fd, "c\n", strlen("c\n")); | |
348 | } | |
349 | if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){ | |
350 | printk("start_debugger : PTRACE_TRACEME failed, " | |
351 | "errno = %d\n", errno); | |
352 | exit(1); | |
353 | } | |
354 | execlp("gdb", "gdb", "--command", tempname, prog, NULL); | |
355 | printk("start_debugger : exec of gdb failed, errno = %d\n", | |
356 | errno); | |
357 | } | |
358 | if(child < 0){ | |
359 | printk("start_debugger : fork for gdb failed, errno = %d\n", | |
360 | errno); | |
361 | return(-1); | |
362 | } | |
363 | *fd_out = slave; | |
364 | return(child); | |
365 | } | |
366 | ||
367 | /* | |
368 | * Overrides for Emacs so that we follow Linus's tabbing style. | |
369 | * Emacs will notice this stuff at the end of the file and automatically | |
370 | * adjust the settings for this buffer only. This must remain at the end | |
371 | * of the file. | |
372 | * --------------------------------------------------------------------------- | |
373 | * Local variables: | |
374 | * c-file-style: "linux" | |
375 | * End: | |
376 | */ |