summaryrefslogtreecommitdiffstats
path: root/sys/kern/sys_process.c
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>2005-06-30 07:49:22 +0000
committerpeter <peter@FreeBSD.org>2005-06-30 07:49:22 +0000
commit921b3c5ee4556d6428b9c88c71d3b69e0da1345b (patch)
treedad118ce52d6d607680db2768f086c4ee826fd22 /sys/kern/sys_process.c
parent14ff9254d85bc5b36288a2b4ba9db5bcb52f7587 (diff)
downloadFreeBSD-src-921b3c5ee4556d6428b9c88c71d3b69e0da1345b.zip
FreeBSD-src-921b3c5ee4556d6428b9c88c71d3b69e0da1345b.tar.gz
Jumbo-commit to enhance 32 bit application support on 64 bit kernels.
This is good enough to be able to run a RELENG_4 gdb binary against a RELENG_4 application, along with various other tools (eg: 4.x gcore). We use this at work. ia32_reg.[ch]: handle the 32 bit register file format, used by ptrace, procfs and core dumps. procfs_*regs.c: vary the format of proc/XXX/*regs depending on the client and target application. procfs_map.c: Don't print a 64 bit value to 32 bit consumers, or their sscanf fails. They expect an unsigned long. imgact_elf.c: produce a valid 32 bit coredump for 32 bit apps. sys_process.c: handle 32 bit consumers debugging 32 bit targets. Note that 64 bit consumers can still debug 32 bit targets. IA64 has got stubs for ia32_reg.c. Known limitations: a 5.x/6.x gdb uses get/setcontext(), which isn't implemented in the 32/64 wrapper yet. We also make a tiny patch to gdb pacify it over conflicting formats of ld-elf.so.1. Approved by: re
Diffstat (limited to 'sys/kern/sys_process.c')
-rw-r--r--sys/kern/sys_process.c200
1 files changed, 178 insertions, 22 deletions
diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c
index b9afb6a..11b66da 100644
--- a/sys/kern/sys_process.c
+++ b/sys/kern/sys_process.c
@@ -32,6 +32,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include "opt_compat.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/lock.h>
@@ -55,6 +57,21 @@ __FBSDID("$FreeBSD$");
#include <vm/vm_object.h>
#include <vm/vm_page.h>
+#ifdef COMPAT_IA32
+#include <sys/procfs.h>
+#include <machine/fpu.h>
+#include <compat/ia32/ia32_reg.h>
+
+extern struct sysentvec ia32_freebsd_sysvec;
+
+struct ptrace_io_desc32 {
+ int piod_op;
+ u_int32_t piod_offs;
+ u_int32_t piod_addr;
+ u_int32_t piod_len;
+};
+#endif
+
/*
* Functions implemented using PROC_ACTION():
*
@@ -138,6 +155,51 @@ proc_write_fpregs(struct thread *td, struct fpreg *fpregs)
PROC_ACTION(set_fpregs(td, fpregs));
}
+#ifdef COMPAT_IA32
+/* For 32 bit binaries, we need to expose the 32 bit regs layouts. */
+int
+proc_read_regs32(struct thread *td, struct reg32 *regs32)
+{
+
+ PROC_ACTION(fill_regs32(td, regs32));
+}
+
+int
+proc_write_regs32(struct thread *td, struct reg32 *regs32)
+{
+
+ PROC_ACTION(set_regs32(td, regs32));
+}
+
+int
+proc_read_dbregs32(struct thread *td, struct dbreg32 *dbregs32)
+{
+
+ PROC_ACTION(fill_dbregs32(td, dbregs32));
+}
+
+int
+proc_write_dbregs32(struct thread *td, struct dbreg32 *dbregs32)
+{
+
+ PROC_ACTION(set_dbregs32(td, dbregs32));
+}
+
+int
+proc_read_fpregs32(struct thread *td, struct fpreg32 *fpregs32)
+{
+
+ PROC_ACTION(fill_fpregs32(td, fpregs32));
+}
+
+int
+proc_write_fpregs32(struct thread *td, struct fpreg32 *fpregs32)
+{
+
+ PROC_ACTION(set_fpregs32(td, fpregs32));
+}
+#endif
+
int
proc_sstep(struct thread *td)
{
@@ -290,6 +352,27 @@ struct ptrace_args {
};
#endif
+#ifdef COMPAT_IA32
+/*
+ * This CPP subterfuge is to try and reduce the number of ifdefs in
+ * the body of the code.
+ * COPYIN(uap->addr, &r.reg, sizeof r.reg);
+ * becomes either:
+ * copyin(uap->addr, &r.reg, sizeof r.reg);
+ * or
+ * copyin(uap->addr, &r.reg32, sizeof r.reg32);
+ * .. except this is done at runtime.
+ */
+#define COPYIN(u, k, s) wrap32 ? \
+ copyin(u, k ## 32, s ## 32) : \
+ copyin(u, k, s)
+#define COPYOUT(k, u, s) wrap32 ? \
+ copyout(k ## 32, u, s ## 32) : \
+ copyout(k, u, s)
+#else
+#define COPYIN(u, k, s) copyin(u, k, s)
+#define COPYOUT(k, u, s) copyout(k, u, s)
+#endif
/*
* MPSAFE
*/
@@ -306,10 +389,21 @@ ptrace(struct thread *td, struct ptrace_args *uap)
struct dbreg dbreg;
struct fpreg fpreg;
struct reg reg;
+#ifdef COMPAT_IA32
+ struct dbreg32 dbreg32;
+ struct fpreg32 fpreg32;
+ struct reg32 reg32;
+ struct ptrace_io_desc32 piod32;
+#endif
} r;
void *addr;
int error = 0;
+#ifdef COMPAT_IA32
+ int wrap32 = 0;
+ if (td->td_proc->p_sysent == &ia32_freebsd_sysvec)
+ wrap32 = 1;
+#endif
addr = &r;
switch (uap->req) {
case PT_GETREGS:
@@ -318,16 +412,16 @@ ptrace(struct thread *td, struct ptrace_args *uap)
case PT_LWPINFO:
break;
case PT_SETREGS:
- error = copyin(uap->addr, &r.reg, sizeof r.reg);
+ error = COPYIN(uap->addr, &r.reg, sizeof r.reg);
break;
case PT_SETFPREGS:
- error = copyin(uap->addr, &r.fpreg, sizeof r.fpreg);
+ error = COPYIN(uap->addr, &r.fpreg, sizeof r.fpreg);
break;
case PT_SETDBREGS:
- error = copyin(uap->addr, &r.dbreg, sizeof r.dbreg);
+ error = COPYIN(uap->addr, &r.dbreg, sizeof r.dbreg);
break;
case PT_IO:
- error = copyin(uap->addr, &r.piod, sizeof r.piod);
+ error = COPYIN(uap->addr, &r.piod, sizeof r.piod);
break;
default:
addr = uap->addr;
@@ -342,16 +436,16 @@ ptrace(struct thread *td, struct ptrace_args *uap)
switch (uap->req) {
case PT_IO:
- (void)copyout(&r.piod, uap->addr, sizeof r.piod);
+ error = COPYOUT(&r.piod, uap->addr, sizeof r.piod);
break;
case PT_GETREGS:
- error = copyout(&r.reg, uap->addr, sizeof r.reg);
+ error = COPYOUT(&r.reg, uap->addr, sizeof r.reg);
break;
case PT_GETFPREGS:
- error = copyout(&r.fpreg, uap->addr, sizeof r.fpreg);
+ error = COPYOUT(&r.fpreg, uap->addr, sizeof r.fpreg);
break;
case PT_GETDBREGS:
- error = copyout(&r.dbreg, uap->addr, sizeof r.dbreg);
+ error = COPYOUT(&r.dbreg, uap->addr, sizeof r.dbreg);
break;
case PT_LWPINFO:
error = copyout(&r.pl, uap->addr, uap->data);
@@ -360,6 +454,30 @@ ptrace(struct thread *td, struct ptrace_args *uap)
return (error);
}
+#undef COPYIN
+#undef COPYOUT
+
+#ifdef COMPAT_IA32
+/*
+ * PROC_READ(regs, td2, addr);
+ * becomes either:
+ * proc_read_regs(td2, addr);
+ * or
+ * proc_read_regs32(td2, addr);
+ * .. except this is done at runtime. There is an additional
+ * complication in that PROC_WRITE disallows 32 bit consumers
+ * from writing to 64 bit address space targets.
+ */
+#define PROC_READ(w, t, a) wrap32 ? \
+ proc_read_ ## w ## 32(t, a) : \
+ proc_read_ ## w (t, a)
+#define PROC_WRITE(w, t, a) wrap32 ? \
+ (safe ? proc_write_ ## w ## 32(t, a) : EINVAL ) : \
+ proc_write_ ## w (t, a)
+#else
+#define PROC_READ(w, t, a) proc_read_ ## w (t, a)
+#define PROC_WRITE(w, t, a) proc_write_ ## w (t, a)
+#endif
int
kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
@@ -368,12 +486,16 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
struct uio uio;
struct proc *curp, *p, *pp;
struct thread *td2 = NULL;
- struct ptrace_io_desc *piod;
+ struct ptrace_io_desc *piod = NULL;
struct ptrace_lwpinfo *pl;
int error, write, tmp, num;
int proctree_locked = 0;
lwpid_t tid = 0, *buf;
pid_t saved_pid = pid;
+#ifdef COMPAT_IA32
+ int wrap32 = 0, safe = 0;
+ struct ptrace_io_desc32 *piod32 = NULL;
+#endif
curp = td->td_proc;
@@ -449,6 +571,17 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
tid = td2->td_tid;
}
+#ifdef COMPAT_IA32
+ /*
+ * Test if we're a 32 bit client and what the target is.
+ * Set the wrap controls accordingly.
+ */
+ if (td->td_proc->p_sysent == &ia32_freebsd_sysvec) {
+ if (td2->td_proc->p_sysent == &ia32_freebsd_sysvec)
+ safe = 1;
+ wrap32 = 1;
+ }
+#endif
/*
* Permissions check
*/
@@ -723,16 +856,32 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
case PT_IO:
PROC_UNLOCK(p);
- piod = addr;
- iov.iov_base = piod->piod_addr;
- iov.iov_len = piod->piod_len;
+#ifdef COMPAT_IA32
+ if (wrap32) {
+ piod32 = addr;
+ iov.iov_base = (void *)(uintptr_t)piod32->piod_addr;
+ iov.iov_len = piod32->piod_len;
+ uio.uio_offset = (off_t)(uintptr_t)piod32->piod_offs;
+ uio.uio_resid = piod32->piod_len;
+ } else
+#endif
+ {
+ piod = addr;
+ iov.iov_base = piod->piod_addr;
+ iov.iov_len = piod->piod_len;
+ uio.uio_offset = (off_t)(uintptr_t)piod->piod_offs;
+ uio.uio_resid = piod->piod_len;
+ }
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
- uio.uio_offset = (off_t)(uintptr_t)piod->piod_offs;
- uio.uio_resid = piod->piod_len;
uio.uio_segflg = UIO_USERSPACE;
uio.uio_td = td;
- switch (piod->piod_op) {
+#ifdef COMPAT_IA32
+ tmp = wrap32 ? piod32->piod_op : piod->piod_op;
+#else
+ tmp = piod->piod_op;
+#endif
+ switch (tmp) {
case PIOD_READ_D:
case PIOD_READ_I:
uio.uio_rw = UIO_READ;
@@ -745,7 +894,12 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
return (EINVAL);
}
error = proc_rwmem(p, &uio);
- piod->piod_len -= uio.uio_resid;
+#ifdef COMPAT_IA32
+ if (wrap32)
+ piod32->piod_len -= uio.uio_resid;
+ else
+#endif
+ piod->piod_len -= uio.uio_resid;
return (error);
case PT_KILL:
@@ -754,42 +908,42 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
case PT_SETREGS:
_PHOLD(p);
- error = proc_write_regs(td2, addr);
+ error = PROC_WRITE(regs, td2, addr);
_PRELE(p);
PROC_UNLOCK(p);
return (error);
case PT_GETREGS:
_PHOLD(p);
- error = proc_read_regs(td2, addr);
+ error = PROC_READ(regs, td2, addr);
_PRELE(p);
PROC_UNLOCK(p);
return (error);
case PT_SETFPREGS:
_PHOLD(p);
- error = proc_write_fpregs(td2, addr);
+ error = PROC_WRITE(fpregs, td2, addr);
_PRELE(p);
PROC_UNLOCK(p);
return (error);
case PT_GETFPREGS:
_PHOLD(p);
- error = proc_read_fpregs(td2, addr);
+ error = PROC_READ(fpregs, td2, addr);
_PRELE(p);
PROC_UNLOCK(p);
return (error);
case PT_SETDBREGS:
_PHOLD(p);
- error = proc_write_dbregs(td2, addr);
+ error = PROC_WRITE(dbregs, td2, addr);
_PRELE(p);
PROC_UNLOCK(p);
return (error);
case PT_GETDBREGS:
_PHOLD(p);
- error = proc_read_dbregs(td2, addr);
+ error = PROC_READ(dbregs, td2, addr);
_PRELE(p);
PROC_UNLOCK(p);
return (error);
@@ -872,6 +1026,8 @@ fail_noproc:
sx_xunlock(&proctree_lock);
return (error);
}
+#undef PROC_READ
+#undef PROC_WRITE
/*
* Stop a process because of a debugging event;
OpenPOWER on IntegriCloud