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