Patchwork [14/23] bsd-user: add shims for process related system calls

login
register
mail settings
Submitter Stacey Son
Date June 24, 2013, 2:03 a.m.
Message ID <1372039435-41921-15-git-send-email-sson@FreeBSD.org>
Download mbox | patch
Permalink /patch/253932/
State New
Headers show

Comments

Stacey Son - June 24, 2013, 2:03 a.m.
This change adds support for process related system calls including fork calls,
execve calls, wait4(2), getuid(2), etc. It also adds a feature to check the
target binary before exec'ing to see if it should run native or under emulation.
This can be useful when there is not kernel support for something like
binfmt_misc as in Linux.  libprocstat is added to the libraries in configure to
support this.

Signed-off-by: Stacey Son <sson@FreeBSD.org>
---
 bsd-user/Makefile.objs     |    3 +-
 bsd-user/bsd-errno.h       |   41 ----
 bsd-user/bsd-proc.c        |  160 ++++++++++++++++
 bsd-user/bsd-proc.h        |  434 ++++++++++++++++++++++++++++++++++++++++++++
 bsd-user/elfload.c         |   37 ++++
 bsd-user/freebsd/os-proc.c |  234 ++++++++++++++++++++++++
 bsd-user/freebsd/os-proc.h |  427 +++++++++++++++++++++++++++++++++++++++++++
 bsd-user/main.c            |   29 +++
 bsd-user/netbsd/os-proc.c  |   11 +
 bsd-user/netbsd/os-proc.h  |  243 +++++++++++++++++++++++++
 bsd-user/openbsd/os-proc.c |   11 +
 bsd-user/openbsd/os-proc.h |  243 +++++++++++++++++++++++++
 bsd-user/qemu-bsd.h        |   13 ++
 bsd-user/qemu.h            |   10 +
 bsd-user/syscall.c         |  303 ++++++++++++++++++++++++++++---
 bsd-user/syscall_defs.h    |   50 +++++
 configure                  |    3 +-
 17 files changed, 2186 insertions(+), 66 deletions(-)
 delete mode 100644 bsd-user/bsd-errno.h
 create mode 100644 bsd-user/bsd-proc.c
 create mode 100644 bsd-user/bsd-proc.h
 create mode 100644 bsd-user/freebsd/os-proc.c
 create mode 100644 bsd-user/freebsd/os-proc.h
 create mode 100644 bsd-user/netbsd/os-proc.c
 create mode 100644 bsd-user/netbsd/os-proc.h
 create mode 100644 bsd-user/openbsd/os-proc.c
 create mode 100644 bsd-user/openbsd/os-proc.h

Patch

diff --git a/bsd-user/Makefile.objs b/bsd-user/Makefile.objs
index 4c6acb0..d21ddfa 100644
--- a/bsd-user/Makefile.objs
+++ b/bsd-user/Makefile.objs
@@ -1,2 +1,3 @@ 
 obj-y = main.o bsdload.o elfload.o mmap.o signal.o strace.o syscall.o \
-	        uaccess.o bsd-mem.o $(TARGET_OS)/os-time.o
+	        uaccess.o bsd-mem.o bsd-proc.o $(TARGET_OS)/os-time.o \
+			$(TARGET_OS)/os-proc.o
diff --git a/bsd-user/bsd-errno.h b/bsd-user/bsd-errno.h
deleted file mode 100644
index 721bfc0..0000000
--- a/bsd-user/bsd-errno.h
+++ /dev/null
@@ -1,41 +0,0 @@ 
-/*
- *  errno translation
- *
- *  Copyright (c) 2003 - 2008 Fabrice Bellard
- *  Copyright (c) 2013 Stacey D. Son
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef __BSD_ERRNO_H_
-#define __BSD_ERRNO_H_
-
-static inline abi_long get_errno(abi_long ret)
-{
-
-    if (ret == -1) {
-        /* XXX need to translate host -> target errnos here */
-        return -(errno);
-    } else {
-        return ret;
-    }
-}
-
-static inline int is_error(abi_long ret)
-{
-
-    return (abi_ulong)ret >= (abi_ulong)(-4096);
-}
-
-#endif /* !__BSD_ERRNO_H_ */
diff --git a/bsd-user/bsd-proc.c b/bsd-user/bsd-proc.c
new file mode 100644
index 0000000..a4bcdc8
--- /dev/null
+++ b/bsd-user/bsd-proc.c
@@ -0,0 +1,160 @@ 
+/*
+ *  BSD process related system call helpers
+ *
+ *  Copyright (c) 2013 Stacey D. Son
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+
+#include "qemu.h"
+#include "qemu-bsd.h"
+
+/*
+ * resource/rusage conversion
+ */
+int target_to_host_resource(int code)
+{
+
+    switch (code) {
+    case TARGET_RLIMIT_AS:
+        return RLIMIT_AS;
+
+    case TARGET_RLIMIT_CORE:
+        return RLIMIT_CORE;
+
+    case TARGET_RLIMIT_CPU:
+        return RLIMIT_CPU;
+
+    case TARGET_RLIMIT_DATA:
+        return RLIMIT_DATA;
+
+    case TARGET_RLIMIT_FSIZE:
+        return RLIMIT_FSIZE;
+
+    case TARGET_RLIMIT_MEMLOCK:
+        return RLIMIT_MEMLOCK;
+
+    case TARGET_RLIMIT_NOFILE:
+        return RLIMIT_NOFILE;
+
+    case TARGET_RLIMIT_NPROC:
+        return RLIMIT_NPROC;
+
+    case TARGET_RLIMIT_RSS:
+        return RLIMIT_RSS;
+
+    case TARGET_RLIMIT_SBSIZE:
+        return RLIMIT_SBSIZE;
+
+    case TARGET_RLIMIT_STACK:
+        return RLIMIT_STACK;
+
+    case TARGET_RLIMIT_SWAP:
+        return RLIMIT_SWAP;
+
+    case TARGET_RLIMIT_NPTS:
+        return RLIMIT_NPTS;
+
+    default:
+        return code;
+    }
+}
+
+rlim_t target_to_host_rlim(abi_ulong target_rlim)
+{
+    abi_ulong target_rlim_swap;
+    rlim_t result;
+
+    target_rlim_swap = tswapal(target_rlim);
+    if (target_rlim_swap == TARGET_RLIM_INFINITY) {
+        return RLIM_INFINITY;
+    }
+
+    result = target_rlim_swap;
+    if (target_rlim_swap != (rlim_t)result) {
+        return RLIM_INFINITY;
+    }
+
+    return result;
+}
+
+abi_ulong host_to_target_rlim(rlim_t rlim)
+{
+    abi_ulong target_rlim_swap;
+    abi_ulong result;
+
+    if (rlim == RLIM_INFINITY || rlim != (abi_long)rlim) {
+        target_rlim_swap = TARGET_RLIM_INFINITY;
+    } else {
+        target_rlim_swap = rlim;
+    }
+    result = tswapal(target_rlim_swap);
+
+    return result;
+}
+
+abi_long host_to_target_rusage(abi_ulong target_addr,
+        const struct rusage *rusage)
+{
+    struct target_freebsd_rusage *target_rusage;
+
+    if (!lock_user_struct(VERIFY_WRITE, target_rusage, target_addr, 0)) {
+        return -TARGET_EFAULT;
+    }
+    __put_user(rusage->ru_utime.tv_sec, &target_rusage->ru_utime.tv_sec);
+    __put_user(rusage->ru_utime.tv_usec, &target_rusage->ru_utime.tv_usec);
+
+    __put_user(rusage->ru_stime.tv_sec, &target_rusage->ru_stime.tv_sec);
+    __put_user(rusage->ru_stime.tv_usec, &target_rusage->ru_stime.tv_usec);
+
+    __put_user(rusage->ru_maxrss, &target_rusage->ru_maxrss);
+    __put_user(rusage->ru_idrss, &target_rusage->ru_idrss);
+    __put_user(rusage->ru_idrss, &target_rusage->ru_idrss);
+    __put_user(rusage->ru_isrss, &target_rusage->ru_isrss);
+    __put_user(rusage->ru_minflt, &target_rusage->ru_minflt);
+    __put_user(rusage->ru_majflt, &target_rusage->ru_majflt);
+    __put_user(rusage->ru_nswap, &target_rusage->ru_nswap);
+    __put_user(rusage->ru_inblock, &target_rusage->ru_inblock);
+    __put_user(rusage->ru_oublock, &target_rusage->ru_oublock);
+    __put_user(rusage->ru_msgsnd, &target_rusage->ru_msgsnd);
+    __put_user(rusage->ru_msgrcv, &target_rusage->ru_msgrcv);
+    __put_user(rusage->ru_nsignals, &target_rusage->ru_nsignals);
+    __put_user(rusage->ru_nvcsw, &target_rusage->ru_nvcsw);
+    __put_user(rusage->ru_nivcsw, &target_rusage->ru_nivcsw);
+    unlock_user_struct(target_rusage, target_addr, 1);
+
+    return 0;
+}
+
+/*
+ * wait status conversion.
+ *
+ * Map host to target signal numbers for the wait family of syscalls.
+ * Assume all other status bits are the same.
+ */
+int host_to_target_waitstatus(int status)
+{
+    if (WIFSIGNALED(status)) {
+        return host_to_target_signal(WTERMSIG(status)) | (status & ~0x7f);
+    }
+    if (WIFSTOPPED(status)) {
+        return (host_to_target_signal(WSTOPSIG(status)) << 8) | (status & 0xff);
+    }
+    return status;
+}
+
diff --git a/bsd-user/bsd-proc.h b/bsd-user/bsd-proc.h
new file mode 100644
index 0000000..d1c732a
--- /dev/null
+++ b/bsd-user/bsd-proc.h
@@ -0,0 +1,434 @@ 
+/*
+ *  process related system call shims and definitions
+ *
+ *  Copyright (c) 2013 Stacey D. Son
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __BSD_PROC_H_
+#define __BSD_PROC_H_
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+#include "qemu-bsd.h"
+
+extern int _getlogin(char*, int);
+
+/* exit(2) */
+static inline abi_long do_bsd_exit(void *cpu_env, abi_long arg1)
+{
+#ifdef TARGET_GPROF
+    _mcleanup();
+#endif
+    gdb_exit(cpu_env, arg1);
+    /* XXX: should free thread stack and CPU env here  */
+    _exit(arg1);
+
+    return 0;
+}
+
+/* getgroups(2) */
+static inline abi_long do_bsd_getgroups(abi_long gidsetsize, abi_long arg2)
+{
+    abi_long ret;
+    uint32_t *target_grouplist;
+    gid_t *grouplist;
+    int i;
+
+    grouplist = alloca(gidsetsize * sizeof(gid_t));
+    ret = get_errno(getgroups(gidsetsize, grouplist));
+    if (gidsetsize != 0) {
+        if (!is_error(ret)) {
+            target_grouplist = lock_user(VERIFY_WRITE, arg2, gidsetsize * 2, 0);
+            if (!target_grouplist) {
+                return -TARGET_EFAULT;
+            }
+            for (i = 0; i < ret; i++) {
+                target_grouplist[i] = tswap32(grouplist[i]);
+            }
+            unlock_user(target_grouplist, arg2, gidsetsize * 2);
+        }
+    }
+    return ret;
+}
+
+/* setgroups(2) */
+static inline abi_long do_bsd_setgroups(abi_long gidsetsize, abi_long arg2)
+{
+    uint32_t *target_grouplist;
+    gid_t *grouplist;
+    int i;
+
+    grouplist = alloca(gidsetsize * sizeof(gid_t));
+    target_grouplist = lock_user(VERIFY_READ, arg2, gidsetsize * 2, 1);
+    if (!target_grouplist) {
+        return -TARGET_EFAULT;
+    }
+    for (i = 0; i < gidsetsize; i++) {
+        grouplist[i] = tswap32(target_grouplist[i]);
+    }
+    unlock_user(target_grouplist, arg2, 0);
+    return get_errno(setgroups(gidsetsize, grouplist));
+}
+
+/* umask(2) */
+static inline abi_long do_bsd_umask(abi_long arg1)
+{
+
+    return get_errno(umask(arg1));
+}
+
+/* setlogin(2) */
+static inline abi_long do_bsd_setlogin(abi_long arg1)
+{
+    abi_long ret;
+    void *p;
+
+    p = lock_user_string(arg1);
+    if (p == NULL) {
+        return -TARGET_EFAULT;
+    }
+    ret = get_errno(setlogin(p));
+    unlock_user(p, arg1, 0);
+
+    return ret;
+}
+
+/* getlogin(2) */
+static inline abi_long do_bsd_getlogin(abi_long arg1, abi_long arg2)
+{
+    abi_long ret;
+    void *p;
+
+    p = lock_user_string(arg1);
+    if (p == NULL) {
+        return -TARGET_EFAULT;
+    }
+    ret = get_errno(_getlogin(p, arg2));
+    unlock_user(p, arg1, 0);
+
+    return ret;
+}
+
+/* getrusage(2) */
+static inline abi_long do_bsd_getrusage(abi_long who, abi_ulong target_addr)
+{
+    abi_long ret;
+    struct rusage rusage;
+
+    ret = get_errno(getrusage(who, &rusage));
+    if (!is_error(ret)) {
+        host_to_target_rusage(target_addr, &rusage);
+    }
+    return ret;
+}
+
+/* getrlimit(2) */
+static inline abi_long do_bsd_getrlimit(abi_long arg1, abi_ulong arg2)
+{
+    abi_long ret;
+    int resource = target_to_host_resource(arg1);
+    struct target_rlimit *target_rlim;
+    struct rlimit rlim;
+
+    switch (resource) {
+    case RLIMIT_STACK:
+        rlim.rlim_cur = target_dflssiz;
+        rlim.rlim_max = target_maxssiz;
+        ret = 0;
+        break;
+
+    case RLIMIT_DATA:
+        rlim.rlim_cur = target_dfldsiz;
+        rlim.rlim_max = target_maxdsiz;
+        ret = 0;
+        break;
+
+    default:
+        ret = get_errno(getrlimit(resource, &rlim));
+        break;
+    }
+    if (!is_error(ret)) {
+        if (!lock_user_struct(VERIFY_WRITE, target_rlim, arg2, 0)) {
+            return -TARGET_EFAULT;
+        }
+        target_rlim->rlim_cur = host_to_target_rlim(rlim.rlim_cur);
+        target_rlim->rlim_max = host_to_target_rlim(rlim.rlim_max);
+        unlock_user_struct(target_rlim, arg2, 1);
+    }
+    return ret;
+}
+
+/* setrlimit(2) */
+static inline abi_long do_bsd_setrlimit(abi_long arg1, abi_ulong arg2)
+{
+    abi_long ret;
+    int resource = target_to_host_resource(arg1);
+    struct target_rlimit *target_rlim;
+    struct rlimit rlim;
+
+    if (RLIMIT_STACK == resource) {
+        /* XXX We should, maybe, allow the stack size to shrink */
+        ret = -TARGET_EPERM;
+    } else {
+        if (!lock_user_struct(VERIFY_READ, target_rlim, arg2, 1)) {
+            return -TARGET_EFAULT;
+        }
+        rlim.rlim_cur = target_to_host_rlim(target_rlim->rlim_cur);
+        rlim.rlim_max = target_to_host_rlim(target_rlim->rlim_max);
+        unlock_user_struct(target_rlim, arg2, 0);
+        ret = get_errno(setrlimit(resource, &rlim));
+    }
+    return ret;
+}
+
+/* getpid(2) */
+static inline abi_long do_bsd_getpid(void)
+{
+
+    return get_errno(getpid());
+}
+
+/* getppid(2) */
+static inline abi_long do_bsd_getppid(void)
+{
+
+    return get_errno(getppid());
+}
+
+/* getuid(2) */
+static inline abi_long do_bsd_getuid(void)
+{
+
+    return get_errno(getuid());
+}
+
+/* geteuid(2) */
+static inline abi_long do_bsd_geteuid(void)
+{
+
+    return get_errno(geteuid());
+}
+
+/* getgid(2) */
+static inline abi_long do_bsd_getgid(void)
+{
+
+    return get_errno(getgid());
+}
+
+/* getegid(2) */
+static inline abi_long do_bsd_getegid(void)
+{
+
+    return get_errno(getegid());
+}
+
+/* setuid(2) */
+static inline abi_long do_bsd_setuid(abi_long arg1)
+{
+
+    return get_errno(setuid(arg1));
+}
+
+/* seteuid(2) */
+static inline abi_long do_bsd_seteuid(abi_long arg1)
+{
+
+    return get_errno(seteuid(arg1));
+}
+
+/* setgid(2) */
+static inline abi_long do_bsd_setgid(abi_long arg1)
+{
+
+    return get_errno(setgid(arg1));
+}
+
+/* setegid(2) */
+static inline abi_long do_bsd_setegid(abi_long arg1)
+{
+
+    return get_errno(setegid(arg1));
+}
+
+/* getpgrp(2) */
+static inline abi_long do_bsd_getpgrp(void)
+{
+
+    return get_errno(getpgrp());
+}
+
+/* setreuid(2) */
+static inline abi_long do_bsd_setreuid(abi_long arg1, abi_long arg2)
+{
+
+    return get_errno(setreuid(arg1, arg2));
+}
+
+/* setregid(2) */
+static inline abi_long do_bsd_setregid(abi_long arg1, abi_long arg2)
+{
+
+    return get_errno(setregid(arg1, arg2));
+}
+
+/* setresuid(2) */
+static inline abi_long do_bsd_setresuid(abi_long arg1, abi_long arg2,
+        abi_long arg3)
+{
+
+    return get_errno(setresuid(arg1, arg2, arg3));
+}
+
+/* getresuid(2) */
+static inline abi_long do_bsd_getresuid(abi_ulong arg1, abi_ulong arg2,
+        abi_ulong arg3)
+{
+    abi_long ret;
+    uid_t ruid, euid, suid;
+
+    ret = get_errno(getresuid(&ruid, &euid, &suid));
+    if (is_error(ret)) {
+            return ret;
+    }
+    if (put_user_s32(ruid, arg1)) {
+        return -TARGET_EFAULT;
+    }
+    if (put_user_s32(euid, arg2)) {
+        return -TARGET_EFAULT;
+    }
+    if (put_user_s32(suid, arg3)) {
+        return -TARGET_EFAULT;
+    }
+    return ret;
+}
+
+/* getresgid(2) */
+static inline abi_long do_bsd_getresgid(abi_ulong arg1, abi_ulong arg2,
+        abi_ulong arg3)
+{
+    abi_long ret;
+    uid_t ruid, euid, suid;
+
+    ret = get_errno(getresgid(&ruid, &euid, &suid));
+    if (is_error(ret)) {
+            return ret;
+    }
+    if (put_user_s32(ruid, arg1)) {
+        return -TARGET_EFAULT;
+    }
+    if (put_user_s32(euid, arg2)) {
+        return -TARGET_EFAULT;
+    }
+    if (put_user_s32(suid, arg3)) {
+        return -TARGET_EFAULT;
+    }
+    return ret;
+}
+
+/* getsid(2) */
+static inline abi_long do_bsd_getsid(abi_long arg1)
+{
+
+    return get_errno(getsid(arg1));
+}
+
+/* setsid(2) */
+static inline abi_long do_bsd_setsid(void)
+{
+
+    return get_errno(setsid());
+}
+
+/* issetugid(2) */
+static inline abi_long do_bsd_issetugid(void)
+{
+
+    return get_errno(issetugid());
+}
+
+/* profil(2) */
+static inline abi_long do_bsd_profil(abi_long arg1, abi_long arg2,
+        abi_long arg3, abi_long arg4)
+{
+
+    qemu_log("qemu: Unsupported syscall profil()\n");
+    return -TARGET_ENOSYS;
+}
+
+
+/* ktrace(2) */
+static inline abi_long do_bsd_ktrace(abi_long arg1, abi_long arg2,
+        abi_long arg3, abi_long arg4)
+{
+
+    qemu_log("qemu: Unsupported syscall ktrace()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* utrace(2) */
+static inline abi_long do_bsd_utrace(abi_long arg1, abi_long arg2)
+{
+
+    qemu_log("qemu: Unsupported syscall ptrace()\n");
+    return -TARGET_ENOSYS;
+}
+
+
+/* ptrace(2) */
+static inline abi_long do_bsd_ptrace(abi_long arg1, abi_long arg2,
+        abi_long arg3, abi_long arg4)
+{
+
+    qemu_log("qemu: Unsupported syscall ptrace()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* getpriority(2) */
+static inline abi_long do_bsd_getpriority(abi_long which, abi_long who)
+{
+    abi_long ret;
+    /*
+     * Note that negative values are valid for getpriority, so we must
+     * differentiate based on errno settings.
+     */
+    errno = 0;
+    ret = getpriority(which, who);
+    if (ret == -1 && errno != 0) {
+        ret = -host_to_target_errno(errno);
+        return ret;
+    }
+    /* Return value is a biased priority to avoid negative numbers. */
+    ret = 20 - ret;
+
+    return ret;
+}
+
+/* setpriority(2) */
+static inline abi_long do_bsd_setpriority(abi_long which, abi_long who,
+        abi_long prio)
+{
+
+    return get_errno(setpriority(which, who, prio));
+}
+
+
+#endif /* !__BSD_PROC_H_ */
+
diff --git a/bsd-user/elfload.c b/bsd-user/elfload.c
index 0cd5fc4..78a8d98 100644
--- a/bsd-user/elfload.c
+++ b/bsd-user/elfload.c
@@ -111,6 +111,7 @@  enum {
 
 #ifdef TARGET_I386
 
+#ifndef __FreeBSD__
 #define ELF_PLATFORM get_elf_platform()
 
 static const char *get_elf_platform(void)
@@ -130,6 +131,7 @@  static uint32_t get_elf_hwcap(void)
 {
     return thread_env->features[FEAT_1_EDX];
 }
+#endif /* ! __FreeBSD__ */
 
 #ifdef TARGET_X86_64
 #define ELF_START_MMAP 0x2aaaaab000ULL
@@ -211,6 +213,9 @@  static inline void init_thread(struct target_pt_regs *regs,
       regs->ARM_cpsr |= CPSR_T;
     regs->ARM_pc = infop->entry & 0xfffffffe;
     regs->ARM_sp = infop->start_stack;
+    if (bsd_type == target_freebsd) {
+        regs->ARM_lr = infop->entry & 0xfffffffe;
+    }
     /* FIXME - what to for failure of get_user()? */
     get_user_ual(regs->ARM_r2, stack + 8); /* envp */
     get_user_ual(regs->ARM_r1, stack + 4); /* envp */
@@ -803,11 +808,14 @@  static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
 {
         abi_ulong sp;
         int size;
+#ifndef __FreeBSD__
         abi_ulong u_platform;
         const char *k_platform;
+#endif
         const int n = sizeof(elf_addr_t);
 
         sp = p;
+#ifndef __FreeBSD__
         u_platform = 0;
         k_platform = ELF_PLATFORM;
         if (k_platform) {
@@ -817,16 +825,19 @@  static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
             /* FIXME - check return value of memcpy_to_target() for failure */
             memcpy_to_target(sp, k_platform, len);
         }
+#endif /* !__FreeBSD__ */
         /*
          * Force 16 byte _final_ alignment here for generality.
          */
         sp = sp &~ (abi_ulong)15;
         size = (DLINFO_ITEMS + 1) * 2;
+#ifndef __FreeBSD__
         if (k_platform)
           size += 2;
 #ifdef DLINFO_ARCH_ITEMS
         size += DLINFO_ARCH_ITEMS * 2;
 #endif
+#endif /* ! __FreeBSD__ */
         size += envc + argc + 2;
         size += (!ibcs ? 3 : 1);        /* argc itself */
         size *= n;
@@ -855,10 +866,12 @@  static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
         NEW_AUX_ENT(AT_EUID, (abi_ulong) geteuid());
         NEW_AUX_ENT(AT_GID, (abi_ulong) getgid());
         NEW_AUX_ENT(AT_EGID, (abi_ulong) getegid());
+#ifndef __FreeBSD__
         NEW_AUX_ENT(AT_HWCAP, (abi_ulong) ELF_HWCAP);
         NEW_AUX_ENT(AT_CLKTCK, (abi_ulong) sysconf(_SC_CLK_TCK));
         if (k_platform)
             NEW_AUX_ENT(AT_PLATFORM, u_platform);
+#endif
 #ifdef ARCH_DLINFO
         /*
          * ARCH_DLINFO must come last so platform specific code can enforce
@@ -1173,6 +1186,30 @@  static void load_symbols(struct elfhdr *hdr, int fd)
     syminfos = s;
 }
 
+/* Check the elf header and see if this a target elf binary. */
+int is_target_elf_binary(int fd)
+{
+    uint8_t buf[128];
+    struct elfhdr elf_ex;
+
+    if (lseek(fd, 0L, SEEK_SET) < 0) {
+        return 0;
+    }
+    if (read(fd, buf, sizeof(buf)) < 0) {
+        return 0;
+    }
+
+    elf_ex = *((struct elfhdr *)buf);
+    bswap_ehdr(&elf_ex);
+
+    if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) ||
+        (!elf_check_arch(elf_ex.e_machine))) {
+        return 0;
+    } else {
+        return 1;
+    }
+}
+
 int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs,
                     struct image_info *info)
 {
diff --git a/bsd-user/freebsd/os-proc.c b/bsd-user/freebsd/os-proc.c
new file mode 100644
index 0000000..b696222
--- /dev/null
+++ b/bsd-user/freebsd/os-proc.c
@@ -0,0 +1,234 @@ 
+/*
+ *  FreeBSD process related emulation code
+ *
+ *  Copyright (c) 2013 Stacey D. Son
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+#include <sys/unistd.h>
+#if defined(__FreeBSD_version) && __FreeBSD_version > 900000
+#include <libprocstat.h>
+#endif
+
+#include "qemu.h"
+
+/*
+ * Get the filename for the given file descriptor.
+ * Note that this may return NULL (fail) if no longer cached in the kernel.
+ */
+static char *
+get_filename_from_fd(pid_t pid, int fd, char *filename, size_t len)
+{
+    char *ret = NULL;
+#if defined(__FreeBSD_version) && __FreeBSD_version > 900000
+    unsigned int cnt;
+    struct procstat *procstat = NULL;
+    struct kinfo_proc *kipp = NULL;
+    struct filestat_list *head = NULL;
+    struct filestat *fst;
+
+    procstat = procstat_open_sysctl();
+    if (NULL == procstat) {
+        goto out;
+    }
+
+    kipp = procstat_getprocs(procstat, KERN_PROC_PID, pid, &cnt);
+    if (NULL == kipp) {
+        goto out;
+    }
+
+    head = procstat_getfiles(procstat, kipp, 0);
+    if (NULL == head) {
+        goto out;
+    }
+
+    STAILQ_FOREACH(fst, head, next) {
+        if (fd == fst->fs_fd) {
+            if (fst->fs_path != NULL) {
+                (void)strlcpy(filename, fst->fs_path, len);
+                ret = filename;
+            }
+            break;
+        }
+    }
+
+out:
+    if (head != NULL) {
+        procstat_freefiles(procstat, head);
+    }
+    if (kipp != NULL) {
+        procstat_freeprocs(procstat, kipp);
+    }
+    if (procstat != NULL) {
+        procstat_close(procstat);
+    }
+#endif
+    return ret;
+}
+
+/*
+ * execve/fexecve
+ */
+abi_long freebsd_exec_common(abi_ulong path_or_fd, abi_ulong guest_argp,
+        abi_ulong guest_envp, int do_fexec)
+{
+    char **argp, **envp, **qargp, **qarg1;
+    int argc, envc;
+    abi_ulong gp;
+    abi_ulong addr;
+    char **q;
+    int total_size = 0;
+    void *p;
+    abi_long ret;
+
+    argc = 0;
+    for (gp = guest_argp; gp; gp += sizeof(abi_ulong)) {
+        if (get_user_ual(addr, gp)) {
+            return -TARGET_EFAULT;
+        }
+        if (!addr) {
+            break;
+        }
+        argc++;
+    }
+    envc = 0;
+    for (gp = guest_envp; gp; gp += sizeof(abi_ulong)) {
+        if (get_user_ual(addr, gp)) {
+            return -TARGET_EFAULT;
+        }
+        if (!addr) {
+            break;
+        }
+        envc++;
+    }
+
+    qargp = argp =  alloca((argc + 3) * sizeof(void *));
+    /* save the first agrument for the emulator */
+    *argp++ = (char *)getprogname();
+    qarg1 = argp;
+    envp = alloca((envc + 1) * sizeof(void *));
+    for (gp = guest_argp, q = argp; gp; gp += sizeof(abi_ulong), q++) {
+        if (get_user_ual(addr, gp)) {
+            ret = -TARGET_EFAULT;
+            goto execve_end;
+        }
+        if (!addr) {
+            break;
+        }
+        *q = lock_user_string(addr);
+        if (*q == NULL) {
+            ret = -TARGET_EFAULT;
+            goto execve_end;
+        }
+        total_size += strlen(*q) + 1;
+    }
+    *q = NULL;
+
+    for (gp = guest_envp, q = envp; gp; gp += sizeof(abi_ulong), q++) {
+        if (get_user_ual(addr, gp)) {
+            ret = -TARGET_EFAULT;
+            goto execve_end;
+        }
+        if (!addr) {
+            break;
+        }
+        *q = lock_user_string(addr);
+        if (*q == NULL) {
+            ret = -TARGET_EFAULT;
+            goto execve_end;
+        }
+        total_size += strlen(*q) + 1;
+    }
+    *q = NULL;
+
+    /*
+     * This case will not be caught by the host's execve() if its
+     * page size is bigger than the target's.
+     */
+    if (total_size > MAX_ARG_PAGES * TARGET_PAGE_SIZE) {
+        ret = -TARGET_E2BIG;
+        goto execve_end;
+    }
+
+    if (do_fexec) {
+        if (((int)path_or_fd > 0 &&
+            is_target_elf_binary((int)path_or_fd)) == 1) {
+            char execpath[PATH_MAX];
+
+            /*
+             * The executable is an elf binary for the target
+             * arch.  execve() it using the emulator if we can
+             * determine the filename path from the fd.
+             */
+            if (get_filename_from_fd(getpid(), (int)path_or_fd, execpath,
+                        sizeof(execpath)) != NULL) {
+                *qarg1 = execpath;
+                ret = get_errno(execve(qemu_proc_pathname, qargp, envp));
+            } else {
+                /* Getting the filename path failed. */
+                ret = -TARGET_EBADF;
+                goto execve_end;
+            }
+        } else {
+            ret = get_errno(fexecve((int)path_or_fd, argp, envp));
+        }
+    } else {
+        int fd;
+
+        p = lock_user_string(path_or_fd);
+        if (p == NULL) {
+            ret = -TARGET_EFAULT;
+            goto execve_end;
+        }
+
+        /*
+         * Check the header and see if it a target elf binary.  If so
+         * then execute using qemu user mode emulator.
+         */
+        fd = open(p, O_RDONLY | O_CLOEXEC);
+        if (fd > 0 && is_target_elf_binary(fd) == 1) {
+            close(fd);
+            /* Execve() as a target binary using emulator. */
+            *qarg1 = (char *)p;
+            ret = get_errno(execve(qemu_proc_pathname, qargp, envp));
+        } else {
+            close(fd);
+            /* Execve() as a host native binary. */
+            ret = get_errno(execve(p, argp, envp));
+        }
+        unlock_user(p, path_or_fd, 0);
+    }
+
+execve_end:
+    for (gp = guest_argp, q = argp; *q; gp += sizeof(abi_ulong), q++) {
+        if (get_user_ual(addr, gp) || !addr) {
+            break;
+        }
+        unlock_user(*q, addr, 0);
+    }
+
+    for (gp = guest_envp, q = envp; *q; gp += sizeof(abi_ulong), q++) {
+        if (get_user_ual(addr, gp) || !addr) {
+            break;
+        }
+        unlock_user(*q, addr, 0);
+    }
+    return ret;
+}
+
+
diff --git a/bsd-user/freebsd/os-proc.h b/bsd-user/freebsd/os-proc.h
new file mode 100644
index 0000000..fd2bb42
--- /dev/null
+++ b/bsd-user/freebsd/os-proc.h
@@ -0,0 +1,427 @@ 
+/*
+ *  process related system call shims and definitions
+ *
+ *  Copyright (c) 2013 Stacey D. Son
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __FREEBSD_PROC_H_
+#define __FREEBSD_PROC_H_
+
+#include <sys/types.h>
+#if defined(__FreeBSD_version) && __FreeBSD_version > 900000
+#include <sys/procdesc.h>
+#endif
+#include <sys/wait.h>
+#include <unistd.h>
+
+
+extern int __setugid(int flag);
+extern int pdwait4(int fd, int *status, int options, struct rusage *rusage);
+
+/* execve(2) */
+static inline abi_long do_freebsd_execve(abi_ulong path_or_fd, abi_ulong argp,
+        abi_ulong envp)
+{
+
+    return freebsd_exec_common(path_or_fd, argp, envp, 0);
+}
+
+/* fexecve(2) */
+static inline abi_long do_freebsd_fexecve(abi_ulong path_or_fd, abi_ulong argp,
+        abi_ulong envp)
+{
+
+    return freebsd_exec_common(path_or_fd, argp, envp, 1);
+}
+
+/* wait4(2) */
+static inline abi_long do_freebsd_wait4(abi_long arg1, abi_ulong target_status,
+        abi_long arg3, abi_ulong target_rusage)
+{
+    abi_long ret;
+    int status;
+    struct rusage rusage, *rusage_ptr = NULL;
+
+    if (target_rusage) {
+        rusage_ptr = &rusage;
+    }
+    ret = get_errno(wait4(arg1, &status, arg3, rusage_ptr));
+    if (!is_error(ret)) {
+        status = host_to_target_waitstatus(status);
+        if (put_user_s32(status, target_status) != 0) {
+            return -TARGET_EFAULT;
+        }
+        if (target_rusage != 0) {
+            host_to_target_rusage(target_rusage, &rusage);
+        }
+    }
+    return ret;
+}
+
+#if defined(__FreeBSD_version) && __FreeBSD_version > 900000
+/* setloginclass(2) */
+static inline abi_long do_freebsd_setloginclass(abi_ulong arg1)
+{
+    abi_long ret;
+    void *p;
+
+    p = lock_user_string(arg1);
+    if (p == NULL) {
+        return -TARGET_EFAULT;
+    }
+    ret = get_errno(setloginclass(p));
+    unlock_user(p, arg1, 0);
+
+    return ret;
+}
+
+/* getloginclass(2) */
+static inline abi_long do_freebsd_getloginclass(abi_ulong arg1, abi_ulong arg2)
+{
+    abi_long ret;
+    void *p;
+
+    p = lock_user_string(arg1);
+    if (p == NULL) {
+        return -TARGET_EFAULT;
+    }
+    ret = get_errno(getloginclass(p, arg2));
+    unlock_user(p, arg1, 0);
+
+    return ret;
+}
+
+/* pdwait4(2) */
+static inline abi_long do_freebsd_pdwait4(abi_long arg1,
+        abi_ulong target_status, abi_long arg3, abi_ulong target_rusage)
+{
+    abi_long ret;
+    int status;
+    struct rusage rusage, *rusage_ptr = NULL;
+
+    if (target_rusage) {
+        rusage_ptr = &rusage;
+    }
+    ret = get_errno(pdwait4(arg1, &status, arg3, rusage_ptr));
+    if (!is_error(ret)) {
+        status = host_to_target_waitstatus(status);
+        if (put_user_s32(status, target_status) != 0) {
+            return -TARGET_EFAULT;
+        }
+        if (target_rusage != 0) {
+            host_to_target_rusage(target_rusage, &rusage);
+        }
+    }
+    return ret;
+}
+
+/* pdgetpid(2) */
+static inline abi_long do_freebsd_pdgetpid(abi_long fd, abi_ulong target_pidp)
+{
+    abi_long ret;
+    pid_t pid;
+
+    ret = get_errno(pdgetpid(fd, &pid));
+    if (!is_error(ret)) {
+        if (put_user_u32(pid, target_pidp)) {
+            return -TARGET_EFAULT;
+        }
+    }
+    return ret;
+}
+
+#else
+
+/* setloginclass(2) */
+static inline abi_long do_freebsd_setloginclass(abi_ulong arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall setloginclass()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* getloginclass(2) */
+static inline abi_long do_freebsd_getloginclass(abi_ulong arg1, abi_ulong arg2)
+{
+
+    qemu_log("qemu: Unsupported syscall getloginclass()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* pdwait4(2) */
+static inline abi_long do_freebsd_pdwait4(abi_long arg1,
+        abi_ulong target_status, abi_long arg3, abi_ulong target_rusage)
+{
+
+    qemu_log("qemu: Unsupported syscall pdwait4()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* pdgetpid(2) */
+static inline abi_long do_freebsd_pdgetpid(abi_long fd, abi_ulong target_pidp)
+{
+
+    qemu_log("qemu: Unsupported syscall pdgetpid()\n");
+    return -TARGET_ENOSYS;
+}
+#endif /* !  __FreeBSD_version > 900000 */
+
+/* undocumented __setugid */
+static inline abi_long do_freebsd___setugid(abi_long arg1)
+{
+
+    return get_errno(__setugid(arg1));
+}
+
+/* fork(2) */
+static inline abi_long do_freebsd_fork(void *cpu_env)
+{
+    abi_long ret;
+    abi_ulong child_flag = 0;
+
+    fork_start();
+    ret = fork();
+    if (ret == 0) {
+        /* child */
+        child_flag = 1;
+        cpu_clone_regs(cpu_env, 0);
+    } else {
+        /* parent */
+        fork_end(0);
+    }
+
+    /*
+     * The fork system call sets a child flag in the second return
+     * value: 0 for parent process, 1 for child process.
+     */
+    set_second_rval(cpu_env, child_flag);
+
+    return ret;
+}
+
+/* vfork(2) */
+static inline abi_long do_freebsd_vfork(void *cpu_env)
+{
+
+    return do_freebsd_fork(cpu_env);
+}
+
+/* rfork(2) */
+static inline abi_long do_freebsd_rfork(void *cpu_env, abi_long flags)
+{
+    abi_long ret;
+    abi_ulong child_flag = 0;
+
+    fork_start();
+    ret = rfork(flags);
+    if (ret == 0) {
+        /* child */
+        child_flag = 1;
+        cpu_clone_regs(cpu_env, 0);
+    } else {
+        /* parent */
+        fork_end(0);
+    }
+
+    /*
+     * The fork system call sets a child flag in the second return
+     * value: 0 for parent process, 1 for child process.
+     */
+    set_second_rval(cpu_env, child_flag);
+
+    return ret;
+
+}
+
+#if defined(__FreeBSD_version) && __FreeBSD_version > 900000
+/* pdfork(2) */
+static inline abi_long do_freebsd_pdfork(void *cpu_env, abi_ulong target_fdp,
+        abi_long flags)
+{
+    abi_long ret;
+    abi_ulong child_flag = 0;
+    int fd;
+
+    fork_start();
+    ret = pdfork(&fd, flags);
+    if (ret == 0) {
+        /* child */
+        child_flag = 1;
+        cpu_clone_regs(cpu_env, 0);
+    } else {
+        /* parent */
+        fork_end(0);
+    }
+    if (put_user_s32(fd, target_fdp)) {
+        return -TARGET_EFAULT;
+    }
+
+    /*
+     * The fork system call sets a child flag in the second return
+     * value: 0 for parent process, 1 for child process.
+     */
+    set_second_rval(cpu_env, child_flag);
+
+    return ret;
+}
+
+#else
+
+/* pdfork(2) */
+static inline abi_long do_freebsd_pdfork(void *cpu_env, abi_ulong arg1,
+        abi_long arg2)
+{
+
+    qemu_log("qemu: Unsupported syscall pdfork()\n");
+    return -TARGET_ENOSYS;
+}
+
+#endif /* __FreeBSD_version > 900000 */
+
+/* jail(2) */
+static inline abi_long do_freebsd_jail(abi_ulong arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall jail()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* jail_attach(2) */
+static inline abi_long do_freebsd_jail_attach(abi_long arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall jail_attach()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* jail_remove(2) */
+static inline abi_long do_freebsd_jail_remove(abi_long arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall jail_remove()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* jail_get(2) */
+static inline abi_long do_freebsd_jail_get(abi_ulong arg1, abi_long arg2,
+        abi_long arg3)
+{
+
+    qemu_log("qemu: Unsupported syscall jail_get()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* jail_set(2) */
+static inline abi_long do_freebsd_jail_set(abi_ulong arg1, abi_long arg2,
+        abi_long arg3)
+{
+
+    qemu_log("qemu: Unsupported syscall jail_set()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* cap_enter(2) */
+static inline abi_long do_freebsd_cap_enter(void)
+{
+
+    qemu_log("qemu: Unsupported syscall cap_enter()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* cap_new(2) */
+static inline abi_long do_freebsd_cap_new(abi_long arg1, abi_ulong arg2)
+{
+
+    qemu_log("qemu: Unsupported syscall cap_new()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* cap_getrights(2) */
+static inline abi_long do_freebsd_cap_getrights(abi_long arg1, abi_ulong arg2)
+{
+
+    qemu_log("qemu: Unsupported syscall cap_getrights()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* cap_getmode(2) */
+static inline abi_long do_freebsd_cap_getmode(abi_ulong arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall cap_getmode()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* audit(2) */
+static inline abi_long do_freebsd_audit(abi_ulong arg1, abi_ulong arg2)
+{
+
+    qemu_log("qemu: Unsupported syscall audit()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* auditon(2) */
+static inline abi_long do_freebsd_auditon(abi_long arg1, abi_ulong arg2,
+        abi_ulong arg3)
+{
+
+    qemu_log("qemu: Unsupported syscall auditon()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* getaudit(2) */
+static inline abi_long do_freebsd_getaudit(abi_ulong arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall getaudit()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* setaudit(2) */
+static inline abi_long do_freebsd_setaudit(abi_ulong arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall setaudit()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* getaudit_addr(2) */
+static inline abi_long do_freebsd_getaudit_addr(abi_ulong arg1,
+        abi_ulong arg2)
+{
+
+    qemu_log("qemu: Unsupported syscall getaudit_addr()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* setaudit_addr(2) */
+static inline abi_long do_freebsd_setaudit_addr(abi_ulong arg1,
+        abi_ulong arg2)
+{
+
+    qemu_log("qemu: Unsupported syscall setaudit_addr()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* auditctl(2) */
+static inline abi_long do_freebsd_auditctl(abi_ulong arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall auditctl()\n");
+    return -TARGET_ENOSYS;
+}
+
+#endif /* ! __FREEBSD_PROC_H_ */
diff --git a/bsd-user/main.c b/bsd-user/main.c
index 0442dbe..1fa5e7f 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -27,6 +27,7 @@ 
 #include <sys/syscall.h>
 #include <sys/types.h>
 #include <sys/mman.h>
+#include <sys/sysctl.h>
 
 #include "qemu.h"
 #include "qemu-common.h"
@@ -56,6 +57,32 @@  unsigned long target_dflssiz = TARGET_DFLSSIZ;   /* initial data size limit */
 unsigned long target_maxssiz = TARGET_MAXSSIZ;   /* max stack size */
 unsigned long target_sgrowsiz = TARGET_SGROWSIZ; /* amount to grow stack */
 
+#ifdef __FreeBSD__
+char qemu_proc_pathname[PATH_MAX];
+
+static void save_proc_pathname(void)
+{
+    int mib[4];
+    size_t len;
+
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_PROC;
+    mib[2] = KERN_PROC_PATHNAME;
+    mib[3] = -1;
+
+    len = sizeof(qemu_proc_pathname);
+    if (sysctl(mib, 4, qemu_proc_pathname, &len, NULL, 0)) {
+        perror("sysctl");
+    }
+}
+
+#else
+
+static void save_prov_pathname(void)
+{
+}
+#endif /* !__FreeBSD__ */
+
 void gemu_log(const char *fmt, ...)
 {
     va_list ap;
@@ -931,6 +958,8 @@  int main(int argc, char **argv)
     if (argc <= 1)
         usage();
 
+    save_proc_pathname();
+
     module_call_init(MODULE_INIT_QOM);
 
     if ((envlist = envlist_create()) == NULL) {
diff --git a/bsd-user/netbsd/os-proc.c b/bsd-user/netbsd/os-proc.c
new file mode 100644
index 0000000..bc11d29
--- /dev/null
+++ b/bsd-user/netbsd/os-proc.c
@@ -0,0 +1,11 @@ 
+/*
+ * XXX To support FreeBSD binaries on NetBSD the following will need to be
+ * emulated.
+ */
+abi_long freebsd_exec_common(abi_ulong path_or_fd, abi_ulong guest_argp,
+        abi_ulong guest_envp, int do_fexec)
+{
+
+    qemu_log("qemu: Unsupported %s\n", __func__);
+    return -TARGET_ENOSYS;
+}
diff --git a/bsd-user/netbsd/os-proc.h b/bsd-user/netbsd/os-proc.h
new file mode 100644
index 0000000..f34d616
--- /dev/null
+++ b/bsd-user/netbsd/os-proc.h
@@ -0,0 +1,243 @@ 
+#ifndef __NETBSD_PROC_H_
+#define __NETBSD_PROC_H_
+
+/*
+ * XXX To support FreeBSD binaries on NetBSD these syscalls will need
+ * to be emulated.
+ */
+
+/* execve(2) */
+static inline abi_long do_freebsd_execve(abi_ulong path_or_fd, abi_ulong argp,
+        abi_ulong envp)
+{
+
+    qemu_log("qemu: Unsupported syscall execve()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* fexecve(2) */
+static inline abi_long do_freebsd_fexecve(abi_ulong path_or_fd, abi_ulong argp,
+        abi_ulong envp)
+{
+
+    qemu_log("qemu: Unsupported syscall fexecve()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* wait4(2) */
+static inline abi_long do_freebsd_wait4(abi_long arg1, abi_ulong target_status,
+        abi_long arg3, abi_ulong target_rusage)
+{
+
+    qemu_log("qemu: Unsupported syscall wait4()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* setloginclass(2) */
+static inline abi_long do_freebsd_setloginclass(abi_ulong arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall setloginclass()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* getloginclass(2) */
+static inline abi_long do_freebsd_getloginclass(abi_ulong arg1, abi_ulong arg2)
+{
+
+    qemu_log("qemu: Unsupported syscall getloginclass()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* pdwait4(2) */
+static inline abi_long do_freebsd_pdwait4(abi_long arg1,
+        abi_ulong target_status, abi_long arg3, abi_ulong target_rusage)
+{
+
+    qemu_log("qemu: Unsupported syscall pdwait4()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* pdgetpid(2) */
+static inline abi_long do_freebsd_pdgetpid(abi_long fd, abi_ulong target_pidp)
+{
+
+    qemu_log("qemu: Unsupported syscall pdgetpid()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* undocumented __setugid */
+static inline abi_long do_freebsd___setugid(abi_long arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall __setugid()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* fork(2) */
+static inline abi_long do_freebsd_fork(void *cpu_env)
+{
+
+    qemu_log("qemu: Unsupported syscall fork()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* vfork(2) */
+static inline abi_long do_freebsd_vfork(void *cpu_env)
+{
+
+    qemu_log("qemu: Unsupported syscall vfork()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* rfork(2) */
+static inline abi_long do_freebsd_rfork(void *cpu_env, abi_long flags)
+{
+
+    qemu_log("qemu: Unsupported syscall rfork()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* pdfork(2) */
+static inline abi_long do_freebsd_pdfork(void *cpu_env, abi_ulong arg1,
+        abi_long arg2)
+{
+
+    qemu_log("qemu: Unsupported syscall pdfork()\n");
+    return -TARGET_ENOSYS
+}
+
+/* jail(2) */
+static inline abi_long do_freebsd_jail(abi_ulong arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall jail()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* jail_attach(2) */
+static inline abi_long do_freebsd_jail_attach(abi_long arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall jail_attach()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* jail_remove(2) */
+static inline abi_long do_freebsd_jail_remove(abi_long arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall jail_remove()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* jail_get(2) */
+static inline abi_long do_freebsd_jail_get(abi_ulong arg1, abi_long arg2,
+        abi_long arg3)
+{
+
+    qemu_log("qemu: Unsupported syscall jail_get()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* jail_set(2) */
+static inline abi_long do_freebsd_jail_set(abi_ulong arg1, abi_long arg2,
+        abi_long arg3)
+{
+
+    qemu_log("qemu: Unsupported syscall jail_set()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* cap_enter(2) */
+static inline abi_long do_freebsd_cap_enter(void)
+{
+
+    qemu_log("qemu: Unsupported syscall cap_enter()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* cap_new(2) */
+static inline abi_long do_freebsd_cap_new(abi_long arg1, abi_ulong arg2)
+{
+
+    qemu_log("qemu: Unsupported syscall cap_new()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* cap_getrights(2) */
+static inline abi_long do_freebsd_cap_getrights(abi_long arg1, abi_ulong arg2)
+{
+
+    qemu_log("qemu: Unsupported syscall cap_getrights()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* cap_getmode(2) */
+static inline abi_long do_freebsd_cap_getmode(abi_ulong arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall cap_getmode()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* audit(2) */
+static inline abi_long do_freebsd_audit(abi_ulong arg1, abi_ulong arg2)
+{
+
+    qemu_log("qemu: Unsupported syscall audit()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* auditon(2) */
+static inline abi_long do_freebsd_auditon(abi_long arg1, abi_ulong arg2,
+        abi_ulong arg3)
+{
+
+    qemu_log("qemu: Unsupported syscall auditon()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* getaudit(2) */
+static inline abi_long do_freebsd_getaudit(abi_ulong arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall getaudit()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* setaudit(2) */
+static inline abi_long do_freebsd_setaudit(abi_ulong arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall setaudit()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* getaudit_addr(2) */
+static inline abi_long do_freebsd_getaudit_addr(abi_ulong arg1,
+        abi_ulong arg2)
+{
+
+    qemu_log("qemu: Unsupported syscall getaudit_addr()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* setaudit_addr(2) */
+static inline abi_long do_freebsd_setaudit_addr(abi_ulong arg1,
+        abi_ulong arg2)
+{
+
+    qemu_log("qemu: Unsupported syscall setaudit_addr()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* auditctl(2) */
+static inline abi_long do_freebsd_auditctl(abi_ulong arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall auditctl()\n");
+    return -TARGET_ENOSYS;
+}
+
+#endif /* ! __NETBSD_PROC_H_ */
diff --git a/bsd-user/openbsd/os-proc.c b/bsd-user/openbsd/os-proc.c
new file mode 100644
index 0000000..bc11d29
--- /dev/null
+++ b/bsd-user/openbsd/os-proc.c
@@ -0,0 +1,11 @@ 
+/*
+ * XXX To support FreeBSD binaries on NetBSD the following will need to be
+ * emulated.
+ */
+abi_long freebsd_exec_common(abi_ulong path_or_fd, abi_ulong guest_argp,
+        abi_ulong guest_envp, int do_fexec)
+{
+
+    qemu_log("qemu: Unsupported %s\n", __func__);
+    return -TARGET_ENOSYS;
+}
diff --git a/bsd-user/openbsd/os-proc.h b/bsd-user/openbsd/os-proc.h
new file mode 100644
index 0000000..9cce719
--- /dev/null
+++ b/bsd-user/openbsd/os-proc.h
@@ -0,0 +1,243 @@ 
+#ifndef __OPENBSD_PROC_H_
+#define __OPENBSD_PROC_H_
+
+/*
+ * XXX To support FreeBSD binaries on OpenBSD these syscalls will need
+ * to be emulated.
+ */
+
+/* execve(2) */
+static inline abi_long do_freebsd_execve(abi_ulong path_or_fd, abi_ulong argp,
+        abi_ulong envp)
+{
+
+    qemu_log("qemu: Unsupported syscall execve()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* fexecve(2) */
+static inline abi_long do_freebsd_fexecve(abi_ulong path_or_fd, abi_ulong argp,
+        abi_ulong envp)
+{
+
+    qemu_log("qemu: Unsupported syscall fexecve()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* wait4(2) */
+static inline abi_long do_freebsd_wait4(abi_long arg1, abi_ulong target_status,
+        abi_long arg3, abi_ulong target_rusage)
+{
+
+    qemu_log("qemu: Unsupported syscall wait4()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* setloginclass(2) */
+static inline abi_long do_freebsd_setloginclass(abi_ulong arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall setloginclass()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* getloginclass(2) */
+static inline abi_long do_freebsd_getloginclass(abi_ulong arg1, abi_ulong arg2)
+{
+
+    qemu_log("qemu: Unsupported syscall getloginclass()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* pdwait4(2) */
+static inline abi_long do_freebsd_pdwait4(abi_long arg1,
+        abi_ulong target_status, abi_long arg3, abi_ulong target_rusage)
+{
+
+    qemu_log("qemu: Unsupported syscall pdwait4()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* pdgetpid(2) */
+static inline abi_long do_freebsd_pdgetpid(abi_long fd, abi_ulong target_pidp)
+{
+
+    qemu_log("qemu: Unsupported syscall pdgetpid()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* undocumented __setugid */
+static inline abi_long do_freebsd___setugid(abi_long arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall __setugid()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* fork(2) */
+static inline abi_long do_freebsd_fork(void *cpu_env)
+{
+
+    qemu_log("qemu: Unsupported syscall fork()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* vfork(2) */
+static inline abi_long do_freebsd_vfork(void *cpu_env)
+{
+
+    qemu_log("qemu: Unsupported syscall vfork()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* rfork(2) */
+static inline abi_long do_freebsd_rfork(void *cpu_env, abi_long flags)
+{
+
+    qemu_log("qemu: Unsupported syscall rfork()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* pdfork(2) */
+static inline abi_long do_freebsd_pdfork(void *cpu_env, abi_ulong arg1,
+        abi_long arg2)
+{
+
+    qemu_log("qemu: Unsupported syscall pdfork()\n");
+    return -TARGET_ENOSYS
+}
+
+/* jail(2) */
+static inline abi_long do_freebsd_jail(abi_ulong arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall jail()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* jail_attach(2) */
+static inline abi_long do_freebsd_jail_attach(abi_long arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall jail_attach()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* jail_remove(2) */
+static inline abi_long do_freebsd_jail_remove(abi_long arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall jail_remove()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* jail_get(2) */
+static inline abi_long do_freebsd_jail_get(abi_ulong arg1, abi_long arg2,
+        abi_long arg3)
+{
+
+    qemu_log("qemu: Unsupported syscall jail_get()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* jail_set(2) */
+static inline abi_long do_freebsd_jail_set(abi_ulong arg1, abi_long arg2,
+        abi_long arg3)
+{
+
+    qemu_log("qemu: Unsupported syscall jail_set()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* cap_enter(2) */
+static inline abi_long do_freebsd_cap_enter(void)
+{
+
+    qemu_log("qemu: Unsupported syscall cap_enter()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* cap_new(2) */
+static inline abi_long do_freebsd_cap_new(abi_long arg1, abi_ulong arg2)
+{
+
+        qemu_log("qemu: Unsupported syscall cap_new()\n");
+            return -TARGET_ENOSYS;
+}
+
+/* cap_getrights(2) */
+static inline abi_long do_freebsd_cap_getrights(abi_long arg1, abi_ulong arg2)
+{
+
+        qemu_log("qemu: Unsupported syscall cap_getrights()\n");
+            return -TARGET_ENOSYS;
+}
+
+/* cap_getmode(2) */
+static inline abi_long do_freebsd_cap_getmode(abi_ulong arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall cap_getmode()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* audit(2) */
+static inline abi_long do_freebsd_audit(abi_ulong arg1, abi_ulong arg2)
+{
+
+    qemu_log("qemu: Unsupported syscall audit()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* auditon(2) */
+static inline abi_long do_freebsd_auditon(abi_long arg1, abi_ulong arg2,
+        abi_ulong arg3)
+{
+
+    qemu_log("qemu: Unsupported syscall auditon()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* getaudit(2) */
+static inline abi_long do_freebsd_getaudit(abi_ulong arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall getaudit()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* setaudit(2) */
+static inline abi_long do_freebsd_setaudit(abi_ulong arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall setaudit()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* getaudit_addr(2) */
+static inline abi_long do_freebsd_getaudit_addr(abi_ulong arg1,
+        abi_ulong arg2)
+{
+
+    qemu_log("qemu: Unsupported syscall getaudit_addr()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* setaudit_addr(2) */
+static inline abi_long do_freebsd_setaudit_addr(abi_ulong arg1,
+        abi_ulong arg2)
+{
+
+    qemu_log("qemu: Unsupported syscall setaudit_addr()\n");
+    return -TARGET_ENOSYS;
+}
+
+/* auditctl(2) */
+static inline abi_long do_freebsd_auditctl(abi_ulong arg1)
+{
+
+    qemu_log("qemu: Unsupported syscall auditctl()\n");
+    return -TARGET_ENOSYS;
+}
+
+#endif /* ! __OPENBSD_PROC_H_ */
diff --git a/bsd-user/qemu-bsd.h b/bsd-user/qemu-bsd.h
index 0add0b3..893a475 100644
--- a/bsd-user/qemu-bsd.h
+++ b/bsd-user/qemu-bsd.h
@@ -20,6 +20,12 @@ 
 #ifndef _QEMU_BSD_H_
 #define _QEMU_BSD_H_
 
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/resource.h>
+#include <sys/shm.h>
+#include <sys/wait.h>
+
 /* bsd-mem.c */
 abi_long target_to_host_ipc_perm(struct ipc_perm *host_ip,
         abi_ulong target_addr);
@@ -30,5 +36,12 @@  abi_long target_to_host_shmid_ds(struct shmid_ds *host_sd,
 abi_long host_to_target_shmid_ds(abi_ulong target_addr,
         struct shmid_ds *host_sd);
 
+/* bsd-proc.c */
+int target_to_host_resource(int code);
+rlim_t target_to_host_rlim(abi_ulong target_rlim);
+abi_ulong host_to_target_rlim(rlim_t rlim);
+abi_long host_to_target_rusage(abi_ulong target_addr,
+        const struct rusage *rusage);
+int host_to_target_waitstatus(int status);
 
 #endif /* !_QEMU_BSD_H_ */
diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h
index da69073..7ded869 100644
--- a/bsd-user/qemu.h
+++ b/bsd-user/qemu.h
@@ -144,6 +144,7 @@  int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs,
                     struct image_info *info);
 int load_flt_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs,
                     struct image_info *info);
+int is_target_elf_binary(int fd);
 
 abi_long memcpy_to_target(abi_ulong dest, const void *src,
                           unsigned long len);
@@ -232,6 +233,15 @@  extern unsigned long target_maxdsiz;
 extern unsigned long target_dflssiz;
 extern unsigned long target_maxssiz;
 extern unsigned long target_sgrowsiz;
+extern char qemu_proc_pathname[];
+
+/* syscall.c */
+abi_long get_errno(abi_long ret);
+int is_error(abi_long ret);
+
+/* os-proc.c */
+abi_long freebsd_exec_common(abi_ulong path_or_fd, abi_ulong guest_argp,
+        abi_ulong guest_envp, int do_fexec);
 
 /* user access */
 
diff --git a/bsd-user/syscall.c b/bsd-user/syscall.c
index a04c1bf..e8fe25c 100644
--- a/bsd-user/syscall.c
+++ b/bsd-user/syscall.c
@@ -32,17 +32,45 @@ 
 #include "qemu-common.h"
 
 #define target_to_host_bitmask(x, tbl) (x)
+static int host_to_target_errno(int err);
 
-#include "bsd-errno.h"
 #include "bsd-file.h"
 #include "bsd-mem.h"
 #include "bsd-signal.h"
+#include "bsd-proc.h"
 
 #include "os-time.h"
 #include "os-signal.h"
+#include "os-proc.h"
 
 /* #define DEBUG */
 
+/*
+ * errno conversion.
+ */
+abi_long get_errno(abi_long ret)
+{
+
+    if (ret == -1) {
+        /* XXX need to translate host -> target errnos here */
+        return -(errno);
+    } else {
+        return ret;
+    }
+}
+
+static int host_to_target_errno(int err)
+{
+    /* XXX need to translate host errnos here */
+    return err;
+}
+
+int is_error(abi_long ret)
+{
+
+    return (abi_ulong)ret >= (abi_ulong)(-4096);
+}
+
 #if defined(TARGET_I386)
 static abi_long do_freebsd_sysarch(CPUX86State *env, int op, abi_ulong parms)
 {
@@ -140,7 +168,7 @@  oidfmt(int *oid, int len, char *fmt, uint32_t *kind)
 
     if (fmt)
         strcpy(fmt, (char *)(buf + sizeof(uint32_t)));
-    return (0);
+    return 0;
 }
 
 /*
@@ -291,16 +319,257 @@  abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
         print_freebsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
 
     switch (num) {
-    case TARGET_FREEBSD_NR_exit:
-#ifdef TARGET_GPROF
-        _mcleanup();
+        /*
+         * process system calls
+         */
+    case TARGET_FREEBSD_NR_fork: /* fork(2) */
+        ret = do_freebsd_fork(cpu_env);
+        break;
+
+    case TARGET_FREEBSD_NR_vfork: /* vfork(2) */
+        ret = do_freebsd_vfork(cpu_env);
+        break;
+
+    case TARGET_FREEBSD_NR_rfork: /* rfork(2) */
+        ret = do_freebsd_rfork(cpu_env, arg1);
+        break;
+
+    case TARGET_FREEBSD_NR_pdfork: /* pdfork(2) */
+        ret = do_freebsd_pdfork(cpu_env, arg1, arg2);
+        break;
+
+    case TARGET_FREEBSD_NR_execve: /* execve(2) */
+        ret = do_freebsd_execve(arg1, arg2, arg3);
+        break;
+
+    case TARGET_FREEBSD_NR_fexecve: /* fexecve(2) */
+        ret = do_freebsd_fexecve(arg1, arg2, arg3);
+        break;
+
+    case TARGET_FREEBSD_NR_wait4: /* wait4(2) */
+        ret = do_freebsd_wait4(arg1, arg2, arg3, arg4);
+        break;
+
+    case TARGET_FREEBSD_NR_exit: /* exit(2) */
+        ret = do_bsd_exit(cpu_env, arg1);
+        break;
+
+    case TARGET_FREEBSD_NR_getgroups: /* getgroups(2) */
+        ret = do_bsd_getgroups(arg1, arg2);
+        break;
+
+    case TARGET_FREEBSD_NR_setgroups: /* setgroups(2) */
+        ret = do_bsd_setgroups(arg1, arg2);
+        break;
+
+    case TARGET_FREEBSD_NR_umask: /* umask(2) */
+        ret = do_bsd_umask(arg1);
+        break;
+
+    case TARGET_FREEBSD_NR_setlogin: /* setlogin(2) */
+        ret = do_bsd_setlogin(arg1);
+        break;
+
+    case TARGET_FREEBSD_NR_getlogin: /* getlogin(2) */
+        ret = do_bsd_getlogin(arg1, arg2);
+        break;
+
+    case TARGET_FREEBSD_NR_getrusage: /* getrusage(2) */
+        ret = do_bsd_getrusage(arg1, arg2);
+        break;
+
+    case TARGET_FREEBSD_NR_getrlimit: /* getrlimit(2) */
+        ret = do_bsd_getrlimit(arg1, arg2);
+        break;
+
+    case TARGET_FREEBSD_NR_setrlimit: /* setrlimit(2) */
+        ret = do_bsd_setrlimit(arg1, arg2);
+        break;
+
+    case TARGET_FREEBSD_NR_getpid: /* getpid(2) */
+        ret = do_bsd_getpid();
+        break;
+
+    case TARGET_FREEBSD_NR_getppid: /* getppid(2) */
+        ret = do_bsd_getppid();
+        break;
+
+    case TARGET_FREEBSD_NR_getuid: /* getuid(2) */
+        ret = do_bsd_getuid();
+        break;
+
+    case TARGET_FREEBSD_NR_geteuid: /* geteuid(2) */
+        ret = do_bsd_geteuid();
+        break;
+
+    case TARGET_FREEBSD_NR_getgid: /* getgid(2) */
+        ret = do_bsd_getgid();
+        break;
+
+    case TARGET_FREEBSD_NR_getegid: /* getegid(2) */
+        ret = do_bsd_getegid();
+        break;
+
+    case TARGET_FREEBSD_NR_setuid: /* setuid(2) */
+        ret = do_bsd_setuid(arg1);
+        break;
+
+    case TARGET_FREEBSD_NR_seteuid: /* seteuid(2) */
+        ret = do_bsd_seteuid(arg1);
+        break;
+
+    case TARGET_FREEBSD_NR_setgid: /* setgid(2) */
+        ret = do_bsd_setgid(arg1);
+        break;
+
+    case TARGET_FREEBSD_NR_setegid: /* setegid(2) */
+        ret = do_bsd_setegid(arg1);
+        break;
+
+    case TARGET_FREEBSD_NR_getpgrp: /* getpgrp(2) */
+        ret = do_bsd_getpgrp();
+        break;
+
+    case TARGET_FREEBSD_NR_setreuid: /* setreuid(2) */
+        ret = do_bsd_setreuid(arg1, arg2);
+        break;
+
+    case TARGET_FREEBSD_NR_setregid: /* setregid(2) */
+        ret = do_bsd_setregid(arg1, arg2);
+        break;
+
+    case TARGET_FREEBSD_NR_getresuid: /* getresuid(2) */
+        ret = do_bsd_getresuid(arg1, arg2, arg3);
+        break;
+
+    case TARGET_FREEBSD_NR_getresgid: /* getresgid(2) */
+        ret = do_bsd_getresgid(arg1, arg2, arg3);
+        break;
+
+    case TARGET_FREEBSD_NR_getsid: /* getsid(2) */
+        ret = do_bsd_getsid(arg1);
+        break;
+
+    case TARGET_FREEBSD_NR_setsid: /* setsid(2) */
+        ret = do_bsd_setsid();
+        break;
+
+    case TARGET_FREEBSD_NR_issetugid: /* issetugid(2) */
+        ret = do_bsd_issetugid();
+        break;
+
+    case TARGET_FREEBSD_NR_profil: /* profil(2) */
+        ret = do_bsd_profil(arg1, arg2, arg3, arg4);
+        break;
+
+    case TARGET_FREEBSD_NR_ktrace: /* ktrace(2) */
+        ret = do_bsd_ktrace(arg1, arg2, arg3, arg4);
+        break;
+
+    case TARGET_FREEBSD_NR_setloginclass: /* setloginclass(2) */
+        ret = do_freebsd_setloginclass(arg1);
+        break;
+
+    case TARGET_FREEBSD_NR_getloginclass: /* getloginclass(2) */
+        ret = do_freebsd_getloginclass(arg1, arg2);
+        break;
+
+#if 0
+    case TARGET_FREEBSD_NR_pdwait4: /* pdwait4(2) */
+        ret = do_freebsd_pdwait4(arg1, arg2, arg3, arg4);
+        break;
 #endif
-        gdb_exit(cpu_env, arg1);
-        /* XXX: should free thread stack and CPU env */
-        _exit(arg1);
-        ret = 0; /* avoid warning */
+
+    case TARGET_FREEBSD_NR_pdgetpid: /* pdgetpid(2) */
+        ret = do_freebsd_pdgetpid(arg1, arg2);
+        break;
+
+    case TARGET_FREEBSD_NR___setugid: /* undocumented */
+        ret = do_freebsd___setugid(arg1);
+        break;
+
+    case TARGET_FREEBSD_NR_jail: /* jail(2) */
+        ret = do_freebsd_jail(arg1);
+        break;
+
+    case TARGET_FREEBSD_NR_jail_attach: /* jail_attach(2) */
+        ret = do_freebsd_jail_attach(arg1);
+        break;
+
+    case TARGET_FREEBSD_NR_jail_remove: /* jail_remove(2) */
+        ret = do_freebsd_jail_remove(arg1);
+        break;
+
+    case TARGET_FREEBSD_NR_jail_get: /* jail_get(2) */
+        ret = do_freebsd_jail_get(arg1, arg2, arg3);
+        break;
+
+    case TARGET_FREEBSD_NR_jail_set: /* jail_set(2) */
+        ret = do_freebsd_jail_set(arg1, arg2, arg3);
+        break;
+
+    case TARGET_FREEBSD_NR_cap_enter: /* cap_enter(2) */
+        ret = do_freebsd_cap_enter();
+        break;
+
+    case TARGET_FREEBSD_NR_cap_new: /* cap_new(2) */
+        ret = do_freebsd_cap_new(arg1, arg2);
+        break;
+
+    case TARGET_FREEBSD_NR_cap_getrights: /* cap_getrights(2) */
+        ret = do_freebsd_cap_getrights(arg1, arg2);
+        break;
+
+    case TARGET_FREEBSD_NR_cap_getmode: /* cap_getmode(2) */
+        ret = do_freebsd_cap_getmode(arg1);
         break;
 
+    case TARGET_FREEBSD_NR_audit: /* audit(2) */
+        ret = do_freebsd_audit(arg1, arg2);
+        break;
+
+    case TARGET_FREEBSD_NR_auditon: /* auditon(2) */
+        ret = do_freebsd_auditon(arg1, arg2, arg3);
+        break;
+
+    case TARGET_FREEBSD_NR_getaudit: /* getaudit(2) */
+        ret = do_freebsd_getaudit(arg1);
+        break;
+
+    case TARGET_FREEBSD_NR_setaudit: /* setaudit(2) */
+        ret = do_freebsd_setaudit(arg1);
+        break;
+
+    case TARGET_FREEBSD_NR_getaudit_addr: /* getaudit_addr(2) */
+        ret = do_freebsd_getaudit_addr(arg1, arg2);
+        break;
+
+    case TARGET_FREEBSD_NR_setaudit_addr: /* setaudit_addr(2) */
+        ret = do_freebsd_setaudit_addr(arg1, arg2);
+        break;
+
+    case TARGET_FREEBSD_NR_auditctl: /* auditctl(2) */
+        ret = do_freebsd_auditctl(arg1);
+        break;
+
+    case TARGET_FREEBSD_NR_utrace: /* utrace(2) */
+        ret = do_bsd_utrace(arg1, arg2);
+        break;
+
+    case TARGET_FREEBSD_NR_ptrace: /* ptrace(2) */
+        ret = do_bsd_ptrace(arg1, arg2, arg3, arg4);
+        break;
+
+    case TARGET_FREEBSD_NR_getpriority: /* getpriority(2) */
+        ret = do_bsd_getpriority(arg1, arg2);
+        break;
+
+    case TARGET_FREEBSD_NR_setpriority: /* setpriority(2) */
+        ret = do_bsd_setpriority(arg1, arg2, arg3);
+        break;
+
+
+
         /*
          * File system calls.
          */
@@ -877,13 +1146,7 @@  abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1,
 
     switch (num) {
     case TARGET_NETBSD_NR_exit:
-#ifdef TARGET_GPROF
-        _mcleanup();
-#endif
-        gdb_exit(cpu_env, arg1);
-        /* XXX: should free thread stack and CPU env */
-        _exit(arg1);
-        ret = 0; /* avoid warning */
+        ret = do_bsd_exit(cpu_env, arg1);
         break;
 
     case TARGET_NETBSD_NR_read:
@@ -933,13 +1196,7 @@  abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
 
     switch (num) {
     case TARGET_OPENBSD_NR_exit:
-#ifdef TARGET_GPROF
-        _mcleanup();
-#endif
-        gdb_exit(cpu_env, arg1);
-        /* XXX: should free thread stack and CPU env */
-        _exit(arg1);
-        ret = 0; /* avoid warning */
+        ret = do_bsd_exit(cpu_env, arg1);
         break;
 
     case TARGET_OPENBSD_NR_read:
diff --git a/bsd-user/syscall_defs.h b/bsd-user/syscall_defs.h
index 7b85835..a69b31b 100644
--- a/bsd-user/syscall_defs.h
+++ b/bsd-user/syscall_defs.h
@@ -159,4 +159,54 @@  struct target_freebsd_kevent {
     abi_ulong  udata;
 } __packed;
 
+/*
+ *  sys/resource.h
+ */
+#if defined(__FreeBSD__) && defined(TARGET_ALPHA)
+#define TARGET_RLIM_INFINITY    0x7fffffffffffffffull
+#elif defined(__FreeBSD__) && (defined(TARGET_MIPS) || \
+        (defined(TARGET_SPARC) && TARGET_ABI_BITS == 32))
+#define TARGET_RLIM_INFINITY    0x7fffffffUL
+#else
+#define TARGET_RLIM_INFINITY    ((abi_ulong)-1)
+#endif
+
+#define TARGET_RLIMIT_CPU       0
+#define TARGET_RLIMIT_FSIZE     1
+#define TARGET_RLIMIT_DATA      2
+#define TARGET_RLIMIT_STACK     3
+#define TARGET_RLIMIT_CORE      4
+#define TARGET_RLIMIT_RSS       5
+#define TARGET_RLIMIT_MEMLOCK   6
+#define TARGET_RLIMIT_NPROC     7
+#define TARGET_RLIMIT_NOFILE    8
+#define TARGET_RLIMIT_SBSIZE    9
+#define TARGET_RLIMIT_AS        10
+#define TARGET_RLIMIT_NPTS      11
+#define TARGET_RLIMIT_SWAP      12
+
+struct target_rlimit {
+    uint64_t rlim_cur;
+    uint64_t rlim_max;
+};
+
+struct target_freebsd_rusage {
+    struct target_freebsd_timeval ru_utime; /* user time used */
+    struct target_freebsd_timeval ru_stime; /* system time used */
+    abi_long    ru_maxrss;      /* maximum resident set size */
+    abi_long    ru_ixrss;       /* integral shared memory size */
+    abi_long    ru_idrss;       /* integral unshared data size */
+    abi_long    ru_isrss;       /* integral unshared stack size */
+    abi_long    ru_minflt;      /* page reclaims */
+    abi_long    ru_majflt;      /* page faults */
+    abi_long    ru_nswap;       /* swaps */
+    abi_long    ru_inblock;     /* block input operations */
+    abi_long    ru_oublock;     /* block output operations */
+    abi_long    ru_msgsnd;      /* messages sent */
+    abi_long    ru_msgrcv;      /* messages received */
+    abi_long    ru_nsignals;    /* signals received */
+    abi_long    ru_nvcsw;       /* voluntary context switches */
+    abi_long    ru_nivcsw;      /* involuntary context switches */
+};
+
 #endif /* ! _SYSCALL_DEFS_H_ */
diff --git a/configure b/configure
index 749eba8..ba90975 100755
--- a/configure
+++ b/configure
@@ -457,7 +457,8 @@  FreeBSD)
   audio_drv_list="oss"
   audio_possible_drivers="oss sdl esd pa"
   # needed for kinfo_getvmmap(3) in libutil.h
-  LIBS="-lutil $LIBS"
+  # -lprocstat needed for procstat_*(3) in bsd-user/main.c
+  LIBS="-lprocstat -lutil $LIBS"
   TARGET_OS="freebsd"
 ;;
 DragonFly)