Home | History | Annotate | Line # | Download | only in i386
linux_ptrace.c revision 1.7
      1  1.7     lukem /*	$NetBSD: linux_ptrace.c,v 1.7 2001/11/13 02:08:39 lukem Exp $	*/
      2  1.1      tron 
      3  1.1      tron /*-
      4  1.1      tron  * Copyright (c) 1999 The NetBSD Foundation, Inc.
      5  1.1      tron  * All rights reserved.
      6  1.1      tron  *
      7  1.1      tron  * This code is derived from software contributed to The NetBSD Foundation
      8  1.1      tron  * by Matthias Scheler.
      9  1.1      tron  *
     10  1.1      tron  * Redistribution and use in source and binary forms, with or without
     11  1.1      tron  * modification, are permitted provided that the following conditions
     12  1.1      tron  * are met:
     13  1.1      tron  * 1. Redistributions of source code must retain the above copyright
     14  1.1      tron  *    notice, this list of conditions and the following disclaimer.
     15  1.1      tron  * 2. Redistributions in binary form must reproduce the above copyright
     16  1.1      tron  *    notice, this list of conditions and the following disclaimer in the
     17  1.1      tron  *    documentation and/or other materials provided with the distribution.
     18  1.1      tron  * 3. All advertising materials mentioning features or use of this software
     19  1.1      tron  *    must display the following acknowledgement:
     20  1.1      tron  *	This product includes software developed by the NetBSD
     21  1.1      tron  *	Foundation, Inc. and its contributors.
     22  1.1      tron  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  1.1      tron  *    contributors may be used to endorse or promote products derived
     24  1.1      tron  *    from this software without specific prior written permission.
     25  1.1      tron  *
     26  1.1      tron  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  1.1      tron  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  1.1      tron  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  1.1      tron  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  1.1      tron  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  1.1      tron  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  1.1      tron  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  1.1      tron  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  1.1      tron  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  1.1      tron  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  1.1      tron  * POSSIBILITY OF SUCH DAMAGE.
     37  1.1      tron  */
     38  1.7     lukem 
     39  1.7     lukem #include <sys/cdefs.h>
     40  1.7     lukem __KERNEL_RCSID(0, "$NetBSD: linux_ptrace.c,v 1.7 2001/11/13 02:08:39 lukem Exp $");
     41  1.1      tron 
     42  1.1      tron #include <sys/types.h>
     43  1.1      tron #include <sys/param.h>
     44  1.5  jdolecek #include <sys/malloc.h>
     45  1.1      tron #include <sys/mount.h>
     46  1.1      tron #include <sys/proc.h>
     47  1.1      tron #include <sys/ptrace.h>
     48  1.1      tron #include <sys/systm.h>
     49  1.1      tron #include <sys/syscallargs.h>
     50  1.5  jdolecek #include <uvm/uvm_extern.h>
     51  1.1      tron 
     52  1.3      tron #include <machine/reg.h>
     53  1.3      tron 
     54  1.1      tron #include <compat/linux/common/linux_types.h>
     55  1.1      tron #include <compat/linux/common/linux_ptrace.h>
     56  1.1      tron #include <compat/linux/common/linux_signal.h>
     57  1.1      tron 
     58  1.1      tron #include <compat/linux/common/linux_util.h>
     59  1.1      tron #include <compat/linux/common/linux_machdep.h>
     60  1.5  jdolecek #include <compat/linux/common/linux_emuldata.h>
     61  1.5  jdolecek #include <compat/linux/common/linux_exec.h>	/* for emul_linux */
     62  1.1      tron 
     63  1.1      tron #include <compat/linux/linux_syscallargs.h>
     64  1.2      tron 
     65  1.5  jdolecek #include <lib/libkern/libkern.h>	/* for offsetof() */
     66  1.5  jdolecek 
     67  1.2      tron struct linux_reg {
     68  1.2      tron 	long ebx;
     69  1.2      tron 	long ecx;
     70  1.2      tron 	long edx;
     71  1.2      tron 	long esi;
     72  1.2      tron 	long edi;
     73  1.2      tron 	long ebp;
     74  1.2      tron 	long eax;
     75  1.6     lukem 	long xds, xes;		/* unsigned short ds, __ds, es, __es; */
     76  1.6     lukem 	long __fs, __gs;	/* unsigned short fs, __fs, gs, __gs; */
     77  1.2      tron 	long orig_eax;
     78  1.2      tron 	long eip;
     79  1.6     lukem 	long xcs;		/* unsigned short cs, __cs; */
     80  1.2      tron 	long eflags;
     81  1.2      tron 	long esp;
     82  1.6     lukem 	long xss;		/* unsigned short ss, __ss; */
     83  1.5  jdolecek };
     84  1.5  jdolecek 
     85  1.5  jdolecek /* structure used for storing floating point context */
     86  1.5  jdolecek struct linux_fpreg {
     87  1.5  jdolecek   long cwd;
     88  1.5  jdolecek   long swd;
     89  1.5  jdolecek   long twd;
     90  1.5  jdolecek   long fip;
     91  1.5  jdolecek   long fcs;
     92  1.5  jdolecek   long foo;
     93  1.5  jdolecek   long fos;
     94  1.5  jdolecek   long st_space [20];
     95  1.2      tron };
     96  1.2      tron 
     97  1.5  jdolecek /* user struct for linux process - this is used for Linux ptrace emulation */
     98  1.5  jdolecek /* most of it is junk only used by gdb */
     99  1.5  jdolecek struct linux_user {
    100  1.5  jdolecek   struct linux_reg regs;	/* registers */
    101  1.5  jdolecek   int u_fpvalid;		/* true if math co-processor being used. */
    102  1.5  jdolecek   struct linux_fpreg i387;	/* Math Co-processor registers. */
    103  1.5  jdolecek /* The rest of this junk is to help gdb figure out what goes where */
    104  1.5  jdolecek #define lusr_startgdb	u_tsize
    105  1.5  jdolecek   unsigned long int u_tsize;	/* Text segment size (pages). */
    106  1.5  jdolecek   unsigned long int u_dsize;	/* Data segment size (pages). */
    107  1.5  jdolecek   unsigned long int u_ssize;	/* Stack segment size (pages). */
    108  1.5  jdolecek   unsigned long start_code;     /* Starting virtual address of text. */
    109  1.5  jdolecek   unsigned long start_stack;	/* Starting virtual address of stack area.
    110  1.5  jdolecek 				   This is actually the bottom of the stack,
    111  1.5  jdolecek 				   the top of the stack is always found in the
    112  1.5  jdolecek 				   esp register.  */
    113  1.5  jdolecek   long int __signal;  		/* Signal that caused the core dump. */
    114  1.5  jdolecek   int __reserved;		/* unused */
    115  1.5  jdolecek   void * u_ar0;			/* Used by gdb to help find the values for */
    116  1.5  jdolecek 				/* the registers. */
    117  1.5  jdolecek   struct linux_fpreg* u_fpstate;/* Math Co-processor pointer. */
    118  1.5  jdolecek   unsigned long __magic;	/* To uniquely identify a core file */
    119  1.5  jdolecek   char u_comm[32];		/* User command that was responsible */
    120  1.5  jdolecek   int u_debugreg[8];
    121  1.5  jdolecek #define u_debugreg_end	u_debugreg[7]
    122  1.5  jdolecek };
    123  1.5  jdolecek 
    124  1.5  jdolecek #define LUSR_OFF(member)	offsetof(struct linux_user, member)
    125  1.5  jdolecek #define ISSET(t, f)		((t) & (f))
    126  1.3      tron 
    127  1.1      tron int
    128  1.3      tron linux_sys_ptrace_arch(p, v, retval)
    129  1.1      tron 	struct proc *p;
    130  1.1      tron 	void *v;
    131  1.1      tron 	register_t *retval;
    132  1.1      tron {
    133  1.1      tron 	struct linux_sys_ptrace_args /* {
    134  1.1      tron 		syscallarg(int) request;
    135  1.1      tron 		syscallarg(int) pid;
    136  1.1      tron 		syscallarg(int) addr;
    137  1.1      tron 		syscallarg(int) data;
    138  1.1      tron 	} */ *uap = v;
    139  1.3      tron 	int request, error;
    140  1.3      tron 	struct proc *t;				/* target process */
    141  1.5  jdolecek 	struct reg *regs = NULL;
    142  1.5  jdolecek 	struct fpreg *fpregs = NULL;
    143  1.5  jdolecek 	struct linux_reg *linux_regs = NULL;
    144  1.5  jdolecek 	struct linux_fpreg *linux_fpregs = NULL;
    145  1.5  jdolecek 	int addr;
    146  1.3      tron 
    147  1.3      tron 	request = SCARG(uap, request);
    148  1.3      tron 
    149  1.5  jdolecek 	if ((request != LINUX_PTRACE_PEEKUSR) &&
    150  1.5  jdolecek 	    (request != LINUX_PTRACE_POKEUSR) &&
    151  1.5  jdolecek 	    (request != LINUX_PTRACE_GETREGS) &&
    152  1.3      tron 	    (request != LINUX_PTRACE_SETREGS) &&
    153  1.3      tron 	    (request != LINUX_PTRACE_GETFPREGS) &&
    154  1.3      tron 	    (request != LINUX_PTRACE_SETFPREGS))
    155  1.3      tron 		return EIO;
    156  1.1      tron 
    157  1.3      tron 	/* Find the process we're supposed to be operating on. */
    158  1.3      tron 	if ((t = pfind(SCARG(uap, pid))) == NULL)
    159  1.3      tron 		return ESRCH;
    160  1.3      tron 
    161  1.3      tron 	/*
    162  1.3      tron 	 * You can't do what you want to the process if:
    163  1.3      tron 	 *	(1) It's not being traced at all,
    164  1.3      tron 	 */
    165  1.3      tron 	if (!ISSET(t->p_flag, P_TRACED))
    166  1.3      tron 		return EPERM;
    167  1.3      tron 
    168  1.3      tron 	/*
    169  1.3      tron 	 *	(2) it's being traced by procfs (which has
    170  1.3      tron 	 *	    different signal delivery semantics),
    171  1.3      tron 	 */
    172  1.3      tron 	if (ISSET(t->p_flag, P_FSTRACE))
    173  1.3      tron 		return EBUSY;
    174  1.3      tron 
    175  1.3      tron 	/*
    176  1.3      tron 	 *	(3) it's not being traced by _you_, or
    177  1.3      tron 	 */
    178  1.3      tron 	if (t->p_pptr != p)
    179  1.3      tron 		return EBUSY;
    180  1.3      tron 
    181  1.3      tron 	/*
    182  1.3      tron 	 *	(4) it's not currently stopped.
    183  1.3      tron 	 */
    184  1.3      tron 	if (t->p_stat != SSTOP || !ISSET(t->p_flag, P_WAITED))
    185  1.3      tron 		return EBUSY;
    186  1.3      tron 
    187  1.3      tron 	*retval = 0;
    188  1.3      tron 
    189  1.3      tron 	switch (request) {
    190  1.3      tron 	case  LINUX_PTRACE_GETREGS:
    191  1.5  jdolecek 		MALLOC(regs, struct reg*, sizeof(struct reg), M_TEMP, M_WAITOK);
    192  1.5  jdolecek 		MALLOC(linux_regs, struct linux_reg*, sizeof(struct linux_reg),
    193  1.5  jdolecek 			M_TEMP, M_WAITOK);
    194  1.5  jdolecek 
    195  1.5  jdolecek 		error = process_read_regs(t, regs);
    196  1.3      tron 		if (error != 0)
    197  1.5  jdolecek 			goto out;
    198  1.3      tron 
    199  1.5  jdolecek 		linux_regs->ebx = regs->r_ebx;
    200  1.5  jdolecek 		linux_regs->ecx = regs->r_ecx;
    201  1.5  jdolecek 		linux_regs->edx = regs->r_edx;
    202  1.5  jdolecek 		linux_regs->esi = regs->r_esi;
    203  1.5  jdolecek 		linux_regs->edi = regs->r_edi;
    204  1.5  jdolecek 		linux_regs->ebp = regs->r_ebp;
    205  1.5  jdolecek 		linux_regs->eax = regs->r_eax;
    206  1.5  jdolecek 		linux_regs->xds = regs->r_ds;
    207  1.5  jdolecek 		linux_regs->xes = regs->r_es;
    208  1.5  jdolecek 		linux_regs->orig_eax = regs->r_eax; /* XXX is this correct? */
    209  1.5  jdolecek 		linux_regs->eip = regs->r_cs + regs->r_eip;
    210  1.5  jdolecek 		linux_regs->xcs = regs->r_cs;
    211  1.5  jdolecek 		linux_regs->eflags = regs->r_eflags;
    212  1.5  jdolecek 		linux_regs->esp = regs->r_esp;
    213  1.5  jdolecek 		linux_regs->xss = regs->r_ss;
    214  1.3      tron 
    215  1.5  jdolecek 		error = copyout(linux_regs, (caddr_t)SCARG(uap, data),
    216  1.3      tron 		    sizeof(struct linux_reg));
    217  1.5  jdolecek 		goto out;
    218  1.5  jdolecek 
    219  1.3      tron 	case  LINUX_PTRACE_SETREGS:
    220  1.5  jdolecek 		MALLOC(regs, struct reg*, sizeof(struct reg), M_TEMP, M_WAITOK);
    221  1.5  jdolecek 		MALLOC(linux_regs, struct linux_reg *, sizeof(struct linux_reg),
    222  1.5  jdolecek 			M_TEMP, M_WAITOK);
    223  1.5  jdolecek 
    224  1.5  jdolecek 		error = copyin((caddr_t)SCARG(uap, data), linux_regs,
    225  1.3      tron 		    sizeof(struct linux_reg));
    226  1.3      tron 		if (error != 0)
    227  1.5  jdolecek 			goto out;
    228  1.5  jdolecek 
    229  1.5  jdolecek 		regs->r_ebx = linux_regs->ebx;
    230  1.5  jdolecek 		regs->r_ecx = linux_regs->ecx;
    231  1.5  jdolecek 		regs->r_edx = linux_regs->edx;
    232  1.5  jdolecek 		regs->r_esi = linux_regs->esi;
    233  1.5  jdolecek 		regs->r_edi = linux_regs->edi;
    234  1.5  jdolecek 		regs->r_ebp = linux_regs->ebp;
    235  1.5  jdolecek 		regs->r_eax = linux_regs->eax;
    236  1.5  jdolecek 		regs->r_ds = linux_regs->xds;
    237  1.5  jdolecek 		regs->r_es = linux_regs->xes;
    238  1.5  jdolecek 		regs->r_eip = linux_regs->eip - linux_regs->xcs;
    239  1.5  jdolecek 		regs->r_cs = linux_regs->xcs;
    240  1.5  jdolecek 		regs->r_eflags = linux_regs->eflags;
    241  1.5  jdolecek 		regs->r_esp = linux_regs->esp;
    242  1.5  jdolecek 		regs->r_ss = linux_regs->xss;
    243  1.5  jdolecek 
    244  1.5  jdolecek 		error = process_write_regs(t, regs);
    245  1.5  jdolecek 		goto out;
    246  1.5  jdolecek 
    247  1.5  jdolecek 	case  LINUX_PTRACE_GETFPREGS:
    248  1.5  jdolecek 		MALLOC(fpregs, struct fpreg *, sizeof(struct fpreg),
    249  1.5  jdolecek 			M_TEMP, M_WAITOK);
    250  1.5  jdolecek 		MALLOC(linux_fpregs, struct linux_fpreg *,
    251  1.5  jdolecek 			sizeof(struct linux_fpreg), M_TEMP, M_WAITOK);
    252  1.5  jdolecek 
    253  1.5  jdolecek 		error = process_read_fpregs(t, fpregs);
    254  1.5  jdolecek 		if (error != 0)
    255  1.5  jdolecek 			goto out;
    256  1.3      tron 
    257  1.5  jdolecek 		/* zero the contents if NetBSD fpreg structure is smaller */
    258  1.5  jdolecek 		if (sizeof(struct fpreg) < sizeof(struct linux_fpreg))
    259  1.5  jdolecek 			memset(linux_fpregs, '\0', sizeof(struct linux_fpreg));
    260  1.5  jdolecek 
    261  1.5  jdolecek 		memcpy(linux_fpregs, fpregs,
    262  1.5  jdolecek 			min(sizeof(struct linux_fpreg), sizeof(struct fpreg)));
    263  1.5  jdolecek 		error = copyout(linux_fpregs, (caddr_t)SCARG(uap, data),
    264  1.5  jdolecek 		    sizeof(struct linux_fpreg));
    265  1.5  jdolecek 		goto out;
    266  1.5  jdolecek 
    267  1.5  jdolecek 	case  LINUX_PTRACE_SETFPREGS:
    268  1.5  jdolecek 		MALLOC(fpregs, struct fpreg *, sizeof(struct fpreg),
    269  1.5  jdolecek 			M_TEMP, M_WAITOK);
    270  1.5  jdolecek 		MALLOC(linux_fpregs, struct linux_fpreg *,
    271  1.5  jdolecek 			sizeof(struct linux_fpreg), M_TEMP, M_WAITOK);
    272  1.5  jdolecek 		error = copyin((caddr_t)SCARG(uap, data), linux_fpregs,
    273  1.5  jdolecek 		    sizeof(struct linux_fpreg));
    274  1.5  jdolecek 		if (error != 0)
    275  1.5  jdolecek 			goto out;
    276  1.1      tron 
    277  1.5  jdolecek 		memset(fpregs, '\0', sizeof(struct fpreg));
    278  1.5  jdolecek 		memcpy(fpregs, linux_fpregs,
    279  1.5  jdolecek 			min(sizeof(struct linux_fpreg), sizeof(struct fpreg)));
    280  1.5  jdolecek 
    281  1.5  jdolecek 		error = process_write_regs(t, regs);
    282  1.5  jdolecek 		goto out;
    283  1.5  jdolecek 
    284  1.5  jdolecek 	case  LINUX_PTRACE_PEEKUSR:
    285  1.5  jdolecek 		addr = SCARG(uap, addr);
    286  1.5  jdolecek 
    287  1.5  jdolecek 		PHOLD(t);	/* need full process info */
    288  1.5  jdolecek 		error = 0;
    289  1.5  jdolecek 		if (addr < LUSR_OFF(lusr_startgdb)) {
    290  1.5  jdolecek 			/* XXX should provide appropriate register */
    291  1.5  jdolecek 			error = 1;
    292  1.5  jdolecek 		} else if (addr == LUSR_OFF(u_tsize))
    293  1.5  jdolecek 			*retval = p->p_vmspace->vm_tsize;
    294  1.5  jdolecek 		else if (addr == LUSR_OFF(u_dsize))
    295  1.5  jdolecek 			*retval = p->p_vmspace->vm_dsize;
    296  1.5  jdolecek 		else if (addr == LUSR_OFF(u_ssize))
    297  1.5  jdolecek 			*retval = p->p_vmspace->vm_ssize;
    298  1.5  jdolecek 		else if (addr == LUSR_OFF(start_code))
    299  1.5  jdolecek 			*retval = (register_t) p->p_vmspace->vm_taddr;
    300  1.5  jdolecek 		else if (addr == LUSR_OFF(start_stack))
    301  1.5  jdolecek 			*retval = (register_t) p->p_vmspace->vm_minsaddr;
    302  1.5  jdolecek 		else if (addr == LUSR_OFF(u_ar0))
    303  1.5  jdolecek 			*retval = LUSR_OFF(regs);
    304  1.5  jdolecek 		else if (addr >= LUSR_OFF(u_debugreg)
    305  1.5  jdolecek 			   && addr <= LUSR_OFF(u_debugreg_end)) {
    306  1.5  jdolecek 			int off = (addr - LUSR_OFF(u_debugreg)) / sizeof(int);
    307  1.5  jdolecek 
    308  1.5  jdolecek 			/* only do this for Linux processes */
    309  1.5  jdolecek 			if (t->p_emul != &emul_linux)
    310  1.5  jdolecek 				error = EINVAL;
    311  1.5  jdolecek 			else {
    312  1.5  jdolecek 				*retval = ((struct linux_emuldata *)
    313  1.5  jdolecek 						t->p_emuldata)->debugreg[off];
    314  1.5  jdolecek 			}
    315  1.5  jdolecek 		} else if (addr == LUSR_OFF(__signal)) {
    316  1.5  jdolecek 			error = 1;
    317  1.5  jdolecek 		} else if (addr == LUSR_OFF(__signal)) {
    318  1.5  jdolecek 			error = 1;
    319  1.5  jdolecek 		} else if (addr == LUSR_OFF(u_fpstate)) {
    320  1.5  jdolecek 			error = 1;
    321  1.5  jdolecek 		} else if (addr == LUSR_OFF(__magic)) {
    322  1.5  jdolecek 			error = 1;
    323  1.5  jdolecek 		} else if (addr == LUSR_OFF(u_comm)) {
    324  1.5  jdolecek 			error = 1;
    325  1.5  jdolecek 		} else {
    326  1.5  jdolecek #ifdef DEBUG_LINUX
    327  1.5  jdolecek 			printf("linux_ptrace: unsupported address: %d\n", addr);
    328  1.5  jdolecek #endif
    329  1.5  jdolecek 			error = 1;
    330  1.5  jdolecek 		}
    331  1.5  jdolecek 
    332  1.5  jdolecek 		PRELE(t);
    333  1.5  jdolecek 
    334  1.5  jdolecek 		if (!error)
    335  1.5  jdolecek 			return 0;
    336  1.5  jdolecek 
    337  1.5  jdolecek 	case  LINUX_PTRACE_POKEUSR:
    338  1.5  jdolecek 		/* we only support setting debugregs for now */
    339  1.5  jdolecek 		addr = SCARG(uap, addr);
    340  1.5  jdolecek 		if (addr >= LUSR_OFF(u_debugreg)
    341  1.5  jdolecek 			   && addr <= LUSR_OFF(u_debugreg_end)) {
    342  1.5  jdolecek 			int off = (addr - LUSR_OFF(u_debugreg)) / sizeof(int);
    343  1.5  jdolecek 			int data = SCARG(uap, data);
    344  1.5  jdolecek 
    345  1.5  jdolecek 			/* only do this for Linux processes */
    346  1.5  jdolecek 			if (t->p_emul != &emul_linux)
    347  1.5  jdolecek 				return EINVAL;
    348  1.5  jdolecek 
    349  1.5  jdolecek 			PHOLD(t);
    350  1.5  jdolecek 			((struct linux_emuldata *)t->p_emuldata)->debugreg[off] = data;
    351  1.5  jdolecek 			PRELE(t);
    352  1.5  jdolecek 			return (0);
    353  1.5  jdolecek 		}
    354  1.5  jdolecek 
    355  1.5  jdolecek 		break;
    356  1.5  jdolecek 	default:
    357  1.5  jdolecek 		/* never reached */
    358  1.5  jdolecek 		break;
    359  1.1      tron 	}
    360  1.1      tron 
    361  1.3      tron 	return EIO;
    362  1.5  jdolecek 
    363  1.5  jdolecek     out:
    364  1.5  jdolecek 	if (regs)
    365  1.5  jdolecek 		FREE(regs, M_TEMP);
    366  1.5  jdolecek 	if (fpregs)
    367  1.5  jdolecek 		FREE(fpregs, M_TEMP);
    368  1.5  jdolecek 	if (linux_regs)
    369  1.5  jdolecek 		FREE(linux_regs, M_TEMP);
    370  1.5  jdolecek 	if (linux_fpregs)
    371  1.5  jdolecek 		FREE(linux_fpregs, M_TEMP);
    372  1.5  jdolecek 	return (error);
    373  1.1      tron }
    374