diff mbox series

[v4,10/16] linux-user: Split out some simple file syscalls

Message ID 20180818190118.12911-11-richard.henderson@linaro.org
State New
Headers show
Series linux-user: Split do_syscall | expand

Commit Message

Richard Henderson Aug. 18, 2018, 7:01 p.m. UTC
This includes close, open, openat, read, readlink, readlinkat, write.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>

---
 linux-user/strace.c           |  64 -----
 linux-user/syscall-file.inc.c | 440 ++++++++++++++++++++++++++++++++++
 linux-user/syscall.c          | 393 ++----------------------------
 linux-user/strace.list        |  21 --
 4 files changed, 463 insertions(+), 455 deletions(-)
 create mode 100644 linux-user/syscall-file.inc.c

-- 
2.17.1

Comments

Laurent Vivier Aug. 22, 2018, 12:50 a.m. UTC | #1
Le 18/08/2018 à 21:01, Richard Henderson a écrit :
> This includes close, open, openat, read, readlink, readlinkat, write.

> 

> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>

> ---

>  linux-user/strace.c           |  64 -----

>  linux-user/syscall-file.inc.c | 440 ++++++++++++++++++++++++++++++++++

>  linux-user/syscall.c          | 393 ++----------------------------

>  linux-user/strace.list        |  21 --

>  4 files changed, 463 insertions(+), 455 deletions(-)

>  create mode 100644 linux-user/syscall-file.inc.c

> 

...
> +#include "syscall-file.inc.c"

> +

>  static const SyscallDef *syscall_table(int num)

>  {

> -#define S(NAME)  case TARGET_NR_##NAME: return &def_##NAME;

> +#define S(NAME)  case TARGET_NR_##NAME: return &def_##NAME

>  

>      switch (num) {

> +    /*

> +     * Unconditional syscalls.

> +     */

> +    S(close);

> +    S(openat);

> +    S(read);

> +    S(write);

> +

> +    /*

> +     * Conditional syscalls.

> +     */

> +#ifdef TARGET_NR_open

> +    S(open);

> +#endif

> +#ifdef TARGET_NR_readlink

> +    S(readlink);

> +#endif

> +#ifdef TARGET_NR_readlinkat

> +    S(readlinkat);

> +#endif

>      }

>      return NULL;


I don't understand why you need/want to duplicate the list of syscalls here.

If I modify your patch as following, it works without duplicating the list:

diff --git a/linux-user/syscall-file.def.c b/linux-user/syscall-file.def.c
new file mode 100644
index 0000000000..78b1bd0467
--- /dev/null
+++ b/linux-user/syscall-file.def.c
@@ -0,0 +1,13 @@
+SYSCALL_DEF(close, ARG_DEC);
+#ifdef TARGET_NR_open
+SYSCALL_DEF(open, ARG_STR, ARG_OPENFLAG, ARG_MODEFLAG);
+#endif
+SYSCALL_DEF(openat, ARG_ATDIRFD, ARG_STR, ARG_OPENFLAG, ARG_MODEFLAG);
+SYSCALL_DEF(read, ARG_DEC, ARG_PTR, ARG_DEC);
+#ifdef TARGET_NR_readlink
+SYSCALL_DEF(readlink, ARG_STR, ARG_PTR, ARG_DEC);
+#endif
+#ifdef TARGET_NR_readlinkat
+SYSCALL_DEF(readlinkat, ARG_ATDIRFD, ARG_STR, ARG_PTR, ARG_DEC);
+#endif
+SYSCALL_DEF(write, ARG_DEC, ARG_PTR, ARG_DEC);
diff --git a/linux-user/syscall-file.inc.c b/linux-user/syscall-file.inc.c
index aecc63682f..dda04200ed 100644
--- a/linux-user/syscall-file.inc.c
+++ b/linux-user/syscall-file.inc.c
@@ -307,21 +307,18 @@ SYSCALL_IMPL(close)
     fd_trans_unregister(arg1);
     return get_errno(close(arg1));
 }
-SYSCALL_DEF(close, ARG_DEC);

 #ifdef TARGET_NR_open
 SYSCALL_IMPL(open)
 {
     return do_openat(cpu_env, AT_FDCWD, arg1, arg2, arg3);
 }
-SYSCALL_DEF(open, ARG_STR, ARG_OPENFLAG, ARG_MODEFLAG);
 #endif

 SYSCALL_IMPL(openat)
 {
     return do_openat(cpu_env, arg1, arg2, arg3, arg4);
 }
-SYSCALL_DEF(openat, ARG_ATDIRFD, ARG_STR, ARG_OPENFLAG, ARG_MODEFLAG);

 SYSCALL_IMPL(read)
 {
@@ -346,7 +343,6 @@ SYSCALL_IMPL(read)
     unlock_user(p, arg2, ret);
     return ret;
 }
-SYSCALL_DEF(read, ARG_DEC, ARG_PTR, ARG_DEC);

 static abi_long do_readlink_proc_exe(char *buf, abi_ulong bufsiz)
 {
@@ -386,7 +382,6 @@ SYSCALL_IMPL(readlink)
     unlock_user(p, arg1, 0);
     return ret;
 }
-SYSCALL_DEF(readlink, ARG_STR, ARG_PTR, ARG_DEC);
 #endif

 #ifdef TARGET_NR_readlinkat
@@ -411,7 +406,6 @@ SYSCALL_IMPL(readlinkat)
     unlock_user(p, arg2, 0);
     return ret;
 }
-SYSCALL_DEF(readlinkat, ARG_ATDIRFD, ARG_STR, ARG_PTR, ARG_DEC);
 #endif

 SYSCALL_IMPL(write)
@@ -437,4 +431,4 @@ SYSCALL_IMPL(write)
     unlock_user(p, arg2, 0);
     return ret;
 }
-SYSCALL_DEF(write, ARG_DEC, ARG_PTR, ARG_DEC);
+#include "syscall-file.def.c"
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index f757ae87b0..21e7700255 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -12117,33 +12117,15 @@ static abi_long do_syscall1(void *cpu_env, int
num, abi_long arg1,

 static const SyscallDef *syscall_table(int num)
 {
-#define S(NAME)  case TARGET_NR_##NAME: return &def_##NAME
+#undef SYSCALL_DEF
+#define SYSCALL_DEF(NAME, ...)  case TARGET_NR_##NAME: return &def_##NAME

     switch (num) {
-    /*
-     * Unconditional syscalls.
-     */
-    S(close);
-    S(openat);
-    S(read);
-    S(write);
-
-    /*
-     * Conditional syscalls.
-     */
-#ifdef TARGET_NR_open
-    S(open);
-#endif
-#ifdef TARGET_NR_readlink
-    S(readlink);
-#endif
-#ifdef TARGET_NR_readlinkat
-    S(readlinkat);
-#endif
+#include "syscall-file.def.c"
     }
     return NULL;

-#undef S
+#undef SYSCALL_DEF
 }

 abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
Richard Henderson Aug. 22, 2018, 10:58 p.m. UTC | #2
On 08/21/2018 05:50 PM, Laurent Vivier wrote:
> I don't understand why you need/want to duplicate the list of syscalls here.

> 

> If I modify your patch as following, it works without duplicating the list:

> 

> diff --git a/linux-user/syscall-file.def.c b/linux-user/syscall-file.def.c

> new file mode 100644

> index 0000000000..78b1bd0467

> --- /dev/null

> +++ b/linux-user/syscall-file.def.c

> @@ -0,0 +1,13 @@

> +SYSCALL_DEF(close, ARG_DEC);

> +#ifdef TARGET_NR_open

> +SYSCALL_DEF(open, ARG_STR, ARG_OPENFLAG, ARG_MODEFLAG);

> +#endif

> +SYSCALL_DEF(openat, ARG_ATDIRFD, ARG_STR, ARG_OPENFLAG, ARG_MODEFLAG);

> +SYSCALL_DEF(read, ARG_DEC, ARG_PTR, ARG_DEC);

> +#ifdef TARGET_NR_readlink

> +SYSCALL_DEF(readlink, ARG_STR, ARG_PTR, ARG_DEC);

> +#endif

> +#ifdef TARGET_NR_readlinkat

> +SYSCALL_DEF(readlinkat, ARG_ATDIRFD, ARG_STR, ARG_PTR, ARG_DEC);

> +#endif

> +SYSCALL_DEF(write, ARG_DEC, ARG_PTR, ARG_DEC);


Sort-of interesting, but I do have other definitions of
syscall structures that do not use this macro.

Please look through e.g. patch 15 and suggest how I might
define mmap2 with your scheme.


r~
Laurent Vivier Aug. 23, 2018, 3:48 p.m. UTC | #3
Le 23/08/2018 à 00:58, Richard Henderson a écrit :
> On 08/21/2018 05:50 PM, Laurent Vivier wrote:

>> I don't understand why you need/want to duplicate the list of syscalls here.

>>

>> If I modify your patch as following, it works without duplicating the list:

>>

>> diff --git a/linux-user/syscall-file.def.c b/linux-user/syscall-file.def.c

>> new file mode 100644

>> index 0000000000..78b1bd0467

>> --- /dev/null

>> +++ b/linux-user/syscall-file.def.c

>> @@ -0,0 +1,13 @@

>> +SYSCALL_DEF(close, ARG_DEC);

>> +#ifdef TARGET_NR_open

>> +SYSCALL_DEF(open, ARG_STR, ARG_OPENFLAG, ARG_MODEFLAG);

>> +#endif

>> +SYSCALL_DEF(openat, ARG_ATDIRFD, ARG_STR, ARG_OPENFLAG, ARG_MODEFLAG);

>> +SYSCALL_DEF(read, ARG_DEC, ARG_PTR, ARG_DEC);

>> +#ifdef TARGET_NR_readlink

>> +SYSCALL_DEF(readlink, ARG_STR, ARG_PTR, ARG_DEC);

>> +#endif

>> +#ifdef TARGET_NR_readlinkat

>> +SYSCALL_DEF(readlinkat, ARG_ATDIRFD, ARG_STR, ARG_PTR, ARG_DEC);

>> +#endif

>> +SYSCALL_DEF(write, ARG_DEC, ARG_PTR, ARG_DEC);

> 

> Sort-of interesting, but I do have other definitions of

> syscall structures that do not use this macro.

> 

> Please look through e.g. patch 15 and suggest how I might

> define mmap2 with your scheme.


We can add a base macro in syscall.h:

#define SYSCALL_DECL(NAME, ARGS, IMPL, PRINT, PRINT_RET, ...) \
    static const SyscallDef def_##NAME = { \
        .name = #NAME, .args = ARGS, .impl = IMPL, .print = PRINT, \
        .print_ret = PRINT_RET, .arg_type = { __VA_ARGS__ } \
    }

and use it with the others:

#define SYSCALL_DEF(NAME, ...) \
    SYSCALL_DECL(NAME, NULL, impl_##NAME, NULL, NULL, __VA_ARGS__)

#define SYSCALL_DEF_ARGS(NAME, ...) \
    SYSCALL_DECL(NAME, args_##NAME, impl_##NAME, NULL, NULL, __VA_ARGS__)

Then in syscall.c:

static const SyscallDef *syscall_table(int num)
{
#undef SYSCALL_DECL
#define SYSCALL_DECL(NAME, ...)  \
    case TARGET_NR_##NAME: return &def_##NAME

    switch (num) {
#include "syscall-file.def.c"
#include "syscall-ipc.def.c"
#include "syscall-mem.def.c"
#include "syscall-proc.def.c"
    }
    return NULL;

#undef SYSCALL_DECL
}

and for mmap2 in syscall-mem.def.c:

SYSCALL_DEF(mlock, ARG_PTR, ARG_DEC);
SYSCALL_DEF(mlockall, ARG_HEX);
#ifdef TARGET_NR_mmap
SYSCALL_DECL(mmap, args_mmap, impl_mmap, NULL, print_syscall_ptr_ret,
             ARG_PTR, ARG_DEC, ARG_MMAPPROT, ARG_MMAPFLAG, ARG_DEC,
ARG_DEC);
#endif
#ifdef TARGET_NR_mmap2
SYSCALL_DECL(mmap2, args_mmap2, impl_mmap, NULL, print_syscall_ptr_ret,
             ARG_PTR, ARG_DEC, ARG_MMAPPROT, ARG_MMAPFLAG, ARG_DEC,
ARG_DEC64);
#endif
...

I pushed the modifications into:
git://github.com/vivier/qemu.git lu-split-4

Note that ipc_shmat can't use the macro (because the name of structure
is def_ipc_shmat, and not def_shmat), but we don't need it because it is
not exported to the syscall_table and only used locally by
SYSCALL_ARGS(ipc).

And doing like this, I think we don't need to add -Wunused-const-variable.

Thanks,
Laurent
diff mbox series

Patch

diff --git a/linux-user/strace.c b/linux-user/strace.c
index 792644cc0d..11e0c86554 100644
--- a/linux-user/strace.c
+++ b/linux-user/strace.c
@@ -2213,41 +2213,6 @@  print_mq_open(const struct syscallname *name,
 }
 #endif
 
-#ifdef TARGET_NR_open
-static void
-print_open(const struct syscallname *name,
-    abi_long arg0, abi_long arg1, abi_long arg2,
-    abi_long arg3, abi_long arg4, abi_long arg5)
-{
-    int is_creat = (arg1 & TARGET_O_CREAT);
-
-    print_syscall_prologue(name);
-    print_string(arg0, 0);
-    print_open_flags(arg1, (is_creat == 0));
-    if (is_creat)
-        print_file_mode(arg2, 1);
-    print_syscall_epilogue(name);
-}
-#endif
-
-#ifdef TARGET_NR_openat
-static void
-print_openat(const struct syscallname *name,
-    abi_long arg0, abi_long arg1, abi_long arg2,
-    abi_long arg3, abi_long arg4, abi_long arg5)
-{
-    int is_creat = (arg2 & TARGET_O_CREAT);
-
-    print_syscall_prologue(name);
-    print_at_dirfd(arg0, 0);
-    print_string(arg1, 0);
-    print_open_flags(arg2, (is_creat == 0));
-    if (is_creat)
-        print_file_mode(arg3, 1);
-    print_syscall_epilogue(name);
-}
-#endif
-
 #ifdef TARGET_NR_mq_unlink
 static void
 print_mq_unlink(const struct syscallname *name,
@@ -2276,35 +2241,6 @@  print_fstatat64(const struct syscallname *name,
 #define print_newfstatat    print_fstatat64
 #endif
 
-#ifdef TARGET_NR_readlink
-static void
-print_readlink(const struct syscallname *name,
-    abi_long arg0, abi_long arg1, abi_long arg2,
-    abi_long arg3, abi_long arg4, abi_long arg5)
-{
-    print_syscall_prologue(name);
-    print_string(arg0, 0);
-    print_pointer(arg1, 0);
-    print_raw_param("%u", arg2, 1);
-    print_syscall_epilogue(name);
-}
-#endif
-
-#ifdef TARGET_NR_readlinkat
-static void
-print_readlinkat(const struct syscallname *name,
-    abi_long arg0, abi_long arg1, abi_long arg2,
-    abi_long arg3, abi_long arg4, abi_long arg5)
-{
-    print_syscall_prologue(name);
-    print_at_dirfd(arg0, 0);
-    print_string(arg1, 0);
-    print_pointer(arg2, 0);
-    print_raw_param("%u", arg3, 1);
-    print_syscall_epilogue(name);
-}
-#endif
-
 #ifdef TARGET_NR_rename
 static void
 print_rename(const struct syscallname *name,
diff --git a/linux-user/syscall-file.inc.c b/linux-user/syscall-file.inc.c
new file mode 100644
index 0000000000..aecc63682f
--- /dev/null
+++ b/linux-user/syscall-file.inc.c
@@ -0,0 +1,440 @@ 
+/*
+ *  Linux file-related syscalls
+ *  Copyright (c) 2003 Fabrice Bellard
+ *
+ *  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/>.
+ */
+
+
+/*
+ * Helpers for do_openat, manipulating /proc/self/foo.
+ */
+
+static int open_self_cmdline(void *cpu_env, int fd)
+{
+    CPUState *cpu = ENV_GET_CPU((CPUArchState *)cpu_env);
+    struct linux_binprm *bprm = ((TaskState *)cpu->opaque)->bprm;
+    int i;
+
+    for (i = 0; i < bprm->argc; i++) {
+        size_t len = strlen(bprm->argv[i]) + 1;
+
+        if (write(fd, bprm->argv[i], len) != len) {
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+static int open_self_maps(void *cpu_env, int fd)
+{
+    CPUState *cpu = ENV_GET_CPU((CPUArchState *)cpu_env);
+    TaskState *ts = cpu->opaque;
+    FILE *fp;
+    char *line = NULL;
+    size_t len = 0;
+    ssize_t read;
+
+    fp = fopen("/proc/self/maps", "r");
+    if (fp == NULL) {
+        return -1;
+    }
+
+    while ((read = getline(&line, &len, fp)) != -1) {
+        int fields, dev_maj, dev_min, inode;
+        uint64_t min, max, offset;
+        char flag_r, flag_w, flag_x, flag_p;
+        char path[512] = "";
+
+        fields = sscanf(line, "%"PRIx64"-%"PRIx64" %c%c%c%c %"
+                        PRIx64" %x:%x %d %512s",
+                        &min, &max, &flag_r, &flag_w, &flag_x,
+                        &flag_p, &offset, &dev_maj, &dev_min, &inode, path);
+
+        if ((fields < 10) || (fields > 11)) {
+            continue;
+        }
+        if (h2g_valid(min)) {
+            int flags = page_get_flags(h2g(min));
+
+            if (!h2g_valid(max - 1)) {
+                max = (uintptr_t)g2h(GUEST_ADDR_MAX) + 1;
+            }
+            if (page_check_range(h2g(min), max - min, flags) == -1) {
+                continue;
+            }
+            if (h2g(min) == ts->info->stack_limit) {
+                pstrcpy(path, sizeof(path), "      [stack]");
+            }
+            dprintf(fd, TARGET_ABI_FMT_lx "-" TARGET_ABI_FMT_lx
+                    " %c%c%c%c %08" PRIx64 " %02x:%02x %d %s%s\n",
+                    h2g(min), h2g(max - 1) + 1, flag_r, flag_w,
+                    flag_x, flag_p, offset, dev_maj, dev_min, inode,
+                    path[0] ? "         " : "", path);
+        }
+    }
+
+    free(line);
+    fclose(fp);
+
+    return 0;
+}
+
+static int open_self_stat(void *cpu_env, int fd)
+{
+    CPUState *cpu = ENV_GET_CPU((CPUArchState *)cpu_env);
+    TaskState *ts = cpu->opaque;
+    abi_ulong start_stack = ts->info->start_stack;
+    int i;
+
+    for (i = 0; i < 44; i++) {
+        char buf[128];
+        int len;
+        uint64_t val = 0;
+
+        if (i == 0) {
+            /* pid */
+            val = getpid();
+            snprintf(buf, sizeof(buf), "%"PRId64 " ", val);
+        } else if (i == 1) {
+            /* app name */
+            snprintf(buf, sizeof(buf), "(%s) ", ts->bprm->argv[0]);
+        } else if (i == 27) {
+            /* stack bottom */
+            val = start_stack;
+            snprintf(buf, sizeof(buf), "%"PRId64 " ", val);
+        } else {
+            /* for the rest, there is MasterCard */
+            snprintf(buf, sizeof(buf), "0%c", i == 43 ? '\n' : ' ');
+        }
+
+        len = strlen(buf);
+        if (write(fd, buf, len) != len) {
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+static int open_self_auxv(void *cpu_env, int fd)
+{
+    CPUState *cpu = ENV_GET_CPU((CPUArchState *)cpu_env);
+    TaskState *ts = cpu->opaque;
+    abi_ulong auxv = ts->info->saved_auxv;
+    abi_ulong len = ts->info->auxv_len;
+    char *ptr;
+
+    /*
+     * Auxiliary vector is stored in target process stack.
+     * read in whole auxv vector and copy it to file
+     */
+    ptr = lock_user(VERIFY_READ, auxv, len, 0);
+    if (ptr != NULL) {
+        while (len > 0) {
+            ssize_t r;
+            r = write(fd, ptr, len);
+            if (r <= 0) {
+                break;
+            }
+            len -= r;
+            ptr += r;
+        }
+        lseek(fd, 0, SEEK_SET);
+        unlock_user(ptr, auxv, len);
+    }
+
+    return 0;
+}
+
+static int is_proc_myself(const char *filename, const char *entry)
+{
+    if (!strncmp(filename, "/proc/", strlen("/proc/"))) {
+        filename += strlen("/proc/");
+        if (!strncmp(filename, "self/", strlen("self/"))) {
+            filename += strlen("self/");
+        } else if (*filename >= '1' && *filename <= '9') {
+            char myself[80];
+            snprintf(myself, sizeof(myself), "%d/", getpid());
+            if (!strncmp(filename, myself, strlen(myself))) {
+                filename += strlen(myself);
+            } else {
+                return 0;
+            }
+        } else {
+            return 0;
+        }
+        if (!strcmp(filename, entry)) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
+static int is_proc(const char *filename, const char *entry)
+{
+    return strcmp(filename, entry) == 0;
+}
+
+static int open_net_route(void *cpu_env, int fd)
+{
+    FILE *fp;
+    char *line = NULL;
+    size_t len = 0;
+    ssize_t read;
+
+    fp = fopen("/proc/net/route", "r");
+    if (fp == NULL) {
+        return -1;
+    }
+
+    /* read header */
+
+    read = getline(&line, &len, fp);
+    dprintf(fd, "%s", line);
+
+    /* read routes */
+
+    while ((read = getline(&line, &len, fp)) != -1) {
+        char iface[16];
+        uint32_t dest, gw, mask;
+        unsigned int flags, refcnt, use, metric, mtu, window, irtt;
+        sscanf(line, "%s\t%08x\t%08x\t%04x\t%d\t%d\t%d\t%08x\t%d\t%u\t%u\n",
+               iface, &dest, &gw, &flags, &refcnt, &use, &metric,
+               &mask, &mtu, &window, &irtt);
+        dprintf(fd, "%s\t%08x\t%08x\t%04x\t%d\t%d\t%d\t%08x\t%d\t%u\t%u\n",
+                iface, tswap32(dest), tswap32(gw), flags, refcnt, use,
+                metric, tswap32(mask), mtu, window, irtt);
+    }
+
+    free(line);
+    fclose(fp);
+
+    return 0;
+}
+#endif
+
+static abi_long do_openat(void *cpu_env, int dirfd, abi_ulong target_path,
+                          int target_flags, int mode)
+{
+    struct fake_open {
+        const char *filename;
+        int (*fill)(void *cpu_env, int fd);
+        int (*cmp)(const char *s1, const char *s2);
+    };
+    static const struct fake_open fakes[] = {
+        { "maps", open_self_maps, is_proc_myself },
+        { "stat", open_self_stat, is_proc_myself },
+        { "auxv", open_self_auxv, is_proc_myself },
+        { "cmdline", open_self_cmdline, is_proc_myself },
+#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
+        { "/proc/net/route", open_net_route, is_proc },
+#endif
+    };
+
+    char *pathname = lock_user_string(target_path);
+    int flags = target_to_host_bitmask(target_flags, fcntl_flags_tbl);
+    int i, ret;
+
+    if (!pathname) {
+        return -TARGET_EFAULT;
+    }
+
+    if (is_proc_myself(pathname, "exe")) {
+        ret = qemu_getauxval(AT_EXECFD);
+        if (ret == 0) {
+            ret = get_errno(safe_openat(dirfd, exec_path, flags, mode));
+        }
+        goto done;
+    }
+
+    for (i = 0; i < ARRAY_SIZE(fakes); ++i) {
+        if (fakes[i].cmp(pathname, fakes[i].filename)) {
+            const char *tmpdir;
+            char filename[PATH_MAX];
+            int r;
+
+            /* Create temporary file.  */
+            tmpdir = getenv("TMPDIR");
+            if (!tmpdir) {
+                tmpdir = "/tmp";
+            }
+            snprintf(filename, sizeof(filename),
+                     "%s/qemu-open.XXXXXX", tmpdir);
+            ret = mkstemp(filename);
+            if (ret < 0) {
+                ret = -TARGET_ENOENT;
+                goto done;
+            }
+            unlink(filename);
+
+            /* Add contents to the temporary file.  */
+            r = fakes[i].fill(cpu_env, ret);
+            if (r) {
+                close(ret);
+                ret = -TARGET_ENOENT;
+                goto done;
+            }
+
+            /* Reset pointer to the beginning.  */
+            lseek(ret, 0, SEEK_SET);
+            goto done;
+        }
+    }
+    ret = get_errno(safe_openat(dirfd, path(pathname), flags, mode));
+
+ done:
+    fd_trans_unregister(ret);
+    unlock_user(pathname, target_path, 0);
+    return ret;
+}
+
+SYSCALL_IMPL(close)
+{
+    fd_trans_unregister(arg1);
+    return get_errno(close(arg1));
+}
+SYSCALL_DEF(close, ARG_DEC);
+
+#ifdef TARGET_NR_open
+SYSCALL_IMPL(open)
+{
+    return do_openat(cpu_env, AT_FDCWD, arg1, arg2, arg3);
+}
+SYSCALL_DEF(open, ARG_STR, ARG_OPENFLAG, ARG_MODEFLAG);
+#endif
+
+SYSCALL_IMPL(openat)
+{
+    return do_openat(cpu_env, arg1, arg2, arg3, arg4);
+}
+SYSCALL_DEF(openat, ARG_ATDIRFD, ARG_STR, ARG_OPENFLAG, ARG_MODEFLAG);
+
+SYSCALL_IMPL(read)
+{
+    abi_long ret;
+    void *p;
+
+    if (arg3 == 0) {
+        return 0;
+    }
+    p = lock_user(VERIFY_WRITE, arg2, arg3, 0);
+    if (!p) {
+        return -TARGET_EFAULT;
+    }
+    ret = get_errno(safe_read(arg1, p, arg3));
+
+    if (!is_error(ret)) {
+        TargetFdDataFunc trans = fd_trans_host_to_target_data(arg1);
+        if (trans) {
+            ret = trans(p, ret);
+        }
+    }
+    unlock_user(p, arg2, ret);
+    return ret;
+}
+SYSCALL_DEF(read, ARG_DEC, ARG_PTR, ARG_DEC);
+
+static abi_long do_readlink_proc_exe(char *buf, abi_ulong bufsiz)
+{
+    char real[PATH_MAX];
+    char *temp = realpath(exec_path, real);
+    abi_long ret;
+
+    /* Return value is # of bytes that we wrote to the buffer. */
+    if (temp == NULL) {
+        return -host_to_target_errno(errno);
+    }
+    ret = MIN(strlen(real), bufsiz);
+    /* We cannot NUL terminate the string. */
+    memcpy(buf, real, ret);
+    return ret;
+}
+
+#ifdef TARGET_NR_readlink
+SYSCALL_IMPL(readlink)
+{
+    char *p = lock_user_string(arg1);
+    abi_ulong bufsiz = arg3;
+    void *buf = lock_user(VERIFY_WRITE, arg2, bufsiz, 0);
+    abi_long ret;
+
+    if (!p || !buf) {
+        ret = -TARGET_EFAULT;
+    } else if (!bufsiz) {
+        /* Short circuit this for the magic exe check. */
+        ret = -TARGET_EINVAL;
+    } else if (is_proc_myself(p, "exe")) {
+        ret = do_readlink_proc_exe(buf, bufsiz);
+    } else {
+        ret = get_errno(readlink(path(p), buf, bufsiz));
+    }
+    unlock_user(buf, arg2, ret);
+    unlock_user(p, arg1, 0);
+    return ret;
+}
+SYSCALL_DEF(readlink, ARG_STR, ARG_PTR, ARG_DEC);
+#endif
+
+#ifdef TARGET_NR_readlinkat
+SYSCALL_IMPL(readlinkat)
+{
+    char *p = lock_user_string(arg2);
+    abi_ulong bufsiz = arg4;
+    void *buf = lock_user(VERIFY_WRITE, arg3, bufsiz, 0);
+    abi_long ret;
+
+    if (!p || !buf) {
+        ret = -TARGET_EFAULT;
+    } else if (!bufsiz) {
+        /* Short circuit this for the magic exe check. */
+        ret = -TARGET_EINVAL;
+    } else if (is_proc_myself(p, "exe")) {
+        ret = do_readlink_proc_exe(buf, bufsiz);
+    } else {
+        ret = get_errno(readlinkat(arg1, path(p), buf, bufsiz));
+    }
+    unlock_user(buf, arg3, ret);
+    unlock_user(p, arg2, 0);
+    return ret;
+}
+SYSCALL_DEF(readlinkat, ARG_ATDIRFD, ARG_STR, ARG_PTR, ARG_DEC);
+#endif
+
+SYSCALL_IMPL(write)
+{
+    TargetFdDataFunc trans = fd_trans_target_to_host_data(arg1);
+    void *p = lock_user(VERIFY_READ, arg2, arg3, 1);
+    abi_long ret;
+
+    if (!p) {
+        return -TARGET_EFAULT;
+    }
+    if (trans) {
+        void *copy = g_malloc(arg3);
+        memcpy(copy, p, arg3);
+        ret = trans(copy, arg3);
+        if (ret >= 0) {
+            ret = get_errno(safe_write(arg1, copy, ret));
+        }
+        g_free(copy);
+    } else {
+        ret = get_errno(safe_write(arg1, p, arg3));
+    }
+    unlock_user(p, arg2, 0);
+    return ret;
+}
+SYSCALL_DEF(write, ARG_DEC, ARG_PTR, ARG_DEC);
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 3b59f7ed65..f757ae87b0 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -7589,267 +7589,6 @@  int host_to_target_waitstatus(int status)
     return status;
 }
 
-static int open_self_cmdline(void *cpu_env, int fd)
-{
-    CPUState *cpu = ENV_GET_CPU((CPUArchState *)cpu_env);
-    struct linux_binprm *bprm = ((TaskState *)cpu->opaque)->bprm;
-    int i;
-
-    for (i = 0; i < bprm->argc; i++) {
-        size_t len = strlen(bprm->argv[i]) + 1;
-
-        if (write(fd, bprm->argv[i], len) != len) {
-            return -1;
-        }
-    }
-
-    return 0;
-}
-
-static int open_self_maps(void *cpu_env, int fd)
-{
-    CPUState *cpu = ENV_GET_CPU((CPUArchState *)cpu_env);
-    TaskState *ts = cpu->opaque;
-    FILE *fp;
-    char *line = NULL;
-    size_t len = 0;
-    ssize_t read;
-
-    fp = fopen("/proc/self/maps", "r");
-    if (fp == NULL) {
-        return -1;
-    }
-
-    while ((read = getline(&line, &len, fp)) != -1) {
-        int fields, dev_maj, dev_min, inode;
-        uint64_t min, max, offset;
-        char flag_r, flag_w, flag_x, flag_p;
-        char path[512] = "";
-        fields = sscanf(line, "%"PRIx64"-%"PRIx64" %c%c%c%c %"PRIx64" %x:%x %d"
-                        " %512s", &min, &max, &flag_r, &flag_w, &flag_x,
-                        &flag_p, &offset, &dev_maj, &dev_min, &inode, path);
-
-        if ((fields < 10) || (fields > 11)) {
-            continue;
-        }
-        if (h2g_valid(min)) {
-            int flags = page_get_flags(h2g(min));
-            max = h2g_valid(max - 1) ? max : (uintptr_t)g2h(GUEST_ADDR_MAX) + 1;
-            if (page_check_range(h2g(min), max - min, flags) == -1) {
-                continue;
-            }
-            if (h2g(min) == ts->info->stack_limit) {
-                pstrcpy(path, sizeof(path), "      [stack]");
-            }
-            dprintf(fd, TARGET_ABI_FMT_lx "-" TARGET_ABI_FMT_lx
-                    " %c%c%c%c %08" PRIx64 " %02x:%02x %d %s%s\n",
-                    h2g(min), h2g(max - 1) + 1, flag_r, flag_w,
-                    flag_x, flag_p, offset, dev_maj, dev_min, inode,
-                    path[0] ? "         " : "", path);
-        }
-    }
-
-    free(line);
-    fclose(fp);
-
-    return 0;
-}
-
-static int open_self_stat(void *cpu_env, int fd)
-{
-    CPUState *cpu = ENV_GET_CPU((CPUArchState *)cpu_env);
-    TaskState *ts = cpu->opaque;
-    abi_ulong start_stack = ts->info->start_stack;
-    int i;
-
-    for (i = 0; i < 44; i++) {
-      char buf[128];
-      int len;
-      uint64_t val = 0;
-
-      if (i == 0) {
-        /* pid */
-        val = getpid();
-        snprintf(buf, sizeof(buf), "%"PRId64 " ", val);
-      } else if (i == 1) {
-        /* app name */
-        snprintf(buf, sizeof(buf), "(%s) ", ts->bprm->argv[0]);
-      } else if (i == 27) {
-        /* stack bottom */
-        val = start_stack;
-        snprintf(buf, sizeof(buf), "%"PRId64 " ", val);
-      } else {
-        /* for the rest, there is MasterCard */
-        snprintf(buf, sizeof(buf), "0%c", i == 43 ? '\n' : ' ');
-      }
-
-      len = strlen(buf);
-      if (write(fd, buf, len) != len) {
-          return -1;
-      }
-    }
-
-    return 0;
-}
-
-static int open_self_auxv(void *cpu_env, int fd)
-{
-    CPUState *cpu = ENV_GET_CPU((CPUArchState *)cpu_env);
-    TaskState *ts = cpu->opaque;
-    abi_ulong auxv = ts->info->saved_auxv;
-    abi_ulong len = ts->info->auxv_len;
-    char *ptr;
-
-    /*
-     * Auxiliary vector is stored in target process stack.
-     * read in whole auxv vector and copy it to file
-     */
-    ptr = lock_user(VERIFY_READ, auxv, len, 0);
-    if (ptr != NULL) {
-        while (len > 0) {
-            ssize_t r;
-            r = write(fd, ptr, len);
-            if (r <= 0) {
-                break;
-            }
-            len -= r;
-            ptr += r;
-        }
-        lseek(fd, 0, SEEK_SET);
-        unlock_user(ptr, auxv, len);
-    }
-
-    return 0;
-}
-
-static int is_proc_myself(const char *filename, const char *entry)
-{
-    if (!strncmp(filename, "/proc/", strlen("/proc/"))) {
-        filename += strlen("/proc/");
-        if (!strncmp(filename, "self/", strlen("self/"))) {
-            filename += strlen("self/");
-        } else if (*filename >= '1' && *filename <= '9') {
-            char myself[80];
-            snprintf(myself, sizeof(myself), "%d/", getpid());
-            if (!strncmp(filename, myself, strlen(myself))) {
-                filename += strlen(myself);
-            } else {
-                return 0;
-            }
-        } else {
-            return 0;
-        }
-        if (!strcmp(filename, entry)) {
-            return 1;
-        }
-    }
-    return 0;
-}
-
-#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
-static int is_proc(const char *filename, const char *entry)
-{
-    return strcmp(filename, entry) == 0;
-}
-
-static int open_net_route(void *cpu_env, int fd)
-{
-    FILE *fp;
-    char *line = NULL;
-    size_t len = 0;
-    ssize_t read;
-
-    fp = fopen("/proc/net/route", "r");
-    if (fp == NULL) {
-        return -1;
-    }
-
-    /* read header */
-
-    read = getline(&line, &len, fp);
-    dprintf(fd, "%s", line);
-
-    /* read routes */
-
-    while ((read = getline(&line, &len, fp)) != -1) {
-        char iface[16];
-        uint32_t dest, gw, mask;
-        unsigned int flags, refcnt, use, metric, mtu, window, irtt;
-        sscanf(line, "%s\t%08x\t%08x\t%04x\t%d\t%d\t%d\t%08x\t%d\t%u\t%u\n",
-                     iface, &dest, &gw, &flags, &refcnt, &use, &metric,
-                     &mask, &mtu, &window, &irtt);
-        dprintf(fd, "%s\t%08x\t%08x\t%04x\t%d\t%d\t%d\t%08x\t%d\t%u\t%u\n",
-                iface, tswap32(dest), tswap32(gw), flags, refcnt, use,
-                metric, tswap32(mask), mtu, window, irtt);
-    }
-
-    free(line);
-    fclose(fp);
-
-    return 0;
-}
-#endif
-
-static int do_openat(void *cpu_env, int dirfd, const char *pathname, int flags, mode_t mode)
-{
-    struct fake_open {
-        const char *filename;
-        int (*fill)(void *cpu_env, int fd);
-        int (*cmp)(const char *s1, const char *s2);
-    };
-    const struct fake_open *fake_open;
-    static const struct fake_open fakes[] = {
-        { "maps", open_self_maps, is_proc_myself },
-        { "stat", open_self_stat, is_proc_myself },
-        { "auxv", open_self_auxv, is_proc_myself },
-        { "cmdline", open_self_cmdline, is_proc_myself },
-#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
-        { "/proc/net/route", open_net_route, is_proc },
-#endif
-        { NULL, NULL, NULL }
-    };
-
-    if (is_proc_myself(pathname, "exe")) {
-        int execfd = qemu_getauxval(AT_EXECFD);
-        return execfd ? execfd : safe_openat(dirfd, exec_path, flags, mode);
-    }
-
-    for (fake_open = fakes; fake_open->filename; fake_open++) {
-        if (fake_open->cmp(pathname, fake_open->filename)) {
-            break;
-        }
-    }
-
-    if (fake_open->filename) {
-        const char *tmpdir;
-        char filename[PATH_MAX];
-        int fd, r;
-
-        /* create temporary file to map stat to */
-        tmpdir = getenv("TMPDIR");
-        if (!tmpdir)
-            tmpdir = "/tmp";
-        snprintf(filename, sizeof(filename), "%s/qemu-open.XXXXXX", tmpdir);
-        fd = mkstemp(filename);
-        if (fd < 0) {
-            return fd;
-        }
-        unlink(filename);
-
-        if ((r = fake_open->fill(cpu_env, fd))) {
-            int e = errno;
-            close(fd);
-            errno = e;
-            return r;
-        }
-        lseek(fd, 0, SEEK_SET);
-
-        return fd;
-    }
-
-    return safe_openat(dirfd, path(pathname), flags, mode);
-}
-
 #define TIMER_MAGIC 0x0caf0000
 #define TIMER_MAGIC_MASK 0xffff0000
 
@@ -8048,57 +7787,6 @@  static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
         preexit_cleanup(cpu_env, arg1);
         _exit(arg1);
         return 0; /* avoid warning */
-    case TARGET_NR_read:
-        if (arg3 == 0) {
-            return 0;
-        } else {
-            if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
-                return -TARGET_EFAULT;
-            ret = get_errno(safe_read(arg1, p, arg3));
-            if (ret >= 0 &&
-                fd_trans_host_to_target_data(arg1)) {
-                ret = fd_trans_host_to_target_data(arg1)(p, ret);
-            }
-            unlock_user(p, arg2, ret);
-        }
-        return ret;
-    case TARGET_NR_write:
-        if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
-            return -TARGET_EFAULT;
-        if (fd_trans_target_to_host_data(arg1)) {
-            void *copy = g_malloc(arg3);
-            memcpy(copy, p, arg3);
-            ret = fd_trans_target_to_host_data(arg1)(copy, arg3);
-            if (ret >= 0) {
-                ret = get_errno(safe_write(arg1, copy, ret));
-            }
-            g_free(copy);
-        } else {
-            ret = get_errno(safe_write(arg1, p, arg3));
-        }
-        unlock_user(p, arg2, 0);
-        return ret;
-
-#ifdef TARGET_NR_open
-    case TARGET_NR_open:
-        if (!(p = lock_user_string(arg1)))
-            return -TARGET_EFAULT;
-        ret = get_errno(do_openat(cpu_env, AT_FDCWD, p,
-                                  target_to_host_bitmask(arg2, fcntl_flags_tbl),
-                                  arg3));
-        fd_trans_unregister(ret);
-        unlock_user(p, arg1, 0);
-        return ret;
-#endif
-    case TARGET_NR_openat:
-        if (!(p = lock_user_string(arg2)))
-            return -TARGET_EFAULT;
-        ret = get_errno(do_openat(cpu_env, arg1, p,
-                                  target_to_host_bitmask(arg3, fcntl_flags_tbl),
-                                  arg4));
-        fd_trans_unregister(ret);
-        unlock_user(p, arg2, 0);
-        return ret;
 #if defined(TARGET_NR_name_to_handle_at) && defined(CONFIG_OPEN_BY_HANDLE)
     case TARGET_NR_name_to_handle_at:
         ret = do_name_to_handle_at(arg1, arg2, arg3, arg4, arg5);
@@ -8110,10 +7798,6 @@  static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
         fd_trans_unregister(ret);
         return ret;
 #endif
-    case TARGET_NR_close:
-        fd_trans_unregister(arg1);
-        return get_errno(close(arg1));
-
     case TARGET_NR_brk:
         return do_brk(arg1);
 #ifdef TARGET_NR_fork
@@ -9375,59 +9059,6 @@  static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
         }
         return ret;
 #endif
-#ifdef TARGET_NR_readlink
-    case TARGET_NR_readlink:
-        {
-            void *p2;
-            p = lock_user_string(arg1);
-            p2 = lock_user(VERIFY_WRITE, arg2, arg3, 0);
-            if (!p || !p2) {
-                ret = -TARGET_EFAULT;
-            } else if (!arg3) {
-                /* Short circuit this for the magic exe check. */
-                ret = -TARGET_EINVAL;
-            } else if (is_proc_myself((const char *)p, "exe")) {
-                char real[PATH_MAX], *temp;
-                temp = realpath(exec_path, real);
-                /* Return value is # of bytes that we wrote to the buffer. */
-                if (temp == NULL) {
-                    ret = get_errno(-1);
-                } else {
-                    /* Don't worry about sign mismatch as earlier mapping
-                     * logic would have thrown a bad address error. */
-                    ret = MIN(strlen(real), arg3);
-                    /* We cannot NUL terminate the string. */
-                    memcpy(p2, real, ret);
-                }
-            } else {
-                ret = get_errno(readlink(path(p), p2, arg3));
-            }
-            unlock_user(p2, arg2, ret);
-            unlock_user(p, arg1, 0);
-        }
-        return ret;
-#endif
-#if defined(TARGET_NR_readlinkat)
-    case TARGET_NR_readlinkat:
-        {
-            void *p2;
-            p  = lock_user_string(arg2);
-            p2 = lock_user(VERIFY_WRITE, arg3, arg4, 0);
-            if (!p || !p2) {
-                ret = -TARGET_EFAULT;
-            } else if (is_proc_myself((const char *)p, "exe")) {
-                char real[PATH_MAX], *temp;
-                temp = realpath(exec_path, real);
-                ret = temp == NULL ? get_errno(-1) : strlen(real) ;
-                snprintf((char *)p2, arg4, "%s", real);
-            } else {
-                ret = get_errno(readlinkat(arg1, path(p), p2, arg4));
-            }
-            unlock_user(p2, arg3, ret);
-            unlock_user(p, arg2, 0);
-        }
-        return ret;
-#endif
 #ifdef TARGET_NR_swapon
     case TARGET_NR_swapon:
         if (!(p = lock_user_string(arg1)))
@@ -12482,11 +12113,33 @@  static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
     return ret;
 }
 
+#include "syscall-file.inc.c"
+
 static const SyscallDef *syscall_table(int num)
 {
-#define S(NAME)  case TARGET_NR_##NAME: return &def_##NAME;
+#define S(NAME)  case TARGET_NR_##NAME: return &def_##NAME
 
     switch (num) {
+    /*
+     * Unconditional syscalls.
+     */
+    S(close);
+    S(openat);
+    S(read);
+    S(write);
+
+    /*
+     * Conditional syscalls.
+     */
+#ifdef TARGET_NR_open
+    S(open);
+#endif
+#ifdef TARGET_NR_readlink
+    S(readlink);
+#endif
+#ifdef TARGET_NR_readlinkat
+    S(readlinkat);
+#endif
     }
     return NULL;
 
diff --git a/linux-user/strace.list b/linux-user/strace.list
index ff8bb19f5f..f3a1b0fe31 100644
--- a/linux-user/strace.list
+++ b/linux-user/strace.list
@@ -97,9 +97,6 @@ 
 #ifdef TARGET_NR_clone
 { TARGET_NR_clone, "clone" , NULL, print_clone, NULL },
 #endif
-#ifdef TARGET_NR_close
-{ TARGET_NR_close, "close" , "%s(%d)", NULL, NULL },
-#endif
 #ifdef TARGET_NR_connect
 { TARGET_NR_connect, "connect" , "%s(%d,%#x,%d)", NULL, NULL },
 #endif
@@ -677,12 +674,6 @@ 
 #ifdef TARGET_NR_olduname
 { TARGET_NR_olduname, "olduname" , NULL, NULL, NULL },
 #endif
-#ifdef TARGET_NR_open
-{ TARGET_NR_open, "open" , NULL, print_open, NULL },
-#endif
-#ifdef TARGET_NR_openat
-{ TARGET_NR_openat, "openat" , NULL, print_openat, NULL },
-#endif
 #ifdef TARGET_NR_osf_adjtime
 { TARGET_NR_osf_adjtime, "osf_adjtime" , NULL, NULL, NULL },
 #endif
@@ -1076,21 +1067,12 @@ 
 #ifdef TARGET_NR_quotactl
 { TARGET_NR_quotactl, "quotactl" , NULL, NULL, NULL },
 #endif
-#ifdef TARGET_NR_read
-{ TARGET_NR_read, "read" , "%s(%d,%#x,%d)", NULL, NULL },
-#endif
 #ifdef TARGET_NR_readahead
 { TARGET_NR_readahead, "readahead" , NULL, NULL, NULL },
 #endif
 #ifdef TARGET_NR_readdir
 { TARGET_NR_readdir, "readdir" , NULL, NULL, NULL },
 #endif
-#ifdef TARGET_NR_readlink
-{ TARGET_NR_readlink, "readlink" , NULL, print_readlink, NULL },
-#endif
-#ifdef TARGET_NR_readlinkat
-{ TARGET_NR_readlinkat, "readlinkat" , NULL, print_readlinkat, NULL },
-#endif
 #ifdef TARGET_NR_readv
 { TARGET_NR_readv, "readv" , NULL, NULL, NULL },
 #endif
@@ -1626,9 +1608,6 @@ 
 #ifdef TARGET_NR_waitpid
 { TARGET_NR_waitpid, "waitpid" , "%s(%d,%p,%#x)", NULL, NULL },
 #endif
-#ifdef TARGET_NR_write
-{ TARGET_NR_write, "write" , "%s(%d,%#x,%d)", NULL, NULL },
-#endif
 #ifdef TARGET_NR_writev
 { TARGET_NR_writev, "writev" , "%s(%d,%p,%#x)", NULL, NULL },
 #endif