Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/i386/linux/linux_ptrace_machdep.c
39483 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2001 Alexander Kabaev
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*/
28
29
#include "opt_cpu.h"
30
31
#include <sys/param.h>
32
#include <sys/systm.h>
33
#include <sys/lock.h>
34
#include <sys/mutex.h>
35
#include <sys/proc.h>
36
#include <sys/ptrace.h>
37
#include <sys/syscallsubr.h>
38
39
#include <machine/md_var.h>
40
#include <machine/pcb.h>
41
42
#include <i386/linux/linux.h>
43
#include <i386/linux/linux_proto.h>
44
#include <compat/linux/linux_signal.h>
45
46
/*
47
* Linux ptrace requests numbers. Mostly identical to FreeBSD,
48
* except for MD ones and PT_ATTACH/PT_DETACH.
49
*/
50
#define PTRACE_TRACEME 0
51
#define PTRACE_PEEKTEXT 1
52
#define PTRACE_PEEKDATA 2
53
#define PTRACE_PEEKUSR 3
54
#define PTRACE_POKETEXT 4
55
#define PTRACE_POKEDATA 5
56
#define PTRACE_POKEUSR 6
57
#define PTRACE_CONT 7
58
#define PTRACE_KILL 8
59
#define PTRACE_SINGLESTEP 9
60
61
#define PTRACE_ATTACH 16
62
#define PTRACE_DETACH 17
63
64
#define LINUX_PTRACE_SYSCALL 24
65
66
#define PTRACE_GETREGS 12
67
#define PTRACE_SETREGS 13
68
#define PTRACE_GETFPREGS 14
69
#define PTRACE_SETFPREGS 15
70
#define PTRACE_GETFPXREGS 18
71
#define PTRACE_SETFPXREGS 19
72
73
#define PTRACE_SETOPTIONS 21
74
75
/*
76
* Linux keeps debug registers at the following
77
* offset in the user struct
78
*/
79
#define LINUX_DBREG_OFFSET 252
80
#define LINUX_DBREG_SIZE (8*sizeof(l_int))
81
82
static __inline int
83
map_signum(int signum)
84
{
85
86
signum = linux_to_bsd_signal(signum);
87
return ((signum == SIGSTOP)? 0 : signum);
88
}
89
90
struct linux_pt_reg {
91
l_long ebx;
92
l_long ecx;
93
l_long edx;
94
l_long esi;
95
l_long edi;
96
l_long ebp;
97
l_long eax;
98
l_int xds;
99
l_int xes;
100
l_int xfs;
101
l_int xgs;
102
l_long orig_eax;
103
l_long eip;
104
l_int xcs;
105
l_long eflags;
106
l_long esp;
107
l_int xss;
108
};
109
110
/*
111
* Translate i386 ptrace registers between Linux and FreeBSD formats.
112
* The translation is pretty straighforward, for all registers, but
113
* orig_eax on Linux side and r_trapno and r_err in FreeBSD
114
*/
115
static void
116
map_regs_to_linux(struct reg *bsd_r, struct linux_pt_reg *linux_r)
117
{
118
linux_r->ebx = bsd_r->r_ebx;
119
linux_r->ecx = bsd_r->r_ecx;
120
linux_r->edx = bsd_r->r_edx;
121
linux_r->esi = bsd_r->r_esi;
122
linux_r->edi = bsd_r->r_edi;
123
linux_r->ebp = bsd_r->r_ebp;
124
linux_r->eax = bsd_r->r_eax;
125
linux_r->xds = bsd_r->r_ds;
126
linux_r->xes = bsd_r->r_es;
127
linux_r->xfs = bsd_r->r_fs;
128
linux_r->xgs = bsd_r->r_gs;
129
linux_r->orig_eax = bsd_r->r_eax;
130
linux_r->eip = bsd_r->r_eip;
131
linux_r->xcs = bsd_r->r_cs;
132
linux_r->eflags = bsd_r->r_eflags;
133
linux_r->esp = bsd_r->r_esp;
134
linux_r->xss = bsd_r->r_ss;
135
}
136
137
static void
138
map_regs_from_linux(struct reg *bsd_r, struct linux_pt_reg *linux_r)
139
{
140
bsd_r->r_ebx = linux_r->ebx;
141
bsd_r->r_ecx = linux_r->ecx;
142
bsd_r->r_edx = linux_r->edx;
143
bsd_r->r_esi = linux_r->esi;
144
bsd_r->r_edi = linux_r->edi;
145
bsd_r->r_ebp = linux_r->ebp;
146
bsd_r->r_eax = linux_r->eax;
147
bsd_r->r_ds = linux_r->xds;
148
bsd_r->r_es = linux_r->xes;
149
bsd_r->r_fs = linux_r->xfs;
150
bsd_r->r_gs = linux_r->xgs;
151
bsd_r->r_eip = linux_r->eip;
152
bsd_r->r_cs = linux_r->xcs;
153
bsd_r->r_eflags = linux_r->eflags;
154
bsd_r->r_esp = linux_r->esp;
155
bsd_r->r_ss = linux_r->xss;
156
}
157
158
struct linux_pt_fpreg {
159
l_long cwd;
160
l_long swd;
161
l_long twd;
162
l_long fip;
163
l_long fcs;
164
l_long foo;
165
l_long fos;
166
l_long st_space[2*10];
167
};
168
169
static void
170
map_fpregs_to_linux(struct fpreg *bsd_r, struct linux_pt_fpreg *linux_r)
171
{
172
linux_r->cwd = bsd_r->fpr_env[0];
173
linux_r->swd = bsd_r->fpr_env[1];
174
linux_r->twd = bsd_r->fpr_env[2];
175
linux_r->fip = bsd_r->fpr_env[3];
176
linux_r->fcs = bsd_r->fpr_env[4];
177
linux_r->foo = bsd_r->fpr_env[5];
178
linux_r->fos = bsd_r->fpr_env[6];
179
bcopy(bsd_r->fpr_acc, linux_r->st_space, sizeof(linux_r->st_space));
180
}
181
182
static void
183
map_fpregs_from_linux(struct fpreg *bsd_r, struct linux_pt_fpreg *linux_r)
184
{
185
bsd_r->fpr_env[0] = linux_r->cwd;
186
bsd_r->fpr_env[1] = linux_r->swd;
187
bsd_r->fpr_env[2] = linux_r->twd;
188
bsd_r->fpr_env[3] = linux_r->fip;
189
bsd_r->fpr_env[4] = linux_r->fcs;
190
bsd_r->fpr_env[5] = linux_r->foo;
191
bsd_r->fpr_env[6] = linux_r->fos;
192
bcopy(bsd_r->fpr_acc, linux_r->st_space, sizeof(bsd_r->fpr_acc));
193
}
194
195
struct linux_pt_fpxreg {
196
l_ushort cwd;
197
l_ushort swd;
198
l_ushort twd;
199
l_ushort fop;
200
l_long fip;
201
l_long fcs;
202
l_long foo;
203
l_long fos;
204
l_long mxcsr;
205
l_long reserved;
206
l_long st_space[32];
207
l_long xmm_space[32];
208
l_long padding[56];
209
};
210
211
static int
212
linux_proc_read_fpxregs(struct thread *td, struct linux_pt_fpxreg *fpxregs)
213
{
214
215
PROC_LOCK_ASSERT(td->td_proc, MA_OWNED);
216
if (cpu_fxsr == 0)
217
return (EIO);
218
bcopy(&get_pcb_user_save_td(td)->sv_xmm, fpxregs, sizeof(*fpxregs));
219
return (0);
220
}
221
222
static int
223
linux_proc_write_fpxregs(struct thread *td, struct linux_pt_fpxreg *fpxregs)
224
{
225
226
PROC_LOCK_ASSERT(td->td_proc, MA_OWNED);
227
if (cpu_fxsr == 0)
228
return (EIO);
229
bcopy(fpxregs, &get_pcb_user_save_td(td)->sv_xmm, sizeof(*fpxregs));
230
return (0);
231
}
232
233
int
234
linux_ptrace(struct thread *td, struct linux_ptrace_args *uap)
235
{
236
union {
237
struct linux_pt_reg reg;
238
struct linux_pt_fpreg fpreg;
239
struct linux_pt_fpxreg fpxreg;
240
} r;
241
union {
242
struct reg bsd_reg;
243
struct fpreg bsd_fpreg;
244
struct dbreg bsd_dbreg;
245
} u;
246
void *addr;
247
pid_t pid;
248
int error, req;
249
250
error = 0;
251
252
/* by default, just copy data intact */
253
req = uap->req;
254
pid = (pid_t)uap->pid;
255
addr = (void *)uap->addr;
256
257
switch (req) {
258
case PTRACE_TRACEME:
259
case PTRACE_POKETEXT:
260
case PTRACE_POKEDATA:
261
case PTRACE_KILL:
262
error = kern_ptrace(td, req, pid, addr, uap->data);
263
break;
264
case PTRACE_PEEKTEXT:
265
case PTRACE_PEEKDATA: {
266
/* need to preserve return value */
267
int rval = td->td_retval[0];
268
error = kern_ptrace(td, req, pid, addr, 0);
269
if (error == 0)
270
error = copyout(td->td_retval, (void *)uap->data,
271
sizeof(l_int));
272
td->td_retval[0] = rval;
273
break;
274
}
275
case PTRACE_DETACH:
276
error = kern_ptrace(td, PT_DETACH, pid, (void *)1,
277
map_signum(uap->data));
278
break;
279
case PTRACE_SINGLESTEP:
280
case PTRACE_CONT:
281
error = kern_ptrace(td, req, pid, (void *)1,
282
map_signum(uap->data));
283
break;
284
case PTRACE_ATTACH:
285
error = kern_ptrace(td, PT_ATTACH, pid, addr, uap->data);
286
break;
287
case PTRACE_GETREGS:
288
/* Linux is using data where FreeBSD is using addr */
289
error = kern_ptrace(td, PT_GETREGS, pid, &u.bsd_reg, 0);
290
if (error == 0) {
291
map_regs_to_linux(&u.bsd_reg, &r.reg);
292
error = copyout(&r.reg, (void *)uap->data,
293
sizeof(r.reg));
294
}
295
break;
296
case PTRACE_SETREGS:
297
/* Linux is using data where FreeBSD is using addr */
298
error = copyin((void *)uap->data, &r.reg, sizeof(r.reg));
299
if (error == 0) {
300
map_regs_from_linux(&u.bsd_reg, &r.reg);
301
error = kern_ptrace(td, PT_SETREGS, pid, &u.bsd_reg, 0);
302
}
303
break;
304
case PTRACE_GETFPREGS:
305
/* Linux is using data where FreeBSD is using addr */
306
error = kern_ptrace(td, PT_GETFPREGS, pid, &u.bsd_fpreg, 0);
307
if (error == 0) {
308
map_fpregs_to_linux(&u.bsd_fpreg, &r.fpreg);
309
error = copyout(&r.fpreg, (void *)uap->data,
310
sizeof(r.fpreg));
311
}
312
break;
313
case PTRACE_SETFPREGS:
314
/* Linux is using data where FreeBSD is using addr */
315
error = copyin((void *)uap->data, &r.fpreg, sizeof(r.fpreg));
316
if (error == 0) {
317
map_fpregs_from_linux(&u.bsd_fpreg, &r.fpreg);
318
error = kern_ptrace(td, PT_SETFPREGS, pid,
319
&u.bsd_fpreg, 0);
320
}
321
break;
322
case PTRACE_SETFPXREGS:
323
error = copyin((void *)uap->data, &r.fpxreg, sizeof(r.fpxreg));
324
if (error)
325
break;
326
/* FALL THROUGH */
327
case PTRACE_GETFPXREGS: {
328
struct proc *p;
329
struct thread *td2;
330
331
if (sizeof(struct linux_pt_fpxreg) != sizeof(struct savexmm)) {
332
static int once = 0;
333
if (!once) {
334
printf("linux: savexmm != linux_pt_fpxreg\n");
335
once = 1;
336
}
337
error = EIO;
338
break;
339
}
340
341
if ((p = pfind(uap->pid)) == NULL) {
342
error = ESRCH;
343
break;
344
}
345
346
/* Exiting processes can't be debugged. */
347
if ((p->p_flag & P_WEXIT) != 0) {
348
error = ESRCH;
349
goto fail;
350
}
351
352
if ((error = p_candebug(td, p)) != 0)
353
goto fail;
354
355
/* System processes can't be debugged. */
356
if ((p->p_flag & P_SYSTEM) != 0) {
357
error = EINVAL;
358
goto fail;
359
}
360
361
/* not being traced... */
362
if ((p->p_flag & P_TRACED) == 0) {
363
error = EPERM;
364
goto fail;
365
}
366
367
/* not being traced by YOU */
368
if (p->p_pptr != td->td_proc) {
369
error = EBUSY;
370
goto fail;
371
}
372
373
/* not currently stopped */
374
if (!P_SHOULDSTOP(p) || (p->p_flag & P_WAITED) == 0) {
375
error = EBUSY;
376
goto fail;
377
}
378
379
if (req == PTRACE_GETFPXREGS) {
380
_PHOLD(p); /* may block */
381
td2 = FIRST_THREAD_IN_PROC(p);
382
error = linux_proc_read_fpxregs(td2, &r.fpxreg);
383
_PRELE(p);
384
PROC_UNLOCK(p);
385
if (error == 0)
386
error = copyout(&r.fpxreg, (void *)uap->data,
387
sizeof(r.fpxreg));
388
} else {
389
/* clear dangerous bits exactly as Linux does*/
390
r.fpxreg.mxcsr &= 0xffbf;
391
_PHOLD(p); /* may block */
392
td2 = FIRST_THREAD_IN_PROC(p);
393
error = linux_proc_write_fpxregs(td2, &r.fpxreg);
394
_PRELE(p);
395
PROC_UNLOCK(p);
396
}
397
break;
398
399
fail:
400
PROC_UNLOCK(p);
401
break;
402
}
403
case PTRACE_PEEKUSR:
404
case PTRACE_POKEUSR: {
405
error = EIO;
406
407
/* check addr for alignment */
408
if (uap->addr < 0 || uap->addr & (sizeof(l_int) - 1))
409
break;
410
/*
411
* Allow Linux programs to access register values in
412
* user struct. We simulate this through PT_GET/SETREGS
413
* as necessary.
414
*/
415
if (uap->addr < sizeof(struct linux_pt_reg)) {
416
error = kern_ptrace(td, PT_GETREGS, pid, &u.bsd_reg, 0);
417
if (error != 0)
418
break;
419
420
map_regs_to_linux(&u.bsd_reg, &r.reg);
421
if (req == PTRACE_PEEKUSR) {
422
error = copyout((char *)&r.reg + uap->addr,
423
(void *)uap->data, sizeof(l_int));
424
break;
425
}
426
427
*(l_int *)((char *)&r.reg + uap->addr) =
428
(l_int)uap->data;
429
430
map_regs_from_linux(&u.bsd_reg, &r.reg);
431
error = kern_ptrace(td, PT_SETREGS, pid, &u.bsd_reg, 0);
432
}
433
434
/*
435
* Simulate debug registers access
436
*/
437
if (uap->addr >= LINUX_DBREG_OFFSET &&
438
uap->addr <= LINUX_DBREG_OFFSET + LINUX_DBREG_SIZE) {
439
error = kern_ptrace(td, PT_GETDBREGS, pid, &u.bsd_dbreg,
440
0);
441
if (error != 0)
442
break;
443
444
uap->addr -= LINUX_DBREG_OFFSET;
445
if (req == PTRACE_PEEKUSR) {
446
error = copyout((char *)&u.bsd_dbreg +
447
uap->addr, (void *)uap->data,
448
sizeof(l_int));
449
break;
450
}
451
452
*(l_int *)((char *)&u.bsd_dbreg + uap->addr) =
453
uap->data;
454
error = kern_ptrace(td, PT_SETDBREGS, pid,
455
&u.bsd_dbreg, 0);
456
}
457
458
break;
459
}
460
case LINUX_PTRACE_SYSCALL:
461
/* fall through */
462
default:
463
printf("linux: ptrace(%u, ...) not implemented\n",
464
(unsigned int)uap->req);
465
error = EINVAL;
466
break;
467
}
468
469
return (error);
470
}
471
472