1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
|
/* The kernel call implemented in this file:
* m_type: SYS_TRACE
*
* The parameters for this kernel call are:
* m_lsys_krn_sys_trace.endpt process that is traced
* m_lsys_krn_sys_trace.request trace request
* m_lsys_krn_sys_trace.address address at traced process' space
* m_lsys_krn_sys_trace.data data to be written
* m_krn_lsys_sys_trace.data data to be returned
*/
#include "kernel/system.h"
#include <sys/ptrace.h>
#if USE_TRACE
/*==========================================================================*
* do_trace *
*==========================================================================*/
int do_trace(struct proc * caller, message * m_ptr)
{
/* Handle the debugging commands supported by the ptrace system call
* The commands are:
* T_STOP stop the process
* T_OK enable tracing by parent for this process
* T_GETINS return value from instruction space
* T_GETDATA return value from data space
* T_GETUSER return value from user process table
* T_SETINS set value in instruction space
* T_SETDATA set value in data space
* T_SETUSER set value in user process table
* T_RESUME resume execution
* T_EXIT exit
* T_STEP set trace bit
* T_SYSCALL trace system call
* T_ATTACH attach to an existing process
* T_DETACH detach from a traced process
* T_SETOPT set trace options
* T_GETRANGE get range of values
* T_SETRANGE set range of values
*
* The T_OK, T_ATTACH, T_EXIT, and T_SETOPT commands are handled completely by
* the process manager. T_GETRANGE and T_SETRANGE use sys_vircopy(). All others
* come here.
*/
register struct proc *rp;
vir_bytes tr_addr = m_ptr->m_lsys_krn_sys_trace.address;
long tr_data = m_ptr->m_lsys_krn_sys_trace.data;
int tr_request = m_ptr->m_lsys_krn_sys_trace.request;
int tr_proc_nr_e = m_ptr->m_lsys_krn_sys_trace.endpt, tr_proc_nr;
unsigned char ub;
int i;
#define COPYTOPROC(addr, myaddr, length) { \
struct vir_addr fromaddr, toaddr; \
int r; \
fromaddr.proc_nr_e = KERNEL; \
toaddr.proc_nr_e = tr_proc_nr_e; \
fromaddr.offset = (myaddr); \
toaddr.offset = (addr); \
if((r=virtual_copy_vmcheck(caller, &fromaddr, \
&toaddr, length)) != OK) { \
printf("Can't copy in sys_trace: %d\n", r);\
return r;\
} \
}
#define COPYFROMPROC(addr, myaddr, length) { \
struct vir_addr fromaddr, toaddr; \
int r; \
fromaddr.proc_nr_e = tr_proc_nr_e; \
toaddr.proc_nr_e = KERNEL; \
fromaddr.offset = (addr); \
toaddr.offset = (myaddr); \
if((r=virtual_copy_vmcheck(caller, &fromaddr, \
&toaddr, length)) != OK) { \
printf("Can't copy in sys_trace: %d\n", r);\
return r;\
} \
}
if(!isokendpt(tr_proc_nr_e, &tr_proc_nr)) return(EINVAL);
if (iskerneln(tr_proc_nr)) return(EPERM);
rp = proc_addr(tr_proc_nr);
if (isemptyp(rp)) return(EINVAL);
switch (tr_request) {
case T_STOP: /* stop process */
RTS_SET(rp, RTS_P_STOP);
/* clear syscall trace and single step flags */
rp->p_misc_flags &= ~(MF_SC_TRACE | MF_STEP);
return(OK);
case T_GETINS: /* return value from instruction space */
COPYFROMPROC(tr_addr, (vir_bytes) &tr_data, sizeof(long));
m_ptr->m_krn_lsys_sys_trace.data = tr_data;
break;
case T_GETDATA: /* return value from data space */
COPYFROMPROC(tr_addr, (vir_bytes) &tr_data, sizeof(long));
m_ptr->m_krn_lsys_sys_trace.data= tr_data;
break;
case T_GETUSER: /* return value from process table */
if ((tr_addr & (sizeof(long) - 1)) != 0) return(EFAULT);
if (tr_addr <= sizeof(struct proc) - sizeof(long)) {
m_ptr->m_krn_lsys_sys_trace.data =
*(long *) ((char *) rp + (int) tr_addr);
break;
}
/* The process's proc struct is followed by its priv struct.
* The alignment here should be unnecessary, but better safe..
*/
i = sizeof(long) - 1;
tr_addr -= (sizeof(struct proc) + i) & ~i;
if (tr_addr > sizeof(struct priv) - sizeof(long)) return(EFAULT);
m_ptr->m_krn_lsys_sys_trace.data =
*(long *) ((char *) rp->p_priv + (int) tr_addr);
break;
case T_SETINS: /* set value in instruction space */
COPYTOPROC(tr_addr, (vir_bytes) &tr_data, sizeof(long));
m_ptr->m_krn_lsys_sys_trace.data = 0;
break;
case T_SETDATA: /* set value in data space */
COPYTOPROC(tr_addr, (vir_bytes) &tr_data, sizeof(long));
m_ptr->m_krn_lsys_sys_trace.data = 0;
break;
case T_SETUSER: /* set value in process table */
if ((tr_addr & (sizeof(reg_t) - 1)) != 0 ||
tr_addr > sizeof(struct stackframe_s) - sizeof(reg_t))
return(EFAULT);
i = (int) tr_addr;
#if defined(__i386__)
/* Altering segment registers might crash the kernel when it
* tries to load them prior to restarting a process, so do
* not allow it.
*/
if (i == (int) &((struct proc *) 0)->p_reg.cs ||
i == (int) &((struct proc *) 0)->p_reg.ds ||
i == (int) &((struct proc *) 0)->p_reg.es ||
i == (int) &((struct proc *) 0)->p_reg.gs ||
i == (int) &((struct proc *) 0)->p_reg.fs ||
i == (int) &((struct proc *) 0)->p_reg.ss)
return(EFAULT);
if (i == (int) &((struct proc *) 0)->p_reg.psw)
/* only selected bits are changeable */
SETPSW(rp, tr_data);
else
*(reg_t *) ((char *) &rp->p_reg + i) = (reg_t) tr_data;
#elif defined(__arm__)
if (i == (int) &((struct proc *) 0)->p_reg.psr) {
/* only selected bits are changeable */
SET_USR_PSR(rp, tr_data);
} else {
*(reg_t *) ((char *) &rp->p_reg + i) = (reg_t) tr_data;
}
#endif
m_ptr->m_krn_lsys_sys_trace.data = 0;
break;
case T_DETACH: /* detach tracer */
rp->p_misc_flags &= ~MF_SC_ACTIVE;
/* fall through */
case T_RESUME: /* resume execution */
RTS_UNSET(rp, RTS_P_STOP);
m_ptr->m_krn_lsys_sys_trace.data = 0;
break;
case T_STEP: /* set trace bit */
rp->p_misc_flags |= MF_STEP;
RTS_UNSET(rp, RTS_P_STOP);
m_ptr->m_krn_lsys_sys_trace.data = 0;
break;
case T_SYSCALL: /* trace system call */
rp->p_misc_flags |= MF_SC_TRACE;
RTS_UNSET(rp, RTS_P_STOP);
m_ptr->m_krn_lsys_sys_trace.data = 0;
break;
case T_READB_INS: /* get value from instruction space */
COPYFROMPROC(tr_addr, (vir_bytes) &ub, 1);
m_ptr->m_krn_lsys_sys_trace.data = ub;
break;
case T_WRITEB_INS: /* set value in instruction space */
ub = (unsigned char) (tr_data & 0xff);
COPYTOPROC(tr_addr, (vir_bytes) &ub, 1);
m_ptr->m_krn_lsys_sys_trace.data = 0;
break;
default:
return(EINVAL);
}
return(OK);
}
#endif /* USE_TRACE */
|