source: vendor/emx/current/src/os2/ptrace.c

Last change on this file was 18, checked in by bird, 22 years ago

Initial revision

  • Property cvs2svn:cvs-rev set to 1.1
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 44.0 KB
Line 
1/* ptrace.c -- Implement ptrace()
2 Copyright (c) 1994-1996 by Eberhard Mattes
3
4This file is part of emx.
5
6emx is free software; you can redistribute it and/or modify it
7under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11emx is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with emx; see the file COPYING. If not, write to
18the Free Software Foundation, 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA.
20
21As special exception, emx.dll can be distributed without source code
22unless it has been changed. If you modify emx.dll, this exception
23no longer applies and you must remove this paragraph from all source
24files for emx.dll. */
25
26
27#define INCL_DOSPROCESS
28#define INCL_DOSSESMGR
29#define INCL_DOSEXCEPTIONS
30#define INCL_DOSMODULEMGR
31#define INCL_DOSSEMAPHORES
32#define INCL_DOSERRORS
33#include <os2emx.h>
34#include "clib.h"
35#include <sys/signal.h>
36#include <sys/ptrace.h>
37#include <sys/user.h>
38#include "reg.h"
39#include <sys/errno.h>
40#include <sys/uflags.h>
41#include "emxdll.h"
42
43#define DR_SERVER_HEV_CLIENT 0x0001
44#define DR_SERVER_HEV_SERVER 0x0002
45#define DR_CLIENT_HEV_CLIENT 0x0004
46#define DR_CLIENT_HEV_SERVER 0x0008
47#define DR_LIST 0x0010
48
49#define SHAREMEM_FMT "/sharemem/emx/ptrace/%.8x.atc"
50
51#define REQ_ATTACH 17
52#define REQ_DOSDEBUG 18
53#define REQ_DETACH 19
54
55#define USEROFF(F) offsetof (struct user, F)
56#define DBGOFF(F) offsetof (uDB_t, F)
57#define CTXOFF(F) offsetof (CONTEXTRECORD, F)
58
59#define PTRACE_REG_COUNT sizeof (ptrace_regs) / sizeof (ptrace_regs[0])
60
61struct reg_table
62{
63 ULONG reg;
64 ULONG user_offset;
65 ULONG dbg_offset;
66 ULONG ctx_offset;
67 BYTE len;
68};
69
70struct co_regs
71{
72 USHORT cw;
73 USHORT unused1;
74 USHORT sw;
75 USHORT unused2;
76 USHORT tw;
77 USHORT unused3;
78 ULONG fip;
79 USHORT fcs;
80 USHORT fop;
81 ULONG foo;
82 USHORT fos;
83 USHORT unused4;
84 BYTE fst[8][10];
85};
86
87enum dbg_type
88{
89 DT_LOCAL,
90 DT_SERVER,
91 DT_CLIENT
92};
93
94struct debuggee
95{
96 struct debuggee *next; /* Next debuggee */
97 enum dbg_type type; /* Type of debuggee */
98 ULONG resources; /* Acquired resources: DR_*. */
99 ULONG pid; /* Process ID */
100 ULONG sid; /* Session ID */
101 ULONG syscall_addr; /* Address of CALL _emx_syscall, or 0 */
102 ULONG layout_addr; /* Address of layout table, or 0 */
103 ULONG signo; /* Signal number, or 0 */
104 ULONG context; /* Address of exception context record, or 0 */
105 ULONG last_cmd; /* Last command */
106 ULONG regs[19]; /* Current registers */
107 ULONG exc_regs[19]; /* Registers for exception handler */
108 uDB_t dbgbuf; /* Buffer for DosDebug, includes registers */
109 struct co_regs dbgco; /* Floating point status */
110
111 ULONG ptn; /* Notification for debugger */
112 ULONG ptn_hmte; /* For PTN_MODULE_LOAD and PTN_MODULE_FREE */
113 ULONG ptn_tid; /* For PTN_THREAD_NEW and PTN_THREAD_END */
114 ULONG ptn_pid; /* For PTN_PROC_NEW */
115 ULONG ptn_flags; /* For PTN_PROC_NEW */
116 ULONG ptn_fork_addr; /* For PTN_PROC_NEW */
117 char ptn_name[260]; /* For PTN_PROC_NEW */
118
119 ULONG n_cmd; /* run() interrupted by notification: CMD */
120 ULONG n_tbreak; /* Ditto, TBREAK */
121 ULONG n_signo; /* Ditto, SIGNO */
122 ULONG n_tid; /* Ditto, TID */
123
124 ULONG regs_tid; /* Registers retrieved for this thread */
125 ULONG server_request; /* Request for DosDebug server */
126 ULONG server_rc; /* Return code of DosDebug on server */
127 ULONG server_arg; /* Argument passed to server */
128 HEV hev_server; /* Data ready for DosDebug server */
129 HEV hev_client; /* Data ready for DosDebug client */
130
131 BYTE regs_changed; /* Registers changed */
132 BYTE fpregs_ok; /* Floating point registers retrieved */
133 BYTE auto_switch; /* Automatic session switching enabled */
134 BYTE brk_flag; /* Break point hit */
135 BYTE more; /* Step/run again */
136 BYTE ptn_flag; /* Notification sent to debugger */
137};
138
139
140/* We keep a linked list of processes being debugged. */
141
142static struct debuggee *dbg_head;
143
144/* Here, we cache the most recently access debuggee. */
145
146static struct debuggee *dbg_cache;
147
148/* Table for converting between user struct offsets, uDB_t offsets,
149 and CONTEXTRECORD offsets. It also contains the lengths of the
150 members. */
151
152static struct reg_table const ptrace_regs[] =
153{
154 {R_GS, USEROFF (u_regs[R_GS]), DBGOFF (GS), CTXOFF (ctx_SegGs), 2},
155 {R_FS, USEROFF (u_regs[R_FS]), DBGOFF (FS), CTXOFF (ctx_SegFs), 2},
156 {R_ES, USEROFF (u_regs[R_ES]), DBGOFF (ES), CTXOFF (ctx_SegEs), 2},
157 {R_DS, USEROFF (u_regs[R_DS]), DBGOFF (DS), CTXOFF (ctx_SegDs), 2},
158 {R_EDI, USEROFF (u_regs[R_EDI]), DBGOFF (EDI), CTXOFF (ctx_RegEdi), 4},
159 {R_ESI, USEROFF (u_regs[R_ESI]), DBGOFF (ESI), CTXOFF (ctx_RegEsi), 4},
160 {R_EBP, USEROFF (u_regs[R_EBP]), DBGOFF (EBP), CTXOFF (ctx_RegEbp), 4},
161 {R_ESP, USEROFF (u_regs[R_ESP]), DBGOFF (ESP), CTXOFF (ctx_RegEsp), 4},
162 {R_EBX, USEROFF (u_regs[R_EBX]), DBGOFF (EBX), CTXOFF (ctx_RegEbx), 4},
163 {R_EDX, USEROFF (u_regs[R_EDX]), DBGOFF (EDX), CTXOFF (ctx_RegEdx), 4},
164 {R_ECX, USEROFF (u_regs[R_ECX]), DBGOFF (ECX), CTXOFF (ctx_RegEcx), 4},
165 {R_EAX, USEROFF (u_regs[R_EAX]), DBGOFF (EAX), CTXOFF (ctx_RegEax), 4},
166 {R_EIP, USEROFF (u_regs[R_EIP]), DBGOFF (EIP), CTXOFF (ctx_RegEip), 4},
167 {R_CS, USEROFF (u_regs[R_CS]), DBGOFF (CS), CTXOFF (ctx_SegCs), 2},
168 {R_EFL, USEROFF (u_regs[R_EFL]), DBGOFF (EFlags), CTXOFF (ctx_EFlags), 4},
169 {R_UESP, USEROFF (u_regs[R_UESP]), DBGOFF (ESP), CTXOFF (ctx_RegEsp), 4},
170 {R_SS, USEROFF (u_regs[R_SS]), DBGOFF (SS), CTXOFF (ctx_SegSs), 2}
171};
172
173
174/* Prototypes for forward references. */
175
176static ULONG start_debug_descendant (struct debuggee *parent);
177static ULONG start_debug_descendant_2 (struct debuggee *parent,
178 struct debuggee *child);
179static ULONG do_attach (ULONG pid);
180static ULONG do_detach (struct debuggee *d, ULONG addr);
181static ULONG call_server (struct debuggee *d, ULONG request);
182static ULONG free_debuggee (struct debuggee *d, ULONG return_value);
183
184
185static const struct reg_table *user_addr (ULONG addr)
186{
187 int i;
188
189 for (i = 0; i < PTRACE_REG_COUNT; ++i)
190 if (ptrace_regs[i].user_offset == addr)
191 return &ptrace_regs[i];
192 return NULL;
193}
194
195
196static ULONG debug (struct debuggee *d, ULONG cmd)
197{
198 ULONG rc;
199
200 d->dbgbuf.Cmd = cmd;
201 d->dbgbuf.Pid = d->pid;
202 d->last_cmd = cmd;
203 if (d->type == DT_CLIENT)
204 {
205 rc = call_server (d, REQ_DOSDEBUG);
206 if (rc == 0)
207 rc = d->server_rc;
208 }
209 else
210 rc = DosDebug (&d->dbgbuf);
211 return (rc == 0 ? 0 : set_error (rc));
212}
213
214
215static ULONG debug_read_8 (struct debuggee *d, ULONG addr, BYTE *dst)
216{
217 ULONG rc;
218
219 d->dbgbuf.Addr = addr;
220 rc = debug (d, DBG_C_ReadMem);
221 if (rc != 0 || d->dbgbuf.Cmd != DBG_N_Success) return EIO;
222 *dst = (BYTE)d->dbgbuf.Value;
223 return 0;
224}
225
226
227static ULONG debug_read_16 (struct debuggee *d, ULONG addr, USHORT *dst)
228{
229 ULONG rc;
230
231 d->dbgbuf.Addr = addr;
232 rc = debug (d, DBG_C_ReadMem);
233 if (rc != 0 || d->dbgbuf.Cmd != DBG_N_Success) return EIO;
234 *dst = (USHORT)d->dbgbuf.Value;
235 return 0;
236}
237
238
239static ULONG debug_read_32 (struct debuggee *d, ULONG addr, ULONG *dst)
240{
241 ULONG rc, w;
242
243 d->dbgbuf.Addr = addr;
244 rc = debug (d, DBG_C_ReadMem);
245 if (rc != 0 || d->dbgbuf.Cmd != DBG_N_Success) return EIO;
246 w = d->dbgbuf.Value & 0xffff;
247 d->dbgbuf.Addr = addr + 2;
248 rc = debug (d, DBG_C_ReadMem);
249 if (rc != 0 || d->dbgbuf.Cmd != DBG_N_Success) return EIO;
250 w |= (d->dbgbuf.Value & 0xffff) << 16;
251 *dst = w;
252 return 0;
253}
254
255
256static void do_auto_switch (struct debuggee *d)
257{
258 DosSelectSession (d->sid);
259 d->auto_switch = FALSE;
260}
261
262
263static int debug_poke16 (struct debuggee *d, ULONG addr, ULONG value)
264{
265 ULONG rc;
266
267 d->dbgbuf.Addr = addr;
268 d->dbgbuf.Value = value;
269 rc = debug (d, DBG_C_WriteMem);
270 if (rc == 0 && d->dbgbuf.Cmd != DBG_N_Success)
271 rc = EIO;
272 return rc;
273}
274
275
276/* Write the registers. TID is the thread ID (which must be
277 non-zero). Return errno. */
278
279static ULONG write_reg (struct debuggee *d, ULONG tid)
280{
281 ULONG rc, i;
282
283 if (d->regs_tid != tid)
284 return EIO;
285
286 for (i = 0; i < PTRACE_REG_COUNT; ++i)
287 memcpy ((char *)&d->dbgbuf + ptrace_regs[i].dbg_offset,
288 &d->regs[ptrace_regs[i].reg], ptrace_regs[i].len);
289
290 d->dbgbuf.Tid = tid;
291 rc = debug (d, DBG_C_WriteReg);
292 if (rc == 0 && d->dbgbuf.Cmd != DBG_N_Success)
293 rc = EIO;
294 if (rc == 0)
295 d->regs_changed = FALSE;
296 return rc;
297}
298
299
300/* Read the registers. TID is the thread ID (0 means the active
301 thread). Return errno. */
302
303static ULONG read_reg (struct debuggee *d, ULONG tid)
304{
305 ULONG rc, i;
306 USHORT us;
307
308 if (d->regs_tid != 0 && d->regs_tid == tid)
309 return 0;
310
311 /* Read registers from a new or a different thread. Write back the
312 registers to the previous thread if required. */
313
314 if (d->regs_changed)
315 write_reg (d, d->regs_tid);
316
317 memset (d->regs, 0, sizeof (d->regs));
318 memset (d->exc_regs, 0, sizeof (d->exc_regs));
319
320 d->dbgbuf.Tid = tid;
321 rc = debug (d, DBG_C_ReadReg);
322 if (rc == 0 && d->dbgbuf.Cmd != DBG_N_Success)
323 rc = EIO;
324 if (rc != 0) return rc;
325 for (i = 0; i < PTRACE_REG_COUNT; ++i)
326 memcpy (&d->regs[ptrace_regs[i].reg],
327 (char *)&d->dbgbuf + ptrace_regs[i].dbg_offset,
328 ptrace_regs[i].len);
329
330 if (d->context != 0)
331 {
332 /* After an exception, get the registers from the exception
333 context record. */
334
335 memcpy (d->exc_regs, d->regs, sizeof (d->exc_regs));
336 for (i = 0; i < PTRACE_REG_COUNT; ++i)
337 {
338 if (ptrace_regs[i].len == 4)
339 rc = debug_read_32 (d, d->context + ptrace_regs[i].ctx_offset,
340 &d->regs[ptrace_regs[i].reg]);
341 else
342 {
343 rc = debug_read_16 (d, d->context + ptrace_regs[i].ctx_offset,
344 &us);
345 d->regs[ptrace_regs[i].reg] = us;
346 }
347 if (rc != 0) return rc;
348 }
349 }
350 d->regs_tid = d->dbgbuf.Tid;
351 return rc;
352}
353
354
355/* Set a watchpoint. Return errno. */
356
357static ULONG set_watch (struct debuggee *d, ULONG addr, ULONG len, ULONG type)
358{
359 ULONG rc;
360
361 d->dbgbuf.Addr = addr;
362 d->dbgbuf.Len = len;
363 d->dbgbuf.Index = 0; /* Reserved */
364 d->dbgbuf.Value = type | DBG_W_Local;
365 rc = debug (d, DBG_C_SetWatch);
366 /* TODO: Check notification code? */
367 return rc;
368}
369
370
371static ULONG terminate (struct debuggee *d)
372{
373 ULONG rc;
374
375 d->regs_tid = 0; d->fpregs_ok = FALSE;
376 rc = debug (d, DBG_C_Term);
377 if (rc != 0)
378 return rc;
379 return free_debuggee (d, 0);
380}
381
382
383/* TODO: Have two register sets, see read_reg(). */
384
385static ULONG get_fpstate (struct debuggee *d)
386{
387 ULONG rc, i, flags;
388
389 if (d->context != 0)
390 {
391 /* After an exception, get the floating point state from the
392 exception context record. */
393
394 d->fpregs_ok = FALSE;
395 memset (&d->dbgco, 0, sizeof (d->dbgco));
396
397 rc = debug_read_32 (d, d->context + CTXOFF (ContextFlags), &flags);
398 if (rc != 0) return rc;
399
400 if (!(flags & CONTEXT_FLOATING_POINT))
401 return 0;
402
403 for (i = 0; i < sizeof (struct co_regs); i += 2)
404 {
405 rc = debug_read_16 (d, d->context + CTXOFF (ctx_env) + i,
406 (USHORT *)((char *)&d->dbgco + i));
407 if (rc != 0) return rc;
408 }
409 d->fpregs_ok = TRUE;
410 return 0;
411 }
412 d->dbgbuf.Tid = 0; /* Thread: active thread */
413 d->dbgbuf.Value = DBG_CO_387; /* Coprocessor type */
414 d->dbgbuf.Buffer = (ULONG)&d->dbgco;
415 d->dbgbuf.Len = sizeof (d->dbgco);
416 d->dbgbuf.Index = 0; /* Reserved */
417 rc = debug (d, DBG_C_ReadCoRegs);
418 if (rc == 0 && d->dbgbuf.Cmd != DBG_N_Success)
419 rc = EIO;
420 d->fpregs_ok = (rc == 0);
421 return rc;
422}
423
424
425/* Return true if EIP points to a CALL instruction. Moreover, true is
426 returned if an error occurs (to be on the safe side for
427 automatically switching to the child session). */
428
429static int callp (struct debuggee *d)
430{
431 ULONG eip, rc;
432 BYTE b;
433
434 /* Do not call read_reg() as that would set `d->regs_tid'. */
435
436 d->dbgbuf.Tid = 0;
437 rc = debug (d, DBG_C_ReadReg);
438 if (rc != 0 || d->dbgbuf.Cmd != DBG_N_Success)
439 return TRUE;
440 eip = d->dbgbuf.EIP;
441
442 /* Skip prefixes. */
443
444 do
445 {
446 rc = debug_read_8 (d, eip++, &b);
447 if (rc != 0) return TRUE;
448 } while ((b & 0xe7) == 0x26 /* Segment override prefix */
449 || b == 0x64 /* FS prefix */
450 || b == 0x65 /* GS prefix */
451 || b == 0x66 /* Operand size prefix */
452 || b == 0x67); /* Address size prefix */
453
454 /* Check for `CALL immediate' instruction. */
455
456 if (b == 0xe8 || b == 0x9a) /* CALL near label, CALL far label*/
457 return TRUE;
458
459 /* Check for `CALL indirect' instruction. */
460
461 if (b == 0xff)
462 {
463 rc = debug_read_8 (d, eip++, &b);
464 if (rc != 0) return TRUE;
465 /* Note: This was off by one bit in child.asm! */
466 if ((b & 0x38) == 0x10) /* CALL near reg/mem */
467 return TRUE;
468 if ((b & 0x38) == 0x18) /* CALL far reg/mem */
469 return TRUE;
470 }
471 return FALSE;
472}
473
474
475#define N_STOP (-1)
476#define N_CONTINUE_STOP (-2)
477#define N_CONTINUE_SEARCH (-3)
478#define N_CONTINUE_EXECUTION (-4)
479#define N_RESUME (-5)
480#define N_NOTIFY (-6)
481
482
483static void set_wait_cur_thread (struct debuggee *d, ULONG wait_ret)
484{
485 ULONG rc, tid;
486
487 if ((uflags & _UF_PTRACE_MODEL) != _UF_PTRACE_MULTITHREAD)
488 tid = 0;
489 else
490 {
491 d->dbgbuf.Tid = 0; /* Current thread */
492 rc = debug (d, DBG_C_ReadReg);
493 if (rc != 0 || d->dbgbuf.Cmd != DBG_N_Success)
494 tid = 0;
495 else
496 tid = d->dbgbuf.Tid;
497 }
498 debug_set_wait (d->pid, tid, wait_ret, FALSE);
499}
500
501
502static int debug_signal (struct debuggee *d, int signo, int context_flag)
503{
504 d->more = FALSE;
505 set_wait_cur_thread (d, 0x7f | (signo << 8));
506 if (context_flag)
507 {
508 d->context = d->dbgbuf.Len; /* Address of context buffer */
509 d->signo = signo;
510 }
511 return 0;
512}
513
514
515static int n_exception (struct debuggee *d)
516{
517 int rc;
518 ULONG report, info, w;
519
520 switch (d->dbgbuf.Value)
521 {
522 case DBG_X_PRE_FIRST_CHANCE: /* pre first chance */
523 case DBG_X_STACK_INVALID: /* invalid stack */
524 /* The exception number is in d->dbgbuf.Buffer. */
525 if (d->dbgbuf.Buffer == XCPT_BREAKPOINT)
526 {
527 d->brk_flag = TRUE; d->more = FALSE;
528 set_wait_cur_thread (d, 0x7f | (SIGTRAP << 8));
529 return N_CONTINUE_STOP;
530 }
531 else if (d->dbgbuf.Buffer == XCPT_SINGLE_STEP)
532 {
533 d->more = FALSE;
534 set_wait_cur_thread (d, 0x7f | (SIGTRAP << 8));
535 return N_CONTINUE_STOP;
536 }
537 break;
538
539 case DBG_X_FIRST_CHANCE: /* first chance */
540 report = d->dbgbuf.Buffer; /* Address of report buffer */
541 rc = debug_read_32 (d, report + offsetof (EXCEPTIONREPORTRECORD,
542 ExceptionNum), &w);
543 if (rc == 0)
544 switch (w)
545 {
546 case XCPT_GUARD_PAGE_VIOLATION:
547 case XCPT_PROCESS_TERMINATE:
548 case XCPT_ASYNC_PROCESS_TERMINATE:
549 return N_CONTINUE_SEARCH;
550
551 case XCPT_ACCESS_VIOLATION:
552 case XCPT_DATATYPE_MISALIGNMENT:
553 return debug_signal (d, SIGSEGV, TRUE);
554
555 case XCPT_INTEGER_DIVIDE_BY_ZERO:
556 case XCPT_INTEGER_OVERFLOW:
557 case XCPT_ARRAY_BOUNDS_EXCEEDED:
558 case XCPT_FLOAT_DENORMAL_OPERAND:
559 case XCPT_FLOAT_DIVIDE_BY_ZERO:
560 case XCPT_FLOAT_INEXACT_RESULT:
561 case XCPT_FLOAT_INVALID_OPERATION:
562 case XCPT_FLOAT_OVERFLOW:
563 case XCPT_FLOAT_STACK_CHECK:
564 case XCPT_FLOAT_UNDERFLOW:
565 return debug_signal (d, SIGFPE, TRUE);
566
567 case XCPT_ILLEGAL_INSTRUCTION:
568 case XCPT_INVALID_LOCK_SEQUENCE:
569 case XCPT_PRIVILEGED_INSTRUCTION:
570 return debug_signal (d, SIGILL, TRUE);
571
572 case XCPT_SIGNAL:
573 info = offsetof (EXCEPTIONREPORTRECORD, ExceptionInfo) + 0;
574 rc = debug_read_32 (d, report + info, &w);
575 if (rc == 0)
576 switch (w)
577 {
578 case XCPT_SIGNAL_INTR:
579 return debug_signal (d, SIGINT, TRUE);
580
581 case XCPT_SIGNAL_BREAK:
582 return debug_signal (d, SIGBREAK, TRUE);
583
584 case XCPT_SIGNAL_KILLPROC:
585 return debug_signal (d, SIGTERM, TRUE);
586 }
587 break;
588
589 default:
590 return debug_signal (d, SIGSEGV, TRUE);
591 }
592 break;
593
594 case DBG_X_LAST_CHANCE: /* last chance */
595
596 return N_CONTINUE_SEARCH;
597 }
598
599 return debug_signal (d, SIGSEGV, FALSE);
600}
601
602
603/* Possible notification for the debugger. */
604
605static int notify (struct debuggee *d, int ptn)
606{
607 if ((uflags & _UF_PTRACE_MODEL) == _UF_PTRACE_STANDARD)
608 return N_CONTINUE_STOP;
609 set_wait_cur_thread (d, 0x7f | (SIGPTRACENOTIFY << 8));
610 d->ptn = ptn;
611 d->ptn_flag = TRUE;
612 return N_NOTIFY;
613}
614
615
616/* Handle the current notification and return N_STOP, N_CONTINUE_STOP,
617 N_CONTINUE_SEARCH, N_RESUME, or errno (non-negative). */
618
619static int notification (struct debuggee *d)
620{
621 int rc;
622
623 switch (d->dbgbuf.Cmd)
624 {
625 case DBG_N_Success:
626
627 /* The request was completed successfully. Run or step again
628 unless `d->more' has been set to FALSE. */
629
630 if (d->more)
631 return N_RESUME;
632
633 /* Running or stepping the debuggee is completed. Read the
634 registers. Skip over INT3 if a breakpoint was hit. */
635
636 rc = read_reg (d, 0);
637 if (rc == 0 && d->brk_flag)
638 {
639 /* Skip the INT3 instruction for compatibility with emx.exe
640 and GDB. */
641
642 d->regs[R_EIP] += 1; /* Skip INT3 */
643 d->regs_changed = TRUE;
644 }
645 return rc; /* ptrace() successful (usually) */
646
647 case DBG_N_Error:
648
649 /* An error occured. */
650
651#if 0
652 oprintf ("DosDebug (%d) error: %d\r\n",
653 (int)d->last_cmd, (int)d->dbgbuf.Value);
654#endif
655 return set_error (d->dbgbuf.Value);
656
657 case DBG_N_ProcTerm:
658
659 /* Process terminated. Let wait() return the termination
660 code. */
661
662 debug_set_wait (d->pid, 0, (d->dbgbuf.Value & 0xff) << 8, TRUE);
663 d->more = FALSE;
664 rc = terminate (d);
665 return rc; /* ptrace() successful (usually) */
666
667 case DBG_N_Exception:
668
669 /* An exception occured. */
670
671 return n_exception (d);
672
673 case DBG_N_ModuleLoad:
674 d->ptn_hmte = d->dbgbuf.Value;
675 return notify (d, PTN_MODULE_LOAD);
676
677 case DBG_N_ModuleFree:
678 d->ptn_hmte = d->dbgbuf.Value;
679 return notify (d, PTN_MODULE_FREE);
680
681 case DBG_N_ThreadCreate:
682 d->ptn_tid = d->dbgbuf.Tid;
683 return notify (d, PTN_THREAD_NEW);
684
685 case DBG_N_ThreadTerm:
686 rc = notify (d, PTN_THREAD_END);
687 if (rc == N_NOTIFY)
688 {
689 d->dbgbuf.Tid = 0;
690 if (debug (d, DBG_C_ReadReg) != 0 || d->dbgbuf.Cmd != DBG_N_Success)
691 rc = EINVAL;
692 else
693 d->ptn_tid = d->dbgbuf.Tid;
694 }
695 return rc;
696
697 case DBG_N_NewProc:
698 rc = notify (d, PTN_PROC_NEW);
699 if (rc == N_NOTIFY)
700 {
701 d->ptn_pid = d->dbgbuf.Value;
702 d->ptn_name[0] = 0;
703 d->ptn_fork_addr = 0;
704 d->ptn_flags = 0;
705 rc = start_debug_descendant (d);
706 }
707 return rc;
708
709 case DBG_N_Watchpoint:
710
711 /* Watchpoint hit. Watchpoints are currently used only for
712 skipping over the call to emx_syscall when single-stepping.
713 Stop the debuggee and let wait() indicate SIGTRAP. */
714
715 d->more = FALSE;
716 set_wait_cur_thread (d, 0x7f | (SIGTRAP << 8));
717 return N_STOP;
718
719 case DBG_N_AliasFree:
720 default:
721
722 /* These notifications are not expected to occur. */
723
724 oprintf ("Unexpected DosDebug notification: %d\r\n",
725 (int)d->dbgbuf.Cmd);
726 quit (255);
727 }
728}
729
730
731/* Perform a DBG_C_Go or DBG_C_SStep command. Set a breakpoint at
732 address TBREAK, if non-zero. Send signal SIGNO, if non-zero. */
733
734static ULONG run (struct debuggee *d, ULONG cmd, ULONG tbreak, ULONG signo,
735 ULONG tid)
736{
737 ULONG rc;
738 int next;
739
740 d->ptn_flag = FALSE;
741
742 /* For now, we support raising only that signal by which the
743 debuggee has been stopped. */
744
745 if (signo != 0 && signo != d->signo)
746 return EINVAL;
747
748 /* Automatically switch sessions now for DBG_C_Go. (For
749 DBG_C_SStep, that will be done later, depending on the thread
750 context.) */
751
752 if (d->auto_switch && cmd == DBG_C_Go)
753 do_auto_switch (d);
754
755 /* Set a breakpoint at address TBREAK, if non-zero. This is used
756 for avoiding stepping into emx.dll (which would confuse GDB). */
757
758 if (tbreak != 0)
759 {
760 rc = set_watch (d, tbreak, 1, DBG_W_Execute);
761 if (rc != 0) return rc;
762 }
763
764 /* If the debuggee has been stopped by an exception, special
765 processing is required now. Note that DBG_C_Continue has not yet
766 been performed in that case (still in first chance
767 processing). */
768
769 if (d->signo != 0)
770 {
771 if (signo == d->signo)
772 {
773 /* Restore the exception handler context, and call user
774 exception handlers. */
775
776 memcpy (d->regs, d->exc_regs, sizeof (d->regs));
777 next = N_CONTINUE_SEARCH;
778 }
779 else
780 {
781 /* Restore the exception context (retrieved from the
782 exception context record), and do not call user exception
783 handlers. */
784
785 next = N_CONTINUE_EXECUTION;
786 }
787 d->regs_changed = TRUE; /* Write back registers */
788
789 /* Special processing of exception done (except for the action
790 indicated by `next'. */
791
792 d->context = 0;
793 d->signo = 0;
794 }
795 else
796 next = N_RESUME;
797
798 /* If any registers have been changed, write them to the
799 debuggee. */
800
801 if (d->regs_changed)
802 {
803 rc = write_reg (d, d->regs_tid);
804 if (rc != 0) return rc;
805 }
806
807 d->more = TRUE; d->brk_flag = FALSE;
808 d->regs_tid = 0; d->fpregs_ok = FALSE;
809
810 for (;;)
811 {
812 switch (next)
813 {
814 case N_RESUME:
815 if (d->auto_switch && cmd == DBG_C_SStep && callp (d))
816 do_auto_switch (d);
817 d->dbgbuf.Tid = tid;
818 rc = debug (d, cmd);
819 if (rc != 0) return rc;
820 break;
821
822 case N_STOP:
823 rc = debug (d, DBG_C_Stop);
824 if (rc != 0) return rc;
825 break;
826
827 case N_CONTINUE_STOP:
828 case N_CONTINUE_SEARCH:
829 case N_CONTINUE_EXECUTION:
830
831 d->dbgbuf.Tid = 0;
832 rc = debug (d, DBG_C_ReadReg);
833 if (rc != 0 || d->dbgbuf.Cmd != DBG_N_Success)
834 d->dbgbuf.Tid = 1;
835
836 if (next == N_CONTINUE_SEARCH)
837 d->dbgbuf.Value = XCPT_CONTINUE_SEARCH;
838 else if (next == N_CONTINUE_EXECUTION)
839 d->dbgbuf.Value = XCPT_CONTINUE_EXECUTION;
840 else
841 d->dbgbuf.Value = XCPT_CONTINUE_STOP;
842 rc = debug (d, DBG_C_Continue);
843 if (rc != 0) return rc;
844 break;
845
846 case N_NOTIFY:
847 d->n_cmd = cmd;
848 d->n_tbreak = tbreak;
849 d->n_signo = signo;
850 d->n_tid = tid;
851 return 0;
852
853 default:
854 /* errno value */
855 return (ULONG)next;
856 }
857 next = notification (d);
858 }
859}
860
861
862/* Read a 32-bit word from the layout table of the module, for
863 find_module_layout(). Note that this macro uses local variables of
864 find_module_layout(). */
865
866#define READ_LAYOUT(member,dst) \
867 (debug_read_32 (d, addr_layout + offsetof (layout_table, member), dst) == 0)
868
869
870/* Set the object addresses and sizes in the structure pointed to by
871 PMODULE, if possible. */
872
873static void find_module_layout (struct debuggee *d, struct ptn_module *pmodule)
874{
875 ULONG addr_1, addr_2, addr_layout, flags;
876 ULONG text_base, text_end, data_base, data_end, bss_base, bss_end;
877 BYTE b;
878
879 /* Get the address of the 1st object, which is supposed to be the
880 .text object. */
881
882 d->dbgbuf.Value = 1;
883 d->dbgbuf.MTE = d->ptn_hmte;
884 if (debug (d, DBG_C_NumToAddr) != 0)
885 return;
886 addr_1 = d->dbgbuf.Addr;
887
888 /* Get the address of the 2nd object, which is supposed to be the
889 .data object. */
890
891 d->dbgbuf.Value = 2; /* 2nd object */
892 d->dbgbuf.MTE = d->ptn_hmte;
893 if (debug (d, DBG_C_NumToAddr) != 0)
894 return;
895 addr_2 = d->dbgbuf.Addr;
896
897 /* Check that the .text object starts with the emx startup code.
898 See also spawn_debug(), crt0.s, and dll0.s. */
899
900 if (debug_read_8 (d, addr_1 + 0, &b) != 0
901 || b != 0x68
902 || debug_read_32 (d, addr_1 + 1, &addr_layout) != 0
903 || addr_layout != addr_2
904 || debug_read_8 (d, addr_1 + 5, &b) != 0
905 || b != 0xe8
906 || debug_read_8 (d, addr_1 + 10, &b) != 0
907 || b != 0xeb
908 || debug_read_8 (d, addr_1 + 12, &b) != 0
909 || b != 0xe8)
910 return;
911
912 /* The module seems to be an emx executable or DLL. Read and check
913 the layout table. */
914
915 if (!READ_LAYOUT (flags, &flags)
916 || (flags >> 24) < 2
917 || !READ_LAYOUT (text_base, &text_base)
918 || !READ_LAYOUT (text_end, &text_end)
919 || text_base > text_end
920 || !READ_LAYOUT (data_base, &data_base)
921 || !READ_LAYOUT (data_end, &data_end)
922 || data_base > data_end
923 || !READ_LAYOUT (bss_base, &bss_base)
924 || !READ_LAYOUT (bss_end, &bss_end)
925 || bss_base > bss_end)
926 return;
927
928 /* Store the object addresses and sizes to the structure provided by
929 the caller of ptrace(). */
930
931 pmodule->text_start = text_base;
932 pmodule->text_size = text_end - text_base;
933 pmodule->data_start = data_base;
934 pmodule->data_size = data_end - data_base;
935 pmodule->bss_start = bss_base;
936 pmodule->bss_size = bss_end - bss_base;
937
938 /* Set PTNMOD_AOUT unless L_FLAG_LINK386 is set. */
939
940 if (!(flags & L_FLAG_LINK386))
941 pmodule->flags |= PTNMOD_AOUT;
942}
943
944
945/* Implement PTRACE_NOTIFICATION. */
946
947static int get_notification (struct debuggee *d, void *data, size_t size,
948 ULONG *errnop)
949{
950 struct ptn_thread *pthread;
951 struct ptn_module *pmodule;
952 struct ptn_proc *pproc;
953 ULONG rc, flags;
954
955 if (!d->ptn_flag)
956 {
957 *errnop = EINVAL;
958 return -1;
959 }
960 switch (d->ptn)
961 {
962 case PTN_THREAD_NEW:
963 case PTN_THREAD_END:
964 if (data != NULL && size >= sizeof (struct ptn_thread))
965 {
966 pthread = data;
967 pthread->tid = d->ptn_tid;
968 }
969 break;
970
971 case PTN_MODULE_LOAD:
972 case PTN_MODULE_FREE:
973 if (data != NULL && size >= sizeof (struct ptn_module))
974 {
975 pmodule = data;
976 pmodule->hmte = d->ptn_hmte;
977 rc = DosQueryModuleName (d->ptn_hmte, sizeof (pmodule->name),
978 pmodule->name);
979 if (rc != 0)
980 pmodule->name[0] = 0;
981 pmodule->text_start = 0; pmodule->text_size = 0;
982 pmodule->data_start = 0; pmodule->data_size = 0;
983 pmodule->bss_start = 0; pmodule->bss_size = 0;
984 pmodule->flags = 0;
985 if (d->ptn == PTN_MODULE_LOAD)
986 find_module_layout (d, pmodule);
987 if (pmodule->name[0] != 0)
988 {
989 rc = DosQueryAppType (pmodule->name, &flags);
990 if (rc == 0 && (flags & FAPPTYP_DLL))
991 pmodule->flags |= PTNMOD_DLL;
992 }
993 }
994 break;
995
996 case PTN_PROC_NEW:
997 if (data != NULL && size >= sizeof (struct ptn_proc))
998 {
999 pproc = data;
1000 pproc->pid = d->ptn_pid;
1001 pproc->fork_addr = d->ptn_fork_addr;
1002 pproc->flags = d->ptn_flags;
1003 memcpy (pproc->name, d->ptn_name, sizeof (pproc->name));
1004 }
1005 break;
1006 }
1007 *errnop = 0;
1008 return d->ptn;
1009}
1010
1011
1012/* Handle PTRACE_THAW and PTRACE_FREEEZ. */
1013
1014static int thaw_freeze (struct debuggee *d, ULONG cmd, ULONG tid,
1015 ULONG *errnop)
1016{
1017 ULONG rc;
1018
1019 if ((uflags & _UF_PTRACE_MODEL) != _UF_PTRACE_MULTITHREAD || tid == 0)
1020 {
1021 *errnop = EINVAL;
1022 return -1;
1023 }
1024
1025 d->dbgbuf.Tid = tid;
1026 rc = debug (d, cmd);
1027 *errnop = rc;
1028 return rc == 0 ? 0 : -1;
1029}
1030
1031
1032int do_ptrace (ULONG request, ULONG pid, ULONG addr, ULONG data, ULONG *errnop)
1033{
1034 ULONG rc, w, tid;
1035 const struct reg_table *rp;
1036 struct debuggee *d;
1037
1038 switch (request)
1039 {
1040 case PTRACE_ATTACH:
1041 rc = do_attach (pid);
1042 *errnop = rc;
1043 return (rc == 0 ? 0 : -1);
1044 }
1045
1046 if ((uflags & _UF_PTRACE_MODEL) == _UF_PTRACE_MULTITHREAD)
1047 {
1048 tid = PTRACE_GETTID (pid);
1049 pid = PTRACE_GETPID (pid);
1050 }
1051 else
1052 tid = 1;
1053
1054 if (pid == 0)
1055 {
1056 *errnop = ESRCH;
1057 return -1;
1058 }
1059
1060 if (dbg_cache != NULL && dbg_cache->pid == pid)
1061 d = dbg_cache;
1062 else
1063 {
1064 for (d = dbg_head; d != NULL; d = d->next)
1065 if (d->pid == pid)
1066 break;
1067 if (d == NULL)
1068 {
1069 *errnop = ESRCH;
1070 return -1;
1071 }
1072 dbg_cache = d;
1073 }
1074 switch (request)
1075 {
1076 case PTRACE_EXIT:
1077 d->regs_tid = 0; d->fpregs_ok = FALSE;
1078 rc = terminate (d);
1079 *errnop = rc;
1080 if (rc != 0) return -1;
1081 debug_set_wait (pid, 0, 0, TRUE);
1082 return 0;
1083
1084 case PTRACE_PEEKTEXT:
1085 case PTRACE_PEEKDATA:
1086 rc = debug_read_32 (d, addr, &w);
1087 *errnop = rc;
1088 if (rc != 0) return -1;
1089 return w;
1090
1091 case PTRACE_POKETEXT:
1092 case PTRACE_POKEDATA:
1093 rc = debug_poke16 (d, addr, (ULONG)data & 0xffff);
1094 if (rc == 0)
1095 rc = debug_poke16 (d, addr +2, (ULONG)data >> 16);
1096 *errnop = rc;
1097 return (rc == 0 ? 0 : -1);
1098
1099 case PTRACE_PEEKUSER:
1100 if (addr == USEROFF (u_ar0))
1101 w = USEROFF (u_regs) + KERNEL_U_ADDR;
1102 else if (addr == USEROFF (u_fpvalid))
1103 {
1104 if (!d->fpregs_ok)
1105 get_fpstate (d);
1106 w = (d->fpregs_ok ? 0xff : 0);
1107 }
1108 else if (addr == USEROFF (u_fpstate.status))
1109 w = 0; /* ... */
1110 else if (addr >= USEROFF (u_fpstate.state)
1111 && addr <= USEROFF (u_fpstate.state) + sizeof (d->dbgco) - 4)
1112 {
1113 if (!d->fpregs_ok)
1114 {
1115 rc = get_fpstate (d);
1116 if (rc != 0)
1117 {
1118 *errnop = rc;
1119 return -1;
1120 }
1121 }
1122 memcpy (&w, (char *)&d->dbgco + addr - USEROFF (u_fpstate.state), 4);
1123 }
1124 else if ((rp = user_addr (addr)) != NULL)
1125 {
1126 rc = read_reg (d, tid);
1127 if (rc != 0)
1128 {
1129 *errnop = rc;
1130 return -1;
1131 }
1132 w = 0;
1133 memcpy (&w, &d->regs[rp->reg], rp->len);
1134 }
1135 else
1136 {
1137 *errnop = EINVAL;
1138 return -1;
1139 }
1140 *errnop = 0;
1141 return w;
1142
1143 case PTRACE_POKEUSER:
1144 rp = user_addr (addr);
1145 if (rp == NULL)
1146 rc = EIO;
1147 else
1148 rc = read_reg (d, tid);
1149 if (rc == 0)
1150 {
1151 d->regs[rp->reg] = 0;
1152 memcpy (&d->regs[rp->reg], &data, rp->len);
1153 d->regs_changed = TRUE;
1154 }
1155 *errnop = rc;
1156 return (rc == 0 ? 0 : -1);
1157
1158 case PTRACE_RESUME:
1159 rc = run (d, DBG_C_Go, 0, data, tid);
1160 *errnop = rc;
1161 return (rc == 0 ? 0 : -1);
1162
1163 case PTRACE_STEP:
1164 /* Avoid stepping into emx_syscall. */
1165 if (d->syscall_addr != 0 && d->syscall_addr == d->regs[R_EIP])
1166 rc = run (d, DBG_C_Go, d->syscall_addr + 5, data, tid);
1167 else
1168 rc = run (d, DBG_C_SStep, 0, data, tid);
1169 *errnop = rc;
1170 return (rc == 0 ? 0 : -1);
1171
1172 case PTRACE_SESSION:
1173 if (!debug_same_sess)
1174 switch (data)
1175 {
1176 case 0: /* Switch to debugger */
1177 DosSelectSession (0);
1178 d->auto_switch = FALSE;
1179 break;
1180
1181 case 1: /* Switch to child */
1182 DosSelectSession (d->sid);
1183 d->auto_switch = FALSE;
1184 break;
1185
1186 case 2: /* Automatic switch to child */
1187 d->auto_switch = TRUE;
1188 break;
1189
1190 default:
1191 /* Succeed for undefined values. */
1192 break;
1193 }
1194 *errnop = 0;
1195 return 0;
1196
1197 case PTRACE_NOTIFICATION:
1198 return get_notification (d, (void *)addr, (size_t)data, errnop);
1199
1200 case PTRACE_CONT:
1201 if (!d->ptn_flag)
1202 {
1203 *errnop = EINVAL;
1204 return -1;
1205 }
1206 rc = run (d, d->n_cmd, d->n_tbreak, d->n_signo, d->n_tid);
1207 *errnop = rc;
1208 return (rc == 0 ? 0 : -1);
1209
1210 case PTRACE_THAW:
1211 return thaw_freeze (d, DBG_C_Resume, tid, errnop);
1212
1213 case PTRACE_FREEZE:
1214 return thaw_freeze (d, DBG_C_Freeze, tid, errnop);
1215
1216 case PTRACE_DETACH:
1217 rc = do_detach (d, addr);
1218 *errnop = rc;
1219 return (rc == 0 ? 0 : -1);
1220
1221 case PTRACE_TRACEME:
1222 default:
1223 *errnop = EINVAL;
1224 return -1;
1225 }
1226}
1227
1228
1229static ULONG free_debuggee (struct debuggee *d, ULONG return_value)
1230{
1231 struct debuggee **pd;
1232
1233 if (d->resources & (DR_SERVER_HEV_SERVER | DR_CLIENT_HEV_SERVER))
1234 DosCloseEventSem (d->hev_server);
1235 if (d->resources & (DR_SERVER_HEV_CLIENT | DR_CLIENT_HEV_CLIENT))
1236 DosCloseEventSem (d->hev_client);
1237
1238 if (d->resources & DR_LIST)
1239 {
1240 for (pd = &dbg_head; *pd != NULL; pd = &(*pd)->next)
1241 if (*pd == d)
1242 {
1243 *pd = d->next;
1244 break;
1245 }
1246 if (dbg_cache == d)
1247 dbg_cache = NULL;
1248 }
1249
1250 DosFreeMem (d);
1251 return return_value;
1252}
1253
1254
1255/* Return errno. */
1256
1257static ULONG new_debuggee (struct debuggee **pd, ULONG pid, ULONG sid,
1258 enum dbg_type type)
1259{
1260 ULONG rc, w;
1261 BYTE b;
1262 struct debuggee *d;
1263 void *mem;
1264
1265 if (type == DT_CLIENT || type == DT_SERVER)
1266 d = *pd;
1267 else
1268 {
1269 rc = DosAllocMem (&mem, sizeof (*d), PAG_COMMIT | PAG_READ | PAG_WRITE);
1270 if (rc != 0) return set_error (rc);
1271 d = (struct debuggee *)mem;
1272 d->resources = 0;
1273 }
1274
1275 d->next = NULL;
1276 d->type = type;
1277 d->syscall_addr = 0;
1278 d->layout_addr = 0;
1279 d->fpregs_ok = FALSE;
1280 d->regs_tid = 0;
1281 d->regs_changed = FALSE;
1282 d->context = 0;
1283 d->signo = 0;
1284 d->ptn = PTN_NONE;
1285 d->ptn_flag = FALSE;
1286
1287 if (type == DT_CLIENT)
1288 {
1289 rc = call_server (d, REQ_ATTACH);
1290 if (rc != 0)
1291 return free_debuggee (d, set_error (rc));
1292 if (d->server_rc != 0)
1293 return free_debuggee (d, d->server_rc);
1294 }
1295
1296 if (type == DT_LOCAL || type == DT_SERVER)
1297 {
1298 d->pid = pid;
1299 d->sid = sid;
1300
1301 d->dbgbuf.Tid = 0; /* reserved */
1302 d->dbgbuf.Value = DBG_L_386; /* level */
1303 rc = debug (d, DBG_C_Connect);
1304 if (rc != 0)
1305 return free_debuggee (d, rc);
1306 if (d->dbgbuf.Cmd != DBG_N_Success)
1307 return free_debuggee (d, EINVAL);
1308 d->dbgbuf.Tid = 0; /* Active thread */
1309 rc = debug (d, DBG_C_ReadReg);
1310 if (rc != 0)
1311 return free_debuggee (d, rc);
1312 if (d->dbgbuf.Cmd != DBG_N_Success)
1313 return free_debuggee (d, EINVAL);
1314 }
1315 if (debug_read_8 (d, ENTRY_POINT+0, &b) == 0 && b == 0x68 /* PUSH n */
1316 && debug_read_8 (d, ENTRY_POINT+5, &b) == 0 && b == 0xe8 /* CALL */
1317 && debug_read_8 (d, ENTRY_POINT+10, &b) == 0 && b == 0xeb /* JMP */
1318 && debug_read_8 (d, ENTRY_POINT+12, &b) == 0 && b == 0xe8 /* CALL */
1319 && debug_read_32 (d, ENTRY_POINT+1, &w) == 0)
1320 {
1321 d->layout_addr = w;
1322 d->syscall_addr = ENTRY_POINT + 12;
1323 }
1324
1325 if (pd != NULL)
1326 *pd = d;
1327
1328 if (type == DT_LOCAL || type == DT_CLIENT)
1329 {
1330 d->next = dbg_head;
1331 dbg_head = d;
1332 d->resources |= DR_LIST;
1333 }
1334 return 0;
1335}
1336
1337
1338/* Prepare a child process for debugging. Return errno. */
1339
1340ULONG spawn_debug (ULONG pid, ULONG sid)
1341{
1342 return new_debuggee (NULL, pid, sid, DT_LOCAL);
1343}
1344
1345
1346/* Detach from a child process, that is, let it run to the end. */
1347
1348static void handle_detach (struct debuggee *d)
1349{
1350 ULONG rc;
1351 enum {GO, END, NOTIFICATION} state;
1352
1353 /* Thaw all threads. */
1354
1355 d->dbgbuf.Cmd = DBG_C_Resume;
1356 d->dbgbuf.Pid = d->pid;
1357 d->dbgbuf.Tid = 0; /* All threads */
1358 rc = DosDebug (&d->dbgbuf);
1359
1360 state = GO;
1361 do
1362 {
1363 if (state == GO)
1364 {
1365 d->dbgbuf.Cmd = DBG_C_Go;
1366 d->dbgbuf.Pid = d->pid;
1367 rc = DosDebug (&d->dbgbuf);
1368 if (rc != 0) break;
1369 }
1370
1371 switch (d->dbgbuf.Cmd)
1372 {
1373 case DBG_N_Error:
1374 state = END;
1375 break;
1376
1377 case DBG_N_Exception:
1378 d->dbgbuf.Cmd = DBG_C_ReadReg;
1379 d->dbgbuf.Pid = d->pid;
1380 d->dbgbuf.Tid = 0;
1381 rc = DosDebug (&d->dbgbuf);
1382 if (rc != 0 || d->dbgbuf.Cmd != DBG_N_Success)
1383 d->dbgbuf.Tid = 1;
1384
1385 d->dbgbuf.Cmd = DBG_C_Continue;
1386 d->dbgbuf.Value = XCPT_CONTINUE_SEARCH;
1387 d->dbgbuf.Pid = d->pid;
1388 rc = DosDebug (&d->dbgbuf);
1389 state = rc == 0 ? NOTIFICATION : END;
1390 break;
1391
1392 case DBG_N_ProcTerm:
1393 d->dbgbuf.Cmd = DBG_C_Go;
1394 d->dbgbuf.Pid = d->pid;
1395 rc = DosDebug (&d->dbgbuf);
1396 state = END;
1397 break;
1398
1399 default:
1400 state = GO;
1401 break;
1402 }
1403 } while (state != END);
1404
1405 free_debuggee (d, 0);
1406 DosExit (EXIT_THREAD, 0);
1407}
1408
1409
1410
1411static void server_answer (struct debuggee *d)
1412{
1413 ULONG rc;
1414
1415 rc = DosPostEventSem (d->hev_client);
1416 if (rc != 0)
1417 {
1418 oprintf ("server_answer: DosPostEventSem failed, rc=%u\r\n", rc);
1419 DosExit (EXIT_THREAD, 2);
1420 }
1421}
1422
1423
1424/* Handle DosDebug requests from a debugger which attached to a child
1425 process of one of our debuggees. */
1426
1427static void debug_descendant_thread (ULONG arg)
1428{
1429 struct debuggee *d;
1430 ULONG rc, post_count, clients;
1431
1432 /* Raise our priority. */
1433
1434 rc = DosSetPriority (PRTYS_THREAD, PRTYC_FOREGROUNDSERVER, 0, 0);
1435
1436 d = (struct debuggee *)arg;
1437 clients = 0;
1438
1439 /* Handle requests. */
1440
1441 for (;;)
1442 {
1443 rc = DosWaitEventSem (d->hev_server, SEM_INDEFINITE_WAIT);
1444 if (rc != 0)
1445 {
1446 oprintf ("debug_descendant_thread: DosWaitEventSem failed, "
1447 "rc=%u\r\n", rc);
1448 return;
1449 }
1450
1451 rc = DosResetEventSem (d->hev_server, &post_count);
1452
1453 switch (d->server_request)
1454 {
1455 case REQ_ATTACH:
1456 if (clients == 0)
1457 {
1458 ++clients;
1459 d->server_rc = 0;
1460 }
1461 else
1462 d->server_rc = EACCES;
1463 server_answer (d);
1464 break;
1465
1466 case REQ_DOSDEBUG:
1467 d->server_rc = DosDebug (&d->dbgbuf);
1468 server_answer (d);
1469 break;
1470
1471 case REQ_DETACH:
1472 d->server_rc = 0; /* errno*/
1473 server_answer (d);
1474 --clients;
1475 if (d->server_arg == 1)
1476 handle_detach (d);
1477 break;
1478
1479 default:
1480 oprintf ("debug_descendant_thread: invalid request %u\r\n",
1481 d->server_request);
1482 break;
1483 }
1484 }
1485}
1486
1487
1488/* The debuggee PARENT got a new child process. Enable debugging of
1489 the child process for PTRACE_ATTACH. Return errno or N_NOTIFY. */
1490
1491static ULONG start_debug_descendant (struct debuggee *parent)
1492{
1493 char mname[64];
1494 struct debuggee *child;
1495 void *mem;
1496 TID tid;
1497 ULONG rc;
1498
1499 sprintf (mname, SHAREMEM_FMT, (unsigned)parent->ptn_pid);
1500 rc = DosAllocSharedMem (&mem, mname, sizeof (*child),
1501 PAG_COMMIT | PAG_READ | PAG_WRITE);
1502 if (rc != 0) return set_error (rc);
1503 child = (struct debuggee *)mem;
1504 child->resources = 0;
1505
1506 rc = DosCreateEventSem (NULL, &child->hev_server, DC_SEM_SHARED, FALSE);
1507 if (rc == 0)
1508 {
1509 child->resources |= DR_SERVER_HEV_SERVER;
1510 rc = DosCreateEventSem (NULL, &child->hev_client, DC_SEM_SHARED, FALSE);
1511 if (rc == 0)
1512 {
1513 child->resources |= DR_SERVER_HEV_CLIENT;
1514
1515 /* We don't have a session ID. TODO: qwait_thread() may
1516 provide the session ID sooner or later. */
1517
1518 rc = new_debuggee (&child, parent->ptn_pid, 0, DT_SERVER);
1519
1520 /* More initializations. */
1521
1522 if (rc == 0)
1523 rc = start_debug_descendant_2 (parent, child);
1524
1525 /* Create a thread which will handle debugging of the new
1526 child process. */
1527
1528 if (rc == 0)
1529 rc = DosCreateThread (&tid, debug_descendant_thread, (ULONG)child,
1530 CREATE_READY | STACK_COMMITTED, 0x4000);
1531 }
1532 }
1533 if (rc != 0)
1534 return free_debuggee (child, set_error (rc));
1535
1536 return N_NOTIFY;
1537}
1538
1539
1540/* Subroutine of the above. Return OS/2 error code. */
1541
1542static ULONG start_debug_descendant_2 (struct debuggee *parent,
1543 struct debuggee *child)
1544{
1545 ULONG rc, flags, w;
1546 BYTE b;
1547
1548 /* For now assume that the child is in the same session as its
1549 parent. (The condition is always true.) That doesn't hurt
1550 because we can't switch sessions anyway (either we'll get error
1551 ERROR_SMG_SESSION_NOT_PARENT or ERROR_SMG_SESSION_NOT_FOREGRND,
1552 depending on whether the client or server attempts to select the
1553 session). */
1554
1555 if (child->sid == 0)
1556 child->sid = parent->sid;
1557
1558 /* Get module name of the child process. */
1559
1560 child->dbgbuf.Tid = 0; /* Current thread */
1561 rc = debug (child, DBG_C_ReadReg);
1562 if (rc == 0 && child->dbgbuf.Cmd == DBG_N_Success)
1563 {
1564 rc = DosQueryModuleName (child->dbgbuf.MTE, sizeof (parent->ptn_name),
1565 parent->ptn_name);
1566 if (rc != 0)
1567 parent->ptn_name[0] = 0;
1568 }
1569
1570 /* Find out whether the child process uses a.out format. */
1571
1572 if (child->layout_addr != 0
1573 && debug_read_32 (child, (child->layout_addr
1574 + offsetof (layout_table, flags)),
1575 &flags) == 0
1576 && !(flags & L_FLAG_LINK386))
1577 parent->ptn_flags |= PTNPROC_AOUT;
1578
1579 /* Find out whether the child process has been forked. */
1580
1581 if (debug_read_8 (parent, (ULONG)&ptrace_forking, &b) == 0 && b)
1582 {
1583 parent->ptn_flags |= PTNPROC_FORK;
1584 if (debug_read_32 (parent, (ULONG)&ptrace_fork_addr, &w) == 0)
1585 parent->ptn_fork_addr = w;
1586 }
1587
1588 return 0;
1589}
1590
1591
1592/* Return OS/2 error code. */
1593
1594static ULONG call_server (struct debuggee *d, ULONG request)
1595{
1596 ULONG rc, count;
1597
1598 rc = DosResetEventSem (d->hev_client, &count);
1599 if (rc != 0 && rc != ERROR_ALREADY_RESET)
1600 return rc;
1601
1602 d->server_request = request;
1603 rc = DosPostEventSem (d->hev_server);
1604 if (rc != 0) return rc;
1605
1606 rc = DosWaitEventSem (d->hev_client, SEM_INDEFINITE_WAIT);
1607 if (rc != 0) return rc;
1608 return 0;
1609}
1610
1611
1612/* Return errno. */
1613
1614static ULONG new_client (struct debuggee **pd, ULONG pid)
1615{
1616 char mname[64];
1617 ULONG rc;
1618 void *mem;
1619 struct debuggee *d;
1620
1621 sprintf (mname, SHAREMEM_FMT, (unsigned)pid);
1622 rc = DosGetNamedSharedMem (&mem, mname, PAG_READ | PAG_WRITE);
1623 if (rc != 0)
1624 return ESRCH;
1625
1626 d = (struct debuggee *)mem;
1627
1628 /* Assume that at most one process at a time connects to the server
1629 for each PID. */
1630
1631 d->resources &= ~(DR_CLIENT_HEV_CLIENT | DR_CLIENT_HEV_SERVER);
1632
1633 rc = DosOpenEventSem (NULL, &d->hev_server);
1634 if (rc == 0)
1635 {
1636 d->resources |= DR_CLIENT_HEV_SERVER;
1637 rc = DosOpenEventSem (NULL, &d->hev_client);
1638 if (rc == 0)
1639 d->resources |= DR_CLIENT_HEV_CLIENT;
1640 }
1641 if (rc != 0)
1642 return free_debuggee (d, set_error (rc));
1643 *pd = d;
1644 return 0;
1645}
1646
1647
1648/* Handle PTRACE_ATTACH. Return errno. */
1649
1650static ULONG do_attach (ULONG pid)
1651{
1652 struct debuggee *d;
1653 ULONG rc;
1654
1655 rc = proc_add_attach (pid);
1656 if (rc != 0) return rc;
1657
1658 rc = new_client (&d, pid);
1659 if (rc == 0)
1660 {
1661 rc = new_debuggee (&d, pid, 0, DT_CLIENT);
1662 if (rc == 0)
1663 debug_set_wait (d->pid, 0, 0x7f | (SIGTRAP << 8), FALSE);
1664 if (rc != 0)
1665 free_debuggee (d, 0);
1666 }
1667 if (rc != 0)
1668 proc_remove_attach (pid);
1669 return rc;
1670}
1671
1672
1673/* Handle PTRACE_DETACH. Return errno. */
1674
1675static ULONG do_detach (struct debuggee *d, ULONG addr)
1676{
1677 ULONG rc;
1678
1679 if (d->type != DT_CLIENT)
1680 return EINVAL;
1681 if (addr != 0 && addr != 1)
1682 return EINVAL;
1683 d->server_arg = addr;
1684 rc = call_server (d, REQ_DETACH);
1685 if (rc != 0)
1686 return set_error (rc);
1687 else
1688 return free_debuggee (d, d->server_rc);
1689}
Note: See TracBrowser for help on using the repository browser.