[4721cf1] | 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 | * |
---|
[981b99f] | 10 | * $Id$ |
---|
| 11 | * |
---|
[4721cf1] | 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 | } |
---|