1 | /* |
---|
2 | ********************************************************************** |
---|
3 | * |
---|
4 | * Component: RDBG |
---|
5 | * Module: servrpc.c |
---|
6 | * |
---|
7 | * Synopsis: support routines for RPC dispatch for remote debug server. |
---|
8 | * Main server dispatch routines from RPC to support remote debug. |
---|
9 | * |
---|
10 | * $Id$ |
---|
11 | * |
---|
12 | ********************************************************************** |
---|
13 | */ |
---|
14 | |
---|
15 | #include <string.h> |
---|
16 | #include <sys/errno.h> |
---|
17 | #include <rdbg/rdbg.h> |
---|
18 | #include <rdbg/remdeb.h> |
---|
19 | #include <rdbg/servrpc.h> |
---|
20 | |
---|
21 | /************************************************************************/ |
---|
22 | |
---|
23 | /* ----------------------------------------------------------------------- |
---|
24 | open_connex_2_svc - setup a new connection from a client. |
---|
25 | |
---|
26 | Notes: |
---|
27 | - this function creates a new connection to a client. It allocates |
---|
28 | an entry in the connection structure and fills in the information |
---|
29 | sent and implied by the message. |
---|
30 | - a client connection entry is needed for all further messages to work |
---|
31 | properly. |
---|
32 | ----------------------------------------------------------------------- */ |
---|
33 | |
---|
34 | open_out * |
---|
35 | RPCGENSRVNAME (open_connex_2_svc) (open_in * in, |
---|
36 | struct svc_req * rqstp) |
---|
37 | { |
---|
38 | static open_out out; /* output response. This could be heap local */ |
---|
39 | int idx; |
---|
40 | static int one_time = 0; /* we do one-time setup on back port */ |
---|
41 | |
---|
42 | /* |
---|
43 | * need to support in->debug_type, in->flags, and in->destination!!! |
---|
44 | */ |
---|
45 | |
---|
46 | if (!one_time) { /* only setup one backport socket */ |
---|
47 | /* |
---|
48 | * now setup signals and the like for handling process changes |
---|
49 | */ |
---|
50 | setErrno (0); |
---|
51 | TspInit (rqstp->rq_xprt->xp_sock); /* init transport system */ |
---|
52 | if (getErrno ()) { /* failed in setup */ |
---|
53 | out.port = (u_long) - 1; |
---|
54 | out.fp = getErrno (); /* error causing to fail */ |
---|
55 | return (&out); /* fail */ |
---|
56 | } |
---|
57 | one_time = True; /* disable doing this again */ |
---|
58 | } |
---|
59 | |
---|
60 | DPRINTF (("open_connex_2_svc: Opening connection from '%s'\n", |
---|
61 | in->user_name)); |
---|
62 | |
---|
63 | /* |
---|
64 | * now setup a validation of all other connections |
---|
65 | */ |
---|
66 | for (idx = 0; idx < conn_list_cnt; idx++) |
---|
67 | if (conn_list[idx].in_use) { /* setup retry timer */ |
---|
68 | DPRINTF (("open_connex_2_svc: Still have connection %d with port %d\n", |
---|
69 | idx, HL_W (*((UINT16 *) & conn_list[idx].back_port.c[2])))); |
---|
70 | } |
---|
71 | |
---|
72 | idx = ConnCreate (rqstp, in); /* setup the connection */ |
---|
73 | out.port = idx; /* connection number */ |
---|
74 | if (idx == -1) |
---|
75 | out.fp = getErrno (); /* error causing to fail */ |
---|
76 | else |
---|
77 | out.fp = TARGET_PROC_TYPE; |
---|
78 | |
---|
79 | out.server_vers = SERVER_VERS; |
---|
80 | return (&out); |
---|
81 | } |
---|
82 | |
---|
83 | /* ----------------------------------------------------------------------- |
---|
84 | send_signal_2_svc - send a kill/signal to the specified process. |
---|
85 | |
---|
86 | Notes: |
---|
87 | - this function sends a signal to the process specified. This process |
---|
88 | does not have to be under debug nor attached by this server. The kill |
---|
89 | may be refused on other grounds though. |
---|
90 | - kill(pid, 0) can be used to validate the process still exists if |
---|
91 | needed. |
---|
92 | ----------------------------------------------------------------------- */ |
---|
93 | |
---|
94 | signal_out * |
---|
95 | RPCGENSRVNAME (send_signal_2_svc) (signal_in * in, |
---|
96 | struct svc_req * rqstp) |
---|
97 | { |
---|
98 | static signal_out out; /* return code from kill */ |
---|
99 | |
---|
100 | /* |
---|
101 | * we do not care if connected |
---|
102 | */ |
---|
103 | setErrno (0); |
---|
104 | out.kill_return = 0; |
---|
105 | out.errNo = 0; |
---|
106 | TotalReboot = 1; |
---|
107 | return (&out); |
---|
108 | } |
---|
109 | |
---|
110 | /* ----------------------------------------------------------------------- |
---|
111 | close_connex_2_svc - close a connection from a client. |
---|
112 | ----------------------------------------------------------------------- */ |
---|
113 | |
---|
114 | void * |
---|
115 | RPCGENSRVNAME (close_connex_2_svc) (close_in * in, |
---|
116 | struct svc_req * rqstp) |
---|
117 | { |
---|
118 | int conn_idx = TspConnGetIndex (rqstp); |
---|
119 | |
---|
120 | if (conn_idx != -1) /* found it, clear out */ |
---|
121 | ConnDelete (conn_idx, rqstp, in->control); |
---|
122 | |
---|
123 | return (void *) ""; /* need to return something */ |
---|
124 | } |
---|
125 | |
---|
126 | /* ----------------------------------------------------------------------- |
---|
127 | ptrace_2_svc - control process under debug. |
---|
128 | ----------------------------------------------------------------------- */ |
---|
129 | |
---|
130 | #define REG_COUNT \ |
---|
131 | (sizeof (xdr_regs) / sizeof (int)) |
---|
132 | |
---|
133 | ptrace_out * |
---|
134 | RPCGENSRVNAME (ptrace_2_svc) (ptrace_in * in, |
---|
135 | struct svc_req * rqstp) |
---|
136 | { |
---|
137 | int conn_idx = rqstp ? TspConnGetIndex (rqstp) : -1; |
---|
138 | static ptrace_out out; /* outut response (error or data) */ |
---|
139 | void *addr, *addr2; /* used for actual ptrace call */ |
---|
140 | unsigned int data; |
---|
141 | int req, pid, ret, pid_idx, idx; |
---|
142 | static union { /* local buffer for returned data */ |
---|
143 | Objects_Id t_list[UTHREAD_MAX]; /* thread_list return */ |
---|
144 | char t_name[THREADNAMEMAX]; /* thread name return */ |
---|
145 | } local_buff; /* for return handling of strings and the like */ |
---|
146 | PID_LIST *plst = NULL; /* current pid_list entry */ |
---|
147 | |
---|
148 | DPRINTF (("ptrace_2_svc: entered (%s (%d), %d, XXXX, %d, XXXX)\n", |
---|
149 | PtraceName (in->addr.req), in->addr.req, in->pid, in->data)); |
---|
150 | |
---|
151 | out.addr.ptrace_addr_data_out_u.addr = 0; |
---|
152 | |
---|
153 | /* |
---|
154 | * validate the connection |
---|
155 | */ |
---|
156 | if (conn_idx == -1 && rqstp != NULL) { /* no connection, error */ |
---|
157 | DPRINTF (("ptrace_2_svc: msg from unknown debugger!\n")); |
---|
158 | out.result = -1; |
---|
159 | out.errNo = ECHILD; /* closest error */ |
---|
160 | out.addr.req = 0; /* to avoid copies that should not occur */ |
---|
161 | return (&out); |
---|
162 | } |
---|
163 | /* |
---|
164 | * Consider that the last back-message is acknowledged |
---|
165 | */ |
---|
166 | if (conn_idx >= 0 && conn_list[conn_idx].retry) { |
---|
167 | TspMessageReceive (conn_idx, in->pid); |
---|
168 | } |
---|
169 | |
---|
170 | req = in->addr.req; |
---|
171 | out.addr.req = req; /* needed for RPC */ |
---|
172 | pid = in->pid; |
---|
173 | addr = addr2 = NULL; |
---|
174 | data = in->data; |
---|
175 | setErrno (0); /* assume works */ |
---|
176 | out.result = 0; /* assume worked ok */ |
---|
177 | out.errNo = 0; |
---|
178 | |
---|
179 | /* |
---|
180 | * lookup process to make sure we have under control |
---|
181 | */ |
---|
182 | pid_idx = FindPidEntry (in->pid); |
---|
183 | if (pid_idx >= 0) { /* found it */ |
---|
184 | plst = &pid_list[pid_idx]; |
---|
185 | if (conn_idx < 0) |
---|
186 | conn_idx = plst->primary_conn; |
---|
187 | } |
---|
188 | |
---|
189 | /* |
---|
190 | * now we handle the special case of ATTACH to a pid we already control |
---|
191 | */ |
---|
192 | if (req == RPT_ATTACH) { /* look it up first */ |
---|
193 | if (plst) { /* we have controlled , so return ok+show conn */ |
---|
194 | ret = 2; /* normally secondary connection */ |
---|
195 | if (!PIDMAP_TEST (conn_idx, pid_idx)) { /* mark as an owner if not already */ |
---|
196 | plst->owners++; |
---|
197 | PIDMAP_SET (conn_idx, pid_idx); /* mask in */ |
---|
198 | } else if (plst->primary_conn != NO_PRIMARY) { /* regrab makes primary */ |
---|
199 | /* |
---|
200 | * Only if not primary already |
---|
201 | */ |
---|
202 | if (plst->primary_conn != conn_idx) { |
---|
203 | TspSendWaitChange (plst->primary_conn, BMSG_NOT_PRIM, conn_idx, plst->pid, 0, False); /* tell old owner */ |
---|
204 | } |
---|
205 | plst->primary_conn = NO_PRIMARY; |
---|
206 | } |
---|
207 | |
---|
208 | if (plst->primary_conn == NO_PRIMARY) { /* none now, so take over */ |
---|
209 | plst->primary_conn = conn_idx; /* new primary */ |
---|
210 | ret = 1; /* primary */ |
---|
211 | } |
---|
212 | out.result = ret; /* primary or secondary owner */ |
---|
213 | return (&out); |
---|
214 | } |
---|
215 | /* |
---|
216 | * else attach process using target code |
---|
217 | */ |
---|
218 | setErrno (ESRCH); /* assume the worst */ |
---|
219 | if (!TgtAttach (conn_idx, pid)) { /* failed */ |
---|
220 | out.errNo = getErrno (); |
---|
221 | out.result = 0; |
---|
222 | } |
---|
223 | return (&out); |
---|
224 | } else if (req == RPT_DETACH) { /* see which kind of detach */ |
---|
225 | if (data == PTRDET_UNOWN) { /* only want to disconnect from */ |
---|
226 | TgtDetachCon (conn_idx, pid_idx, True); /* remove from control */ |
---|
227 | return (&out); /* done */ |
---|
228 | } |
---|
229 | } else if (plst && (req == RPT_GETNAME || req == RPT_GETBREAK)) { |
---|
230 | /* |
---|
231 | * do nothing |
---|
232 | */ |
---|
233 | } |
---|
234 | |
---|
235 | else if (plst && req == RPT_CLRBREAK) { |
---|
236 | /* |
---|
237 | * To be able to remove breakpoints from a "running" system |
---|
238 | */ |
---|
239 | DPRINTF (("ptrace_2_svc: allowing RPT_CLRBREAK %d\n", data)); |
---|
240 | /* |
---|
241 | * do nothing |
---|
242 | */ |
---|
243 | } |
---|
244 | |
---|
245 | else if (plst && plst->running) { /* error, process is running and not detach */ |
---|
246 | out.result = -1; |
---|
247 | out.errNo = ETXTBSY; /* closest error */ |
---|
248 | DPRINTF (("ptrace_2_svc: failed, still running.\n")); |
---|
249 | return (&out); |
---|
250 | } |
---|
251 | if (plst == NULL) { |
---|
252 | out.result = -1; |
---|
253 | out.errNo = ESRCH; |
---|
254 | DPRINTF (("ptrace_2_svc: No such process.\n")); |
---|
255 | return (&out); |
---|
256 | } |
---|
257 | |
---|
258 | /* |
---|
259 | * now make sure secondary owner is not trying to modify |
---|
260 | */ |
---|
261 | if (!(in->flags & PTRFLG_NON_OWNER)) /* if not overriden */ |
---|
262 | if (conn_idx != plst->primary_conn |
---|
263 | && ((req >= RPT_POKETEXT && req <= RPT_SINGLESTEP) |
---|
264 | || (req >= RPT_SETREGS && req <= RPT_SETFPAREGS && (req & 1)) |
---|
265 | || (req >= RPT_SYSCALL && req <= RPT_DUMPCORE) |
---|
266 | || (req >= RPT_SETTARGETTHREAD && req <= RPT_THREADRESUME) |
---|
267 | || (req >= RPT_SETTHREADNAME && req <= RPT_SETTHREADREGS) |
---|
268 | || (req >= RPT_STEPRANGE && req <= RPT_CLRBREAK) |
---|
269 | || (req == RPT_STOP) |
---|
270 | || (req >= RPT_PSETREGS && req <= RPT_PSETTHREADREGS))) { /* not owner */ |
---|
271 | out.result = -1; |
---|
272 | out.errNo = EPERM; /* cannot alter as not primary */ |
---|
273 | DPRINTF (("ptrace_2_svc: refused, not owner, flags %d conn_idx %d primary_conn %d\n", in->flags, conn_idx, plst->primary_conn)); |
---|
274 | return (&out); |
---|
275 | } |
---|
276 | |
---|
277 | addr = (void *) in->addr.ptrace_addr_data_in_u.address; /* default */ |
---|
278 | /* |
---|
279 | * now setup normal ptrace request by unpacking. May execute here. |
---|
280 | */ |
---|
281 | switch (req) { /* handle unpacking or setup for real call */ |
---|
282 | /* |
---|
283 | * first the ones where addr points to input data |
---|
284 | */ |
---|
285 | case RPT_SETREGS: |
---|
286 | case RPT_SETTHREADREGS: |
---|
287 | addr = (void *) &in->addr.ptrace_addr_data_in_u.regs; /* reg list */ |
---|
288 | break; |
---|
289 | |
---|
290 | case RPT_PSETREGS: |
---|
291 | case RPT_PSETTHREADREGS: |
---|
292 | if (in->addr.ptrace_addr_data_in_u.pregs.pregs_len != REG_COUNT) { |
---|
293 | DPRINTF (("ptrace_2_svc: pid %d got %d expected %d\n", pid, |
---|
294 | in->addr.ptrace_addr_data_in_u.pregs.pregs_len, REG_COUNT)); |
---|
295 | setErrno (EINVAL); |
---|
296 | break; |
---|
297 | } |
---|
298 | req = req == RPT_PSETREGS ? RPT_SETREGS : RPT_SETTHREADREGS; |
---|
299 | addr = (void *) in->addr.ptrace_addr_data_in_u.pregs.pregs_val; |
---|
300 | break; |
---|
301 | |
---|
302 | case RPT_SETTHREADNAME: |
---|
303 | addr = (void *) in->addr.ptrace_addr_data_in_u.name; |
---|
304 | break; |
---|
305 | case RPT_WRITETEXT: |
---|
306 | case RPT_WRITEDATA: |
---|
307 | if ((int) data < 0) { |
---|
308 | setErrno (EINVAL); |
---|
309 | break; |
---|
310 | } |
---|
311 | addr = (void *) in->addr.ptrace_addr_data_in_u.mem.addr; /* targ addr */ |
---|
312 | addr2 = (void *) in->addr.ptrace_addr_data_in_u.mem.data; /* buff */ |
---|
313 | |
---|
314 | /* |
---|
315 | * Forbid writing over breakpoints |
---|
316 | */ |
---|
317 | if (BreakOverwrite (plst, addr, data)) { |
---|
318 | setErrno (EBUSY); |
---|
319 | } |
---|
320 | break; |
---|
321 | |
---|
322 | case RPT_POKETEXT: |
---|
323 | case RPT_POKEDATA: |
---|
324 | /* |
---|
325 | * Forbid writing over breakpoints |
---|
326 | */ |
---|
327 | if (BreakOverwrite (plst, addr, sizeof (int))) { |
---|
328 | setErrno (EBUSY); |
---|
329 | } |
---|
330 | break; |
---|
331 | |
---|
332 | /* |
---|
333 | * now ones where we handle locally |
---|
334 | */ |
---|
335 | case RPT_GETTARGETTHREAD: |
---|
336 | out.result = plst->thread; |
---|
337 | req = 0; /* force exit */ |
---|
338 | break; |
---|
339 | |
---|
340 | case RPT_PGETREGS: /* return from our buffer */ |
---|
341 | out.addr.ptrace_addr_data_out_u.pregs.pregs_len = REG_COUNT; |
---|
342 | out.addr.ptrace_addr_data_out_u.pregs.pregs_val = (u_int *) & plst->regs; |
---|
343 | req = 0; /* force exit */ |
---|
344 | break; |
---|
345 | |
---|
346 | case RPT_GETREGS: |
---|
347 | /* |
---|
348 | * return directly from our buffer |
---|
349 | */ |
---|
350 | /* |
---|
351 | * this buffer is refreshed when changing target thread |
---|
352 | */ |
---|
353 | out.addr.ptrace_addr_data_out_u.regs = plst->regs; |
---|
354 | req = 0; /* force exit */ |
---|
355 | break; |
---|
356 | |
---|
357 | case RPT_SETBREAK: |
---|
358 | idx = BreakSet (plst, conn_idx, &in->addr.ptrace_addr_data_in_u.breakp); |
---|
359 | if (idx < 0) |
---|
360 | break; |
---|
361 | req = 0; /* force exit */ |
---|
362 | out.result = idx; /* return break index (>0) */ |
---|
363 | break; |
---|
364 | |
---|
365 | case RPT_CLRBREAK: |
---|
366 | if (conn_list[conn_idx].flags & DEBUGGER_IS_GDB) { |
---|
367 | data = BreakGetIndex (plst, addr); |
---|
368 | } |
---|
369 | out.result = BreakClear (plst, conn_idx, data); |
---|
370 | /* |
---|
371 | * if errored, errno will still be set |
---|
372 | */ |
---|
373 | req = 0; |
---|
374 | break; |
---|
375 | |
---|
376 | case RPT_GETBREAK: |
---|
377 | /* |
---|
378 | * data=handle, addr=in_buffer, returns next break. Data=0, returns cnt |
---|
379 | */ |
---|
380 | out.result = BreakGet (plst, data, &out.addr. |
---|
381 | ptrace_addr_data_out_u.breakp); |
---|
382 | req = 0; /* handle locally */ |
---|
383 | break; |
---|
384 | |
---|
385 | case RPT_GETNAME: /* get the name of the process */ |
---|
386 | if (!plst->name) |
---|
387 | out.addr.ptrace_addr_data_out_u.mem.dataNb = 0; |
---|
388 | else { |
---|
389 | int maxLen = sizeof(out.addr.ptrace_addr_data_out_u.mem.data) - 1; |
---|
390 | data = strlen (plst->name); |
---|
391 | if (data > maxLen) |
---|
392 | data = maxLen; |
---|
393 | out.addr.ptrace_addr_data_out_u.mem.dataNb = data + 1; |
---|
394 | memcpy (out.addr.ptrace_addr_data_out_u.mem.data, plst->name, data + 1); |
---|
395 | out.addr.ptrace_addr_data_out_u.mem.data[maxLen] = '\0'; |
---|
396 | } |
---|
397 | req = 0; |
---|
398 | break; |
---|
399 | |
---|
400 | case RPT_CONTTO: |
---|
401 | if (BreakSetAt (plst, conn_idx, (u_long) addr, BRKT_STEPEMUL) < 0) { |
---|
402 | DPRINTF (("ptrace_2_svc: BreakSet failed at %x", addr)); |
---|
403 | break; |
---|
404 | } |
---|
405 | req = RPT_CONT; |
---|
406 | /* |
---|
407 | * data can contain a signal number, addr2 is unused |
---|
408 | */ |
---|
409 | goto case_RPT_CONT; |
---|
410 | |
---|
411 | case RPT_STEPRANGE: |
---|
412 | /* |
---|
413 | * convert to step |
---|
414 | */ |
---|
415 | if (!data) |
---|
416 | data = 1; /* should we give an error?? */ |
---|
417 | BreakStepRange (plst, addr, data); |
---|
418 | if (getErrno ()) |
---|
419 | break; |
---|
420 | |
---|
421 | req = RPT_SINGLESTEP; /* do by stepping */ |
---|
422 | addr = (void *) 1; /* start from current PC */ |
---|
423 | data = -2; /* want non-atomic stepping */ |
---|
424 | /* |
---|
425 | * fall through to other exec cases |
---|
426 | */ |
---|
427 | |
---|
428 | case RPT_CONT: |
---|
429 | case_RPT_CONT: |
---|
430 | case RPT_SINGLESTEP: |
---|
431 | |
---|
432 | if (BreakStepOff (plst, &addr2)) { /* need clear then step off break */ |
---|
433 | /* |
---|
434 | * clear break, step, then do exec |
---|
435 | */ |
---|
436 | if (addr == (void *) 1) |
---|
437 | addr = (void *) plst->regs.REG_PC; /* need for patch */ |
---|
438 | |
---|
439 | /* |
---|
440 | * data is always 0, so atomic single-step |
---|
441 | */ |
---|
442 | } else if (req == RPT_SINGLESTEP) { |
---|
443 | data = -2; /* want non-atomic stepping */ |
---|
444 | } |
---|
445 | break; |
---|
446 | |
---|
447 | /* |
---|
448 | * now ones where addr points to an output area |
---|
449 | */ |
---|
450 | case RPT_PGETTHREADREGS: |
---|
451 | addr = (void *) out.addr.ptrace_addr_data_out_u.mem.data; |
---|
452 | if (sizeof(out.addr.ptrace_addr_data_out_u.mem.data) < REG_COUNT * sizeof (int)) { |
---|
453 | setErrno (EINVAL); |
---|
454 | break; |
---|
455 | } |
---|
456 | if (data == plst->thread) { |
---|
457 | out.addr.ptrace_addr_data_out_u.pregs.pregs_len = REG_COUNT; |
---|
458 | out.addr.ptrace_addr_data_out_u.pregs.pregs_val = |
---|
459 | (u_int *) & plst->regs; |
---|
460 | req = 0; /* force exit */ |
---|
461 | break; |
---|
462 | } |
---|
463 | req = RPT_GETTHREADREGS; |
---|
464 | break; |
---|
465 | |
---|
466 | case RPT_GETTHREADREGS: |
---|
467 | addr = (void *) &out.addr.ptrace_addr_data_out_u.regs; |
---|
468 | break; |
---|
469 | case RPT_GETTHREADNAME: |
---|
470 | out.addr.ptrace_addr_data_out_u.name = local_buff.t_name; |
---|
471 | addr = (void *) out.addr.ptrace_addr_data_out_u.name; |
---|
472 | break; |
---|
473 | case RPT_THREADLIST: |
---|
474 | out.addr.ptrace_addr_data_out_u.threads.threads = |
---|
475 | (ptThreadList) local_buff.t_list; |
---|
476 | addr = (void *) out.addr.ptrace_addr_data_out_u.threads.threads; |
---|
477 | break; |
---|
478 | case RPT_READTEXT: |
---|
479 | case RPT_READDATA: |
---|
480 | if ((int) data < 0) { |
---|
481 | setErrno (EINVAL); |
---|
482 | break; |
---|
483 | } |
---|
484 | addr = (void *) in->addr.ptrace_addr_data_in_u.address; |
---|
485 | addr2 = (void *) out.addr.ptrace_addr_data_out_u.mem.data; |
---|
486 | out.addr.ptrace_addr_data_out_u.mem.dataNb = data; |
---|
487 | break; |
---|
488 | case RPT_DETACH: |
---|
489 | /* |
---|
490 | * Do not allow detaching if breakpoints still there |
---|
491 | */ |
---|
492 | if (BreakGet (plst, 0, NULL)) { /* some bkpts still set */ |
---|
493 | setErrno (EINVAL); /* cannot detach safely */ |
---|
494 | break; |
---|
495 | } |
---|
496 | /* |
---|
497 | * fall through |
---|
498 | */ |
---|
499 | case RPT_KILL: |
---|
500 | /* |
---|
501 | * in the event they are trying to detach or kill a terminated process, |
---|
502 | * we just delete the entry. |
---|
503 | */ |
---|
504 | if (PROC_TERMINATED (plst)) { |
---|
505 | TgtDelete (plst, -1, BMSG_KILLED); /* just blow off */ |
---|
506 | req = 0; /* now exit */ |
---|
507 | } |
---|
508 | break; |
---|
509 | } |
---|
510 | |
---|
511 | if (getErrno ()) { /* failed in code above */ |
---|
512 | out.result = -1; |
---|
513 | out.errNo = getErrno (); |
---|
514 | DPRINTF (("ptrace_2_svc: result %d errNo %d\n", out.result, out.errNo)); |
---|
515 | return (&out); |
---|
516 | } else if (!req) { /* bail out now */ |
---|
517 | DPRINTF (("ptrace_2_svc: result %d errNo %d\n", out.result, out.errNo)); |
---|
518 | return (&out); |
---|
519 | } |
---|
520 | |
---|
521 | /* |
---|
522 | * OK, make the call |
---|
523 | */ |
---|
524 | out.result = TgtPtrace (req, pid, addr, data, addr2); |
---|
525 | out.errNo = getErrno (); |
---|
526 | |
---|
527 | /* |
---|
528 | * if no error, cleanup afterwards |
---|
529 | */ |
---|
530 | if (getErrno ()) { |
---|
531 | /* |
---|
532 | * Remove step-emul breakpoints if any |
---|
533 | */ |
---|
534 | if (req == RPT_SINGLESTEP || req == RPT_CONT) { |
---|
535 | BreakClear (plst, -1, -1); |
---|
536 | } |
---|
537 | DPRINTF (("ptrace_2_svc: result %d errNo %d\n", out.result, out.errNo)); |
---|
538 | return (&out); /* return error */ |
---|
539 | } |
---|
540 | |
---|
541 | switch (in->addr.req) { /* handle some special calls that affect state */ |
---|
542 | case RPT_CONT: |
---|
543 | case RPT_STEPRANGE: |
---|
544 | /* |
---|
545 | * change to running |
---|
546 | */ |
---|
547 | if (in->addr.req == RPT_STEPRANGE) |
---|
548 | plst->last_start = LAST_RANGE; /* so range steps */ |
---|
549 | else if (addr2) |
---|
550 | plst->last_start = LAST_STEPOFF; /* now continue after wait */ |
---|
551 | else |
---|
552 | plst->last_start = LAST_CONT; |
---|
553 | plst->running = 1; /* mark as running */ |
---|
554 | if (!rqstp) /* Called internally to restart bkpt, no msg to anybody */ |
---|
555 | break; |
---|
556 | TgtNotifyAll (pid_idx, BMSG_WAIT, 0, 0, (in->flags & PTRFLG_NON_OWNER) |
---|
557 | ? -1 : conn_idx, True); |
---|
558 | break; |
---|
559 | case RPT_SINGLESTEP: |
---|
560 | /* |
---|
561 | * mark as step |
---|
562 | */ |
---|
563 | plst->last_start = LAST_STEP; /* so we know how started */ |
---|
564 | plst->running = 1; /* mark as running (wait should catch fast) */ |
---|
565 | break; |
---|
566 | case RPT_DETACH: /* mark as disconnected */ |
---|
567 | case RPT_KILL: /* mark as killed */ |
---|
568 | if (in->flags & PTRFLG_FREE) /* notify and delete entry */ |
---|
569 | TgtDelete (plst, -1, |
---|
570 | (in->addr.req == RPT_KILL) ? BMSG_KILLED : BMSG_DETACH); |
---|
571 | else { /* notify and mark */ |
---|
572 | plst->last_start = (in->addr.req == RPT_KILL) ? |
---|
573 | LAST_KILLED : LAST_DETACHED; |
---|
574 | plst->state = -1; |
---|
575 | plst->running = False; |
---|
576 | TgtNotifyAll (pid_idx, (in->addr.req == RPT_KILL) ? |
---|
577 | BMSG_KILLED : BMSG_DETACH, 0, 0, -1, True); |
---|
578 | } |
---|
579 | break; |
---|
580 | case RPT_SETTHREADREGS: |
---|
581 | case RPT_PSETTHREADREGS: |
---|
582 | if (data != plst->thread) |
---|
583 | break; |
---|
584 | DPRINTF (("ptrace_2_svc: pid %d target thread regs changed!\n", pid)); |
---|
585 | |
---|
586 | case RPT_SETREGS: |
---|
587 | case RPT_PSETREGS: |
---|
588 | /* |
---|
589 | * change our buffer as well |
---|
590 | */ |
---|
591 | if (plst->regs.REG_PC != ((xdr_regs *) addr)->REG_PC) |
---|
592 | BreakPcChanged (plst); |
---|
593 | plst->regs = *(xdr_regs *) addr; /* copy in */ |
---|
594 | break; |
---|
595 | |
---|
596 | /* |
---|
597 | * case RPT_PGETREGS has been handled locally above |
---|
598 | */ |
---|
599 | case RPT_PGETTHREADREGS: |
---|
600 | /* |
---|
601 | * We need to update pointer so that XDR works on return |
---|
602 | */ |
---|
603 | out.addr.ptrace_addr_data_out_u.pregs.pregs_len = REG_COUNT; |
---|
604 | out.addr.ptrace_addr_data_out_u.pregs.pregs_val = |
---|
605 | (void *) out.addr.ptrace_addr_data_out_u.mem.data; |
---|
606 | break; |
---|
607 | |
---|
608 | case RPT_PEEKTEXT: |
---|
609 | case RPT_PEEKDATA: |
---|
610 | case RPT_READDATA: |
---|
611 | case RPT_READTEXT: |
---|
612 | if (req < RPT_READDATA) { /* peek */ |
---|
613 | /* |
---|
614 | * addr is start |
---|
615 | */ |
---|
616 | data = sizeof (int); |
---|
617 | addr2 = &out.result; /* data buffer */ |
---|
618 | /* |
---|
619 | * Like read: addr is start, data is length, addr2 is buffer |
---|
620 | */ |
---|
621 | } |
---|
622 | BreakHide (plst, addr, data, addr2); |
---|
623 | break; |
---|
624 | |
---|
625 | case RPT_SETTARGETTHREAD: |
---|
626 | DPRINTF (("ptrace_2_svc: pid %d new target thread %d\n", pid, data)); |
---|
627 | TgtPtrace (RPT_GETREGS, pid, (char *) &plst->regs, 0, NULL); |
---|
628 | plst->thread = data; |
---|
629 | if (plst->break_list) { /* Forget we had to step off breakpoint */ |
---|
630 | BASE_BREAK *base = (BASE_BREAK *) plst->break_list; |
---|
631 | DPRINTF (("ptrace_2_svc: clr_step %d last_break %d\n", base->clr_step, |
---|
632 | base->last_break)); |
---|
633 | base->clr_step = 0; /* Not stopped on break */ |
---|
634 | base->last_break = 0; |
---|
635 | } |
---|
636 | break; |
---|
637 | |
---|
638 | case RPT_THREADLIST: |
---|
639 | out.addr.ptrace_addr_data_out_u.threads.nbThread = out.result; |
---|
640 | break; |
---|
641 | |
---|
642 | default: |
---|
643 | break; |
---|
644 | } /* end switch */ |
---|
645 | DPRINTF (("ptrace_2_svc 2: result %d errNo %d\n", out.result, out.errNo)); |
---|
646 | return (&out); |
---|
647 | } |
---|
648 | |
---|
649 | /* ----------------------------------------------------------------------- |
---|
650 | wait_info_2_svc - non-blocking wait request to check status. |
---|
651 | ----------------------------------------------------------------------- */ |
---|
652 | |
---|
653 | wait_out * |
---|
654 | RPCGENSRVNAME (wait_info_2_svc) (wait_in * in, struct svc_req *rqstp) |
---|
655 | { |
---|
656 | int conn_idx = TspConnGetIndex (rqstp); |
---|
657 | static wait_out out; /* output of pid and status */ |
---|
658 | int idx; |
---|
659 | PID_LIST *plst; |
---|
660 | |
---|
661 | memset (&out, 0, sizeof (out)); /* zero for safety */ |
---|
662 | out.reason = STOP_ERROR; /* assume the worst */ |
---|
663 | |
---|
664 | if (conn_idx == -1) { /* no connection, error */ |
---|
665 | DPRINTF (("wait_info_2_svc: msg from unknown debugger!\n")); |
---|
666 | out.wait_return = -1; |
---|
667 | out.errNo = ECHILD; /* closest error */ |
---|
668 | return (&out); |
---|
669 | } else { /* see if confirming message received */ |
---|
670 | if (conn_list[conn_idx].retry) |
---|
671 | TspMessageReceive (conn_idx, in->pid); |
---|
672 | } |
---|
673 | |
---|
674 | if (!in->pid) { /* warm test verify only */ |
---|
675 | /* |
---|
676 | * this call (pid==0) is made to confirm that that connection is still |
---|
677 | * active. |
---|
678 | */ |
---|
679 | /* |
---|
680 | * we let it fall through as an error since any use other than connection |
---|
681 | * reset would be an error (there is no pid0). |
---|
682 | */ |
---|
683 | } else { /* normal request */ |
---|
684 | idx = FindPidEntry (in->pid); |
---|
685 | if (idx >= 0) { /* found process they requested on */ |
---|
686 | plst = &pid_list[idx]; |
---|
687 | out.wait_return = plst->running ? 0 : in->pid; |
---|
688 | /* |
---|
689 | * return: 0 is running, pid is stopped/term |
---|
690 | */ |
---|
691 | out.errNo = 0; |
---|
692 | out.status = plst->state; /* last stopped reason if stopped */ |
---|
693 | out.thread = plst->thread; /* current thread (or -1 if none) from stop */ |
---|
694 | if (!out.wait_return) |
---|
695 | out.reason = STOP_NONE; /* running, no action */ |
---|
696 | else if (STS_SIGNALLED (out.status)) { /* stopped on signal */ |
---|
697 | out.handle = STS_GETSIG (out.status); /* signal number */ |
---|
698 | if (out.handle == SIGTRAP) |
---|
699 | if (plst->is_step) { /* single step with hitting a break */ |
---|
700 | out.reason = STOP_STEP; |
---|
701 | out.handle = 0; /* no information */ |
---|
702 | } else { /* stopped on break */ |
---|
703 | out.reason = STOP_BREAK; |
---|
704 | if (plst->break_list) |
---|
705 | out.handle = ((BASE_BREAK *) plst->break_list)->last_break; |
---|
706 | else |
---|
707 | out.handle = 0; /* no break */ |
---|
708 | } else |
---|
709 | out.reason = STOP_SIGNAL; |
---|
710 | out.PC = plst->regs.REG_PC; /* copy standard regs */ |
---|
711 | out.SP = plst->regs.REG_SP; |
---|
712 | out.FP = plst->regs.REG_FP; |
---|
713 | } else { /* terminated, so lower use count */ |
---|
714 | if (plst->last_start == LAST_KILLED) |
---|
715 | out.reason = STOP_KILLED; |
---|
716 | else if (plst->last_start == LAST_DETACHED) |
---|
717 | out.reason = STOP_DETACHED; |
---|
718 | else if (plst->last_start == LAST_START) { /* failed in exec */ |
---|
719 | out.reason = STOP_SPAWN_FAILED; |
---|
720 | out.handle = STS_GETCODE (out.status); /* errno reason */ |
---|
721 | } else if (STS_TERMONSIG (out.status)) { /* terminated on signal */ |
---|
722 | out.reason = STOP_TERM_SIG; |
---|
723 | /* |
---|
724 | * mask off the core-dumped bit 7 |
---|
725 | */ |
---|
726 | out.handle = (int) (unsigned) (u_char) STS_TERMGETSIG (out.status); |
---|
727 | } else { /* exit(2)ed */ |
---|
728 | out.reason = STOP_TERM_EXIT; |
---|
729 | out.handle = STS_GETCODE (out.status); /* code */ |
---|
730 | } |
---|
731 | } |
---|
732 | DPRINTF (("wait_info_2_svc: pid %d return %d status %x errNo %d" |
---|
733 | " reason %d handle %d pc %x sp %x fp %x thread %d\n", |
---|
734 | in->pid, out.wait_return, out.status, out.errNo, out.reason, |
---|
735 | out.handle, out.PC, out.SP, out.FP, out.thread)); |
---|
736 | return (&out); |
---|
737 | } |
---|
738 | } |
---|
739 | /* |
---|
740 | * if not found in list, we return error: no such process |
---|
741 | */ |
---|
742 | out.wait_return = -1; |
---|
743 | out.errNo = ESRCH; /* no process */ |
---|
744 | out.status = 0; |
---|
745 | return (&out); |
---|
746 | } |
---|
747 | |
---|
748 | /* ----------------------------------------------------------------------- |
---|
749 | get_signal_names_2_svc - return names for signals |
---|
750 | ----------------------------------------------------------------------- */ |
---|
751 | |
---|
752 | static one_signal SignalNames[] = { |
---|
753 | {SIGILL, "SIGILL/EVT_ILL"}, |
---|
754 | {SIGTRAP, "SIGTRAP/EVT_BKPT"}, |
---|
755 | {SIGFPE, "SIGFPE/EVT_FPE"}, |
---|
756 | {SIGKILL, "SIGKILL/EVT_AKILL"}, |
---|
757 | {SIGSEGV, "SIGSEGV/EVT_SEGV"}, |
---|
758 | {17, "SIGSTOP"}, |
---|
759 | {23, "SIGSTOP"} |
---|
760 | }; |
---|
761 | |
---|
762 | get_signal_names_out * |
---|
763 | RPCGENSRVNAME (get_signal_names_2_svc) (void * in, struct svc_req * rqstp) |
---|
764 | { |
---|
765 | static get_signal_names_out out; |
---|
766 | |
---|
767 | out.signals.all_signals_len = sizeof (SignalNames) / sizeof (SignalNames[0]); |
---|
768 | out.signals.all_signals_val = SignalNames; |
---|
769 | |
---|
770 | return (&out); |
---|
771 | } |
---|