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
This commit is contained in:
Peter Wemm 2005-06-30 07:49:22 +00:00
parent d14b395392
commit 62919d788b
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=147692
14 changed files with 908 additions and 44 deletions

224
sys/amd64/ia32/ia32_reg.c Normal file
View File

@ -0,0 +1,224 @@
/*-
* Copyright (c) 2005 Peter Wemm
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/exec.h>
#include <sys/fcntl.h>
#include <sys/imgact.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/mman.h>
#include <sys/namei.h>
#include <sys/pioctl.h>
#include <sys/proc.h>
#include <sys/procfs.h>
#include <sys/resourcevar.h>
#include <sys/systm.h>
#include <sys/signalvar.h>
#include <sys/stat.h>
#include <sys/sx.h>
#include <sys/syscall.h>
#include <sys/sysctl.h>
#include <sys/sysent.h>
#include <sys/vnode.h>
#include <vm/vm.h>
#include <vm/vm_kern.h>
#include <vm/vm_param.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
#include <vm/vm_object.h>
#include <vm/vm_extern.h>
#include <compat/freebsd32/freebsd32_util.h>
#include <compat/freebsd32/freebsd32_proto.h>
#include <machine/fpu.h>
#include <compat/ia32/ia32_reg.h>
#include <machine/psl.h>
#include <machine/segments.h>
#include <machine/specialreg.h>
#include <machine/frame.h>
#include <machine/md_var.h>
#include <machine/pcb.h>
#include <machine/cpufunc.h>
#define CS_SECURE(cs) (ISPL(cs) == SEL_UPL)
#define EFL_SECURE(ef, oef) ((((ef) ^ (oef)) & ~PSL_USERCHANGE) == 0)
int
fill_regs32(struct thread *td, struct reg32 *regs)
{
struct pcb *pcb;
struct trapframe *tp;
tp = td->td_frame;
pcb = td->td_pcb;
regs->r_fs = pcb->pcb_fs;
regs->r_es = pcb->pcb_es;
regs->r_ds = pcb->pcb_ds;
regs->r_edi = tp->tf_rdi;
regs->r_esi = tp->tf_rsi;
regs->r_ebp = tp->tf_rbp;
regs->r_ebx = tp->tf_rbx;
regs->r_edx = tp->tf_rdx;
regs->r_ecx = tp->tf_rcx;
regs->r_eax = tp->tf_rax;
regs->r_eip = tp->tf_rip;
regs->r_cs = tp->tf_cs;
regs->r_eflags = tp->tf_rflags;
regs->r_esp = tp->tf_rsp;
regs->r_ss = tp->tf_ss;
regs->r_gs = pcb->pcb_gs;
return (0);
}
int
set_regs32(struct thread *td, struct reg32 *regs)
{
struct pcb *pcb;
struct trapframe *tp;
tp = td->td_frame;
if (!EFL_SECURE(regs->r_eflags, tp->tf_rflags) || !CS_SECURE(regs->r_cs))
return (EINVAL);
pcb = td->td_pcb;
load_fs(regs->r_fs);
pcb->pcb_fs = regs->r_fs;
load_es(regs->r_es);
pcb->pcb_es = regs->r_es;
load_ds(regs->r_ds);
pcb->pcb_ds = regs->r_ds;
tp->tf_rdi = regs->r_edi;
tp->tf_rsi = regs->r_esi;
tp->tf_rbp = regs->r_ebp;
tp->tf_rbx = regs->r_ebx;
tp->tf_rdx = regs->r_edx;
tp->tf_rcx = regs->r_ecx;
tp->tf_rax = regs->r_eax;
tp->tf_rip = regs->r_eip;
tp->tf_cs = regs->r_cs;
tp->tf_rflags = regs->r_eflags;
tp->tf_rsp = regs->r_esp;
tp->tf_ss = regs->r_ss;
load_gs(regs->r_gs);
pcb->pcb_gs = regs->r_gs;
return (0);
}
int
fill_fpregs32(struct thread *td, struct fpreg32 *regs)
{
struct save87 *sv_87 = (struct save87 *)regs;
struct env87 *penv_87 = &sv_87->sv_env;
struct savefpu *sv_fpu = &td->td_pcb->pcb_save;
struct envxmm *penv_xmm = &sv_fpu->sv_env;
int i;
bzero(regs, sizeof(*regs));
/* FPU control/status */
penv_87->en_cw = penv_xmm->en_cw;
penv_87->en_sw = penv_xmm->en_sw;
penv_87->en_tw = penv_xmm->en_tw;
/*
* XXX for en_fip/fcs/foo/fos, check if the fxsave format
* uses the old-style layout for 32 bit user apps. If so,
* read the ip and operand segment registers from there.
* For now, use the process's %cs/%ds.
*/
penv_87->en_fip = penv_xmm->en_rip;
penv_87->en_fcs = td->td_frame->tf_cs;
penv_87->en_opcode = penv_xmm->en_opcode;
penv_87->en_foo = penv_xmm->en_rdp;
penv_87->en_fos = td->td_pcb->pcb_ds;
/* FPU registers */
for (i = 0; i < 8; ++i)
sv_87->sv_ac[i] = sv_fpu->sv_fp[i].fp_acc;
return (0);
}
int
set_fpregs32(struct thread *td, struct fpreg32 *regs)
{
struct save87 *sv_87 = (struct save87 *)regs;
struct env87 *penv_87 = &sv_87->sv_env;
struct savefpu *sv_fpu = &td->td_pcb->pcb_save;
struct envxmm *penv_xmm = &sv_fpu->sv_env;
int i;
/* FPU control/status */
penv_xmm->en_cw = penv_87->en_cw;
penv_xmm->en_sw = penv_87->en_sw;
penv_xmm->en_tw = penv_87->en_tw;
penv_xmm->en_rip = penv_87->en_fip;
/* penv_87->en_fcs and en_fos ignored, see above */
penv_xmm->en_opcode = penv_87->en_opcode;
penv_xmm->en_rdp = penv_87->en_foo;
/* FPU registers */
for (i = 0; i < 8; ++i)
sv_fpu->sv_fp[i].fp_acc = sv_87->sv_ac[i];
for (i = 8; i < 16; ++i)
bzero(&sv_fpu->sv_fp[i].fp_acc, sizeof(sv_fpu->sv_fp[i].fp_acc));
return (0);
}
int
fill_dbregs32(struct thread *td, struct dbreg32 *regs)
{
struct dbreg dr;
int err, i;
err = fill_dbregs(td, &dr);
for (i = 0; i < 8; i++)
regs->dr[i] = dr.dr[i];
for (i = 8; i < 16; i++)
regs->dr[i] = 0;
return (err);
}
int
set_dbregs32(struct thread *td, struct dbreg32 *regs)
{
struct dbreg dr;
int i;
for (i = 0; i < 8; i++)
dr.dr[i] = regs->dr[i];
for (i = 8; i < 16; i++)
dr.dr[i] = 0;
return (set_dbregs(td, &dr));
}

141
sys/compat/ia32/ia32_reg.h Normal file
View File

@ -0,0 +1,141 @@
/*-
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* William Jolitz.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from: @(#)reg.h 5.5 (Berkeley) 1/18/91
* $FreeBSD$
*/
#ifndef _COMPAT_IA32_IA32_REG_H_
#define _COMPAT_IA32_IA32_REG_H_
/*
* Register set accessible via /proc/$pid/regs and PT_{SET,GET}REGS.
*/
struct reg32 {
unsigned int r_fs;
unsigned int r_es;
unsigned int r_ds;
unsigned int r_edi;
unsigned int r_esi;
unsigned int r_ebp;
unsigned int r_isp;
unsigned int r_ebx;
unsigned int r_edx;
unsigned int r_ecx;
unsigned int r_eax;
unsigned int r_trapno;
unsigned int r_err;
unsigned int r_eip;
unsigned int r_cs;
unsigned int r_eflags;
unsigned int r_esp;
unsigned int r_ss;
unsigned int r_gs;
};
/*
* Register set accessible via /proc/$pid/fpregs.
*/
struct fpreg32 {
unsigned int fpr_env[7];
unsigned char fpr_acc[8][10];
unsigned int fpr_ex_sw;
unsigned char fpr_pad[64];
};
/*
* Register set accessible via /proc/$pid/dbregs.
*/
struct dbreg32 {
unsigned int dr[8]; /* debug registers */
};
/* Environment information of floating point unit */
struct env87 {
int en_cw; /* control word (16bits) */
int en_sw; /* status word (16bits) */
int en_tw; /* tag word (16bits) */
int en_fip; /* floating point instruction pointer */
u_short en_fcs; /* floating code segment selector */
u_short en_opcode; /* opcode last executed (11 bits ) */
int en_foo; /* floating operand offset */
int en_fos; /* floating operand segment selector */
};
#ifdef __ia64__
/* Layout of an x87 fpu register (amd64 gets this elsewhere) */
struct fpacc87 {
u_char fp_bytes[10];
};
#endif
/* Floating point context */
struct save87 {
struct env87 sv_env; /* floating point control/status */
struct fpacc87 sv_ac[8]; /* accumulator contents, 0-7 */
u_char sv_pad0[4]; /* padding for (now unused) saved status word */
u_char sv_pad[64]; /* padding; used by emulators */
};
/*
* Alternative layouts for <sys/procfs.h>
* Used in core dumps, the reason for this file existing.
*/
struct prstatus32 {
int pr_version;
u_int pr_statussz;
u_int pr_gregsetsz;
u_int pr_fpregsetsz;
int pr_osreldate;
int pr_cursig;
pid_t pr_pid;
struct reg32 pr_reg;
};
struct prpsinfo32 {
int pr_version;
u_int pr_psinfosz;
char pr_fname[PRFNAMESZ+1];
char pr_psargs[PRARGSZ+1];
};
/*
* Wrappers and converters.
*/
int fill_regs32(struct thread *, struct reg32 *);
int set_regs32(struct thread *, struct reg32 *);
int fill_fpregs32(struct thread *, struct fpreg32 *);
int set_fpregs32(struct thread *, struct fpreg32 *);
int fill_dbregs32(struct thread *, struct dbreg32 *);
int set_dbregs32(struct thread *, struct dbreg32 *);
#endif /* !_COMPAT_IA32_IA32_REG_H_ */

View File

@ -188,6 +188,7 @@ pci/agp_intel.c optional agp
# IA32 binary support
#
#amd64/ia32/ia32_exception.S optional compat_ia32
amd64/ia32/ia32_reg.c optional compat_ia32
amd64/ia32/ia32_signal.c optional compat_ia32
amd64/ia32/ia32_sigtramp.S optional compat_ia32
amd64/ia32/ia32_syscall.c optional compat_ia32

View File

@ -89,6 +89,7 @@ ia64/acpica/madt.c optional acpi
ia64/disasm/disasm_decode.c standard
ia64/disasm/disasm_extract.c standard
ia64/disasm/disasm_format.c standard
ia64/ia32/ia32_reg.c optional compat_ia32
ia64/ia32/ia32_signal.c optional compat_ia32
ia64/ia32/ia32_sigtramp.c optional compat_ia32
ia64/ia32/ia32_trap.c optional compat_ia32

View File

@ -43,6 +43,8 @@
* $FreeBSD$
*/
#include "opt_compat.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/lock.h>
@ -56,11 +58,42 @@
#include <fs/pseudofs/pseudofs.h>
#include <fs/procfs/procfs.h>
#ifdef COMPAT_IA32
#include <sys/procfs.h>
#include <machine/fpu.h>
#include <compat/ia32/ia32_reg.h>
extern struct sysentvec ia32_freebsd_sysvec;
/*
* PROC(write, dbregs, td2, &r) becomes
* proc_write_dbregs(td2, &r) or
* proc_write_dbregs32(td2, &r32)
*
* UIOMOVE_FROMBUF(r, uio) becomes
* uiomove_frombuf(&r, sizeof(r), uio) or
* uiomove_frombuf(&r32, sizeof(r32), uio)
*/
#define PROC(d, w, t, r) wrap32 ? \
proc_ ## d ## _ ## w ## 32(t, r ## 32) : \
proc_ ## d ## _ ## w(t, r)
#define UIOMOVE_FROMBUF(k, u) wrap32 ? \
uiomove_frombuf(& k ## 32, sizeof(k ## 32), u) : \
uiomove_frombuf(& k, sizeof(k), u)
#else
#define PROC(d, w, t, r) proc_ ## d ## _ ## w(t, r)
#define UIOMOVE_FROMBUF(k, u) uiomove_frombuf(& k, sizeof(k), u)
#endif
int
procfs_doprocdbregs(PFS_FILL_ARGS)
{
int error;
struct dbreg r;
struct thread *td2;
#ifdef COMPAT_IA32
struct dbreg32 r32;
int wrap32 = 0;
#endif
PROC_LOCK(p);
KASSERT(p->p_lock > 0, ("proc not held"));
@ -70,10 +103,20 @@ procfs_doprocdbregs(PFS_FILL_ARGS)
}
/* XXXKSE: */
error = proc_read_dbregs(FIRST_THREAD_IN_PROC(p), &r);
td2 = FIRST_THREAD_IN_PROC(p);
#ifdef COMPAT_IA32
if (td->td_proc->p_sysent == &ia32_freebsd_sysvec) {
if (td2->td_proc->p_sysent != &ia32_freebsd_sysvec) {
PROC_UNLOCK(p);
return (EINVAL);
}
wrap32 = 1;
}
#endif
error = PROC(read, dbregs, td2, &r);
if (error == 0) {
PROC_UNLOCK(p);
error = uiomove_frombuf(&r, sizeof(r), uio);
error = UIOMOVE_FROMBUF(r, uio);
PROC_LOCK(p);
}
if (error == 0 && uio->uio_rw == UIO_WRITE) {
@ -81,7 +124,7 @@ procfs_doprocdbregs(PFS_FILL_ARGS)
error = EBUSY;
else
/* XXXKSE: */
error = proc_write_dbregs(FIRST_THREAD_IN_PROC(p), &r);
error = PROC(write, dbregs, td2, &r);
}
PROC_UNLOCK(p);

View File

@ -37,6 +37,8 @@
* $FreeBSD$
*/
#include "opt_compat.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/lock.h>
@ -50,11 +52,42 @@
#include <fs/pseudofs/pseudofs.h>
#include <fs/procfs/procfs.h>
#ifdef COMPAT_IA32
#include <sys/procfs.h>
#include <machine/fpu.h>
#include <compat/ia32/ia32_reg.h>
extern struct sysentvec ia32_freebsd_sysvec;
/*
* PROC(write, fpregs, td2, &r) becomes
* proc_write_fpregs(td2, &r) or
* proc_write_fpregs32(td2, &r32)
*
* UIOMOVE_FROMBUF(r, uio) becomes
* uiomove_frombuf(&r, sizeof(r), uio) or
* uiomove_frombuf(&r32, sizeof(r32), uio)
*/
#define PROC(d, w, t, r) wrap32 ? \
proc_ ## d ## _ ## w ## 32(t, r ## 32) : \
proc_ ## d ## _ ## w(t, r)
#define UIOMOVE_FROMBUF(k, u) wrap32 ? \
uiomove_frombuf(& k ## 32, sizeof(k ## 32), u) : \
uiomove_frombuf(& k, sizeof(k), u)
#else
#define PROC(d, w, t, r) proc_ ## d ## _ ## w(t, r)
#define UIOMOVE_FROMBUF(k, u) uiomove_frombuf(& k, sizeof(k), u)
#endif
int
procfs_doprocfpregs(PFS_FILL_ARGS)
{
int error;
struct fpreg r;
struct thread *td2;
#ifdef COMPAT_IA32
struct fpreg32 r32;
int wrap32 = 0;
#endif
PROC_LOCK(p);
KASSERT(p->p_lock > 0, ("proc not held"));
@ -64,10 +97,20 @@ procfs_doprocfpregs(PFS_FILL_ARGS)
}
/* XXXKSE: */
error = proc_read_fpregs(FIRST_THREAD_IN_PROC(p), &r);
td2 = FIRST_THREAD_IN_PROC(p);
#ifdef COMPAT_IA32
if (td->td_proc->p_sysent == &ia32_freebsd_sysvec) {
if (td2->td_proc->p_sysent != &ia32_freebsd_sysvec) {
PROC_UNLOCK(p);
return (EINVAL);
}
wrap32 = 1;
}
#endif
error = PROC(read, fpregs, td2, &r);
if (error == 0) {
PROC_UNLOCK(p);
error = uiomove_frombuf(&r, sizeof(r), uio);
error = UIOMOVE_FROMBUF(r, uio);
PROC_LOCK(p);
}
if (error == 0 && uio->uio_rw == UIO_WRITE) {
@ -75,7 +118,7 @@ procfs_doprocfpregs(PFS_FILL_ARGS)
error = EBUSY;
else
/* XXXKSE: */
error = proc_write_fpregs(FIRST_THREAD_IN_PROC(p), &r);
error = PROC(write, fpregs, td2, &r);
}
PROC_UNLOCK(p);

View File

@ -41,6 +41,19 @@
#include <fs/pseudofs/pseudofs.h>
#include <fs/procfs/procfs.h>
#ifdef COMPAT_IA32
struct procfs_status32 {
int state; /* Running, stopped, something else? */
int flags; /* Any flags */
unsigned int events; /* Events to stop on */
int why; /* What event, if any, proc stopped on */
unsigned int val; /* Any extra data */
};
#define PIOCWAIT32 _IOR('p', 4, struct procfs_status32)
#define PIOCSTATUS32 _IOR('p', 6, struct procfs_status32)
#endif
/*
* Process ioctls
*/
@ -48,6 +61,9 @@ int
procfs_ioctl(PFS_IOCTL_ARGS)
{
struct procfs_status *ps;
#ifdef COMPAT_IA32
struct procfs_status32 *ps32;
#endif
int error, flags, sig;
PROC_LOCK(p);
@ -94,6 +110,25 @@ procfs_ioctl(PFS_IOCTL_ARGS)
ps->why = p->p_step ? p->p_stype : 0;
ps->val = p->p_step ? p->p_xstat : 0;
break;
#ifdef COMPAT_IA32
case PIOCWAIT32:
while (p->p_step == 0) {
/* sleep until p stops */
error = msleep(&p->p_stype, &p->p_mtx,
PWAIT|PCATCH, "pioctl", 0);
if (error != 0)
break;
}
/* fall through to PIOCSTATUS32 */
case PIOCSTATUS32:
ps32 = (struct procfs_status32 *)data;
ps32->state = (p->p_step == 0);
ps32->flags = 0; /* nope */
ps32->events = p->p_stops;
ps32->why = p->p_step ? p->p_stype : 0;
ps32->val = p->p_step ? p->p_xstat : 0;
break;
#endif
#if defined(COMPAT_FREEBSD5) || defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IOC(IOC_IN, 'p', 5, 0):
#endif

View File

@ -35,6 +35,8 @@
* $FreeBSD$
*/
#include "opt_compat.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/lock.h>
@ -54,6 +56,14 @@
#include <vm/vm_page.h>
#include <vm/vm_object.h>
#ifdef COMPAT_IA32
#include <sys/procfs.h>
#include <machine/fpu.h>
#include <compat/ia32/ia32_reg.h>
extern struct sysentvec ia32_freebsd_sysvec;
#endif
#define MEBUFFERSIZE 256
@ -77,6 +87,9 @@ procfs_doprocmap(PFS_FILL_ARGS)
vm_map_entry_t entry;
char mebuffer[MEBUFFERSIZE];
char *fullpath, *freepath;
#ifdef COMPAT_IA32
int wrap32 = 0;
#endif
GIANT_REQUIRED;
@ -92,6 +105,13 @@ procfs_doprocmap(PFS_FILL_ARGS)
if (uio->uio_offset != 0)
return (0);
#ifdef COMPAT_IA32
if (curthread->td_proc->p_sysent == &ia32_freebsd_sysvec) {
if (p->p_sysent != &ia32_freebsd_sysvec)
return (EOPNOTSUPP);
wrap32 = 1;
}
#endif
error = 0;
if (map != &curthread->td_proc->p_vmspace->vm_map)
vm_map_lock_read(map);
@ -164,7 +184,12 @@ procfs_doprocmap(PFS_FILL_ARGS)
snprintf(mebuffer, sizeof mebuffer,
"0x%lx 0x%lx %d %d %p %s%s%s %d %d 0x%x %s %s %s %s\n",
(u_long)entry->start, (u_long)entry->end,
resident, privateresident, obj,
resident, privateresident,
#ifdef COMPAT_IA32
wrap32 ? NULL : obj, /* Hide 64 bit value */
#else
obj,
#endif
(entry->protection & VM_PROT_READ)?"r":"-",
(entry->protection & VM_PROT_WRITE)?"w":"-",
(entry->protection & VM_PROT_EXECUTE)?"x":"-",

View File

@ -37,6 +37,8 @@
* $FreeBSD$
*/
#include "opt_compat.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/lock.h>
@ -50,11 +52,42 @@
#include <fs/pseudofs/pseudofs.h>
#include <fs/procfs/procfs.h>
#ifdef COMPAT_IA32
#include <sys/procfs.h>
#include <machine/fpu.h>
#include <compat/ia32/ia32_reg.h>
extern struct sysentvec ia32_freebsd_sysvec;
/*
* PROC(write, regs, td2, &r) becomes
* proc_write_regs(td2, &r) or
* proc_write_regs32(td2, &r32)
*
* UIOMOVE_FROMBUF(r, uio) becomes
* uiomove_frombuf(&r, sizeof(r), uio) or
* uiomove_frombuf(&r32, sizeof(r32), uio)
*/
#define PROC(d, w, t, r) wrap32 ? \
proc_ ## d ## _ ## w ## 32(t, r ## 32) : \
proc_ ## d ## _ ## w(t, r)
#define UIOMOVE_FROMBUF(k, u) wrap32 ? \
uiomove_frombuf(& k ## 32, sizeof(k ## 32), u) : \
uiomove_frombuf(& k, sizeof(k), u)
#else
#define PROC(d, w, t, r) proc_ ## d ## _ ## w(t, r)
#define UIOMOVE_FROMBUF(k, u) uiomove_frombuf(& k, sizeof(k), u)
#endif
int
procfs_doprocregs(PFS_FILL_ARGS)
{
int error;
struct reg r;
struct thread *td2;
#ifdef COMPAT_IA32
struct reg32 r32;
int wrap32 = 0;
#endif
PROC_LOCK(p);
KASSERT(p->p_lock > 0, ("proc not held"));
@ -64,10 +97,20 @@ procfs_doprocregs(PFS_FILL_ARGS)
}
/* XXXKSE: */
error = proc_read_regs(FIRST_THREAD_IN_PROC(p), &r);
td2 = FIRST_THREAD_IN_PROC(p);
#ifdef COMPAT_IA32
if (td->td_proc->p_sysent == &ia32_freebsd_sysvec) {
if (td2->td_proc->p_sysent != &ia32_freebsd_sysvec) {
PROC_UNLOCK(p);
return (EINVAL);
}
wrap32 = 1;
}
#endif
error = PROC(read, regs, td2, &r);
if (error == 0) {
PROC_UNLOCK(p);
error = uiomove_frombuf(&r, sizeof(r), uio);
error = UIOMOVE_FROMBUF(r, uio);
PROC_LOCK(p);
}
if (error == 0 && uio->uio_rw == UIO_WRITE) {
@ -75,7 +118,7 @@ procfs_doprocregs(PFS_FILL_ARGS)
error = EBUSY;
else
/* XXXKSE: */
error = proc_write_regs(FIRST_THREAD_IN_PROC(p), &r);
error = PROC(write, regs, td2, &r);
}
PROC_UNLOCK(p);

82
sys/ia64/ia32/ia32_reg.c Normal file
View File

@ -0,0 +1,82 @@
/*-
* Copyright (c) 2005 Peter Wemm
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/procfs.h>
#include <compat/ia32/ia32_reg.h>
int
fill_regs32(struct thread *td, struct reg32 *regs)
{
bzero(regs, sizeof(*regs));
return (EOPNOTSUPP);
}
int
set_regs32(struct thread *td, struct reg32 *regs)
{
return (EOPNOTSUPP);
}
int
fill_fpregs32(struct thread *td, struct fpreg32 *regs)
{
bzero(regs, sizeof(*regs));
return (EOPNOTSUPP);
}
int
set_fpregs32(struct thread *td, struct fpreg32 *regs)
{
return (EOPNOTSUPP);
}
int
fill_dbregs32(struct thread *td, struct dbreg32 *regs)
{
bzero(regs, sizeof(*regs));
return (EOPNOTSUPP);
}
int
set_dbregs32(struct thread *td, struct dbreg32 *regs)
{
return (EOPNOTSUPP);
}

View File

@ -31,6 +31,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_compat.h"
#include <sys/param.h>
#include <sys/exec.h>
#include <sys/fcntl.h>
@ -66,6 +68,11 @@ __FBSDID("$FreeBSD$");
#include <machine/elf.h>
#include <machine/md_var.h>
#if defined(COMPAT_IA32) && __ELF_WORD_SIZE == 32
#include <machine/fpu.h>
#include <compat/ia32/ia32_reg.h>
#endif
#define OLD_EI_BRAND 8
static int __elfN(check_header)(const Elf_Ehdr *hdr);
@ -1118,17 +1125,31 @@ __elfN(corehdr)(td, vp, cred, numsegs, hdr, hdrsize)
td)); /* XXXKSE */
}
#if defined(COMPAT_IA32) && __ELF_WORD_SIZE == 32
typedef struct prstatus32 elf_prstatus_t;
typedef struct prpsinfo32 elf_prpsinfo_t;
typedef struct fpreg32 elf_prfpregset_t;
typedef struct fpreg32 elf_fpregset_t;
typedef struct reg32 elf_gregset_t;
#else
typedef prstatus_t elf_prstatus_t;
typedef prpsinfo_t elf_prpsinfo_t;
typedef prfpregset_t elf_prfpregset_t;
typedef prfpregset_t elf_fpregset_t;
typedef gregset_t elf_gregset_t;
#endif
static void
__elfN(puthdr)(struct thread *td, void *dst, size_t *off, int numsegs)
{
struct {
prstatus_t status;
prfpregset_t fpregset;
prpsinfo_t psinfo;
elf_prstatus_t status;
elf_prfpregset_t fpregset;
elf_prpsinfo_t psinfo;
} *tempdata;
prstatus_t *status;
prfpregset_t *fpregset;
prpsinfo_t *psinfo;
elf_prstatus_t *status;
elf_prfpregset_t *fpregset;
elf_prpsinfo_t *psinfo;
struct proc *p;
struct thread *thr;
size_t ehoff, noteoff, notesz, phoff;
@ -1160,7 +1181,7 @@ __elfN(puthdr)(struct thread *td, void *dst, size_t *off, int numsegs)
if (dst != NULL) {
psinfo->pr_version = PRPSINFO_VERSION;
psinfo->pr_psinfosz = sizeof(prpsinfo_t);
psinfo->pr_psinfosz = sizeof(elf_prpsinfo_t);
strlcpy(psinfo->pr_fname, p->p_comm, sizeof(psinfo->pr_fname));
/*
* XXX - We don't fill in the command line arguments properly
@ -1182,14 +1203,19 @@ __elfN(puthdr)(struct thread *td, void *dst, size_t *off, int numsegs)
while (thr != NULL) {
if (dst != NULL) {
status->pr_version = PRSTATUS_VERSION;
status->pr_statussz = sizeof(prstatus_t);
status->pr_gregsetsz = sizeof(gregset_t);
status->pr_fpregsetsz = sizeof(fpregset_t);
status->pr_statussz = sizeof(elf_prstatus_t);
status->pr_gregsetsz = sizeof(elf_gregset_t);
status->pr_fpregsetsz = sizeof(elf_fpregset_t);
status->pr_osreldate = osreldate;
status->pr_cursig = p->p_sig;
status->pr_pid = thr->td_tid;
#if defined(COMPAT_IA32) && __ELF_WORD_SIZE == 32
fill_regs32(thr, &status->pr_reg);
fill_fpregs32(thr, fpregset);
#else
fill_regs(thr, &status->pr_reg);
fill_fpregs(thr, fpregset);
#endif
}
__elfN(putnote)(dst, off, "FreeBSD", NT_PRSTATUS, status,
sizeof *status);
@ -1235,7 +1261,11 @@ __elfN(puthdr)(struct thread *td, void *dst, size_t *off, int numsegs)
ehdr->e_ident[EI_ABIVERSION] = 0;
ehdr->e_ident[EI_PAD] = 0;
ehdr->e_type = ET_CORE;
#if defined(COMPAT_IA32) && __ELF_WORD_SIZE == 32
ehdr->e_machine = EM_386;
#else
ehdr->e_machine = ELF_ARCH;
#endif
ehdr->e_version = EV_CURRENT;
ehdr->e_entry = 0;
ehdr->e_phoff = phoff;

View File

@ -37,6 +37,7 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_compat.h"
#include "opt_hwpmc_hooks.h"
#include "opt_ntp.h"
#include "opt_watchdog.h"
@ -84,8 +85,36 @@ SYSINIT(clocks, SI_SUB_CLOCKS, SI_ORDER_FIRST, initclocks, NULL)
/* Some of these don't belong here, but it's easiest to concentrate them. */
long cp_time[CPUSTATES];
SYSCTL_OPAQUE(_kern, OID_AUTO, cp_time, CTLFLAG_RD, &cp_time, sizeof(cp_time),
"LU", "CPU time statistics");
#ifdef COMPAT_IA32
extern struct sysentvec ia32_freebsd_sysvec;
#endif
static int
sysctl_kern_cp_time(SYSCTL_HANDLER_ARGS)
{
int error;
#ifdef COMPAT_IA32
int i;
unsigned int cp_time32[CPUSTATES];
if (req->td->td_proc->p_sysent == &ia32_freebsd_sysvec) {
if (!req->oldptr)
return SYSCTL_OUT(req, 0, sizeof(cp_time32));
for (i = 0; i < CPUSTATES; i++)
cp_time32[i] = (unsigned int)cp_time[i];
error = SYSCTL_OUT(req, cp_time32, sizeof(cp_time32));
} else
#endif
{
if (!req->oldptr)
return SYSCTL_OUT(req, 0, sizeof(cp_time));
error = SYSCTL_OUT(req, cp_time, sizeof(cp_time));
}
return error;
}
SYSCTL_PROC(_kern, OID_AUTO, cp_time, CTLTYPE_LONG|CTLFLAG_RD,
0,0, sysctl_kern_cp_time, "LU", "CPU time statistics");
#ifdef SW_WATCHDOG
#include <sys/watchdog.h>

View File

@ -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 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data)
sx_xunlock(&proctree_lock);
return (error);
}
#undef PROC_READ
#undef PROC_WRITE
/*
* Stop a process because of a debugging event;

View File

@ -135,6 +135,17 @@ int proc_read_dbregs(struct thread *_td, struct dbreg *_dbreg);
int proc_write_dbregs(struct thread *_td, struct dbreg *_dbreg);
int proc_sstep(struct thread *_td);
int proc_rwmem(struct proc *_p, struct uio *_uio);
#ifdef COMPAT_IA32
struct reg32;
struct fpreg32;
struct dbreg32;
int proc_read_regs32(struct thread *_td, struct reg32 *_reg32);
int proc_write_regs32(struct thread *_td, struct reg32 *_reg32);
int proc_read_fpregs32(struct thread *_td, struct fpreg32 *_fpreg32);
int proc_write_fpregs32(struct thread *_td, struct fpreg32 *_fpreg32);
int proc_read_dbregs32(struct thread *_td, struct dbreg32 *_dbreg32);
int proc_write_dbregs32(struct thread *_td, struct dbreg32 *_dbreg32);
#endif
#else /* !_KERNEL */
#include <sys/cdefs.h>