From patchwork Tue Jun 12 00:51:38 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Henderson X-Patchwork-Id: 138280 Delivered-To: patch@linaro.org Received: by 2002:a2e:970d:0:0:0:0:0 with SMTP id r13-v6csp4706857lji; Mon, 11 Jun 2018 18:04:22 -0700 (PDT) X-Google-Smtp-Source: ADUXVKLyW+nX7dGeq32jwMd204RyHa0tpi8MKEbIvciRG0veYdEv9LhquDOKU5tW+5dGJ7PdUO/0 X-Received: by 2002:a37:b8c4:: with SMTP id i187-v6mr1423557qkf.412.1528765461965; Mon, 11 Jun 2018 18:04:21 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1528765461; cv=none; d=google.com; s=arc-20160816; b=JjBtEZ4K0C6A0o7rrP1IckZ/cUPmssIFxS5U5Yfnp0Iu8qw2MNLGfhIBiIuHpuvuH1 2BhUSsgBac6/OsDPbP+5bjHz91tn19wktTiQy644XQGPS+KQF2VQoZKyjc33G4bmwx0X 0IjKnUbQcPK4ICxJEFFRuxxbEs/eTESn3RGXncv5lRyenaj6mFuH5T5YnOq4PKp/rpfw WNiwj965lodXLFOop3PU6M9wWMXptClcoBmSqAM+VVDt9cwRpH8d3tYGW3qRsAzV3PWD 55wGgPG2ig8F/pZSxJlE+zYZjuj/kf7LHzAApzMQZaV2o4kUFs8a2ecQkHR1wjs+GeQl rruA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:cc:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:subject:references:in-reply-to :message-id:date:to:from:dkim-signature:arc-authentication-results; bh=hX+TGhTD2p7o+3KWTnd17f+9q3W7+N7q89nDFWGJYTg=; b=oR3pNzOjuCoWX4pDHgH7Jp9CMap/iJkRqb0Sj3s3tf/d43514WNxewJJqVQkIN0L5E 779EO0jbn+4SYr8ayz7p0bMWvFcXHUIHPJuX3HBlj4dqWHFJjfPD+JTEBdaWLxmEFTlG 5Njb8X9nEFnSH/Y2HFFoT7nvp8Im8rkzv5Xmcn8JheE4GUl2b8gpswU2Hhei8Wj8No6l 5DtkVQzbYSnzkglvRwMKTmnz4vE9N2F8PcyXHoP31lu1qK+HdHMynhBVEELHFk1ZiRbI Br+vqtVx7R1JlTe+Z1tTm8u5c3JpYO5+4QFTFAQ/uJw20th/wCXWyqHLPlhh1XQmVLpU 4NOw== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@linaro.org header.s=google header.b=HouDKOrY; spf=pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 2001:4830:134:3::11 as permitted sender) smtp.mailfrom=qemu-devel-bounces+patch=linaro.org@nongnu.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from lists.gnu.org (lists.gnu.org. [2001:4830:134:3::11]) by mx.google.com with ESMTPS id g41-v6si4156517qtk.195.2018.06.11.18.04.21 for (version=TLS1 cipher=AES128-SHA bits=128/128); Mon, 11 Jun 2018 18:04:21 -0700 (PDT) Received-SPF: pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 2001:4830:134:3::11 as permitted sender) client-ip=2001:4830:134:3::11; Authentication-Results: mx.google.com; dkim=fail header.i=@linaro.org header.s=google header.b=HouDKOrY; spf=pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 2001:4830:134:3::11 as permitted sender) smtp.mailfrom=qemu-devel-bounces+patch=linaro.org@nongnu.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from localhost ([::1]:52075 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fSXjR-00069J-F2 for patch@linaro.org; Mon, 11 Jun 2018 21:04:21 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:59356) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fSXXr-0005IG-Oo for qemu-devel@nongnu.org; Mon, 11 Jun 2018 20:52:27 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fSXXo-0007cc-GL for qemu-devel@nongnu.org; Mon, 11 Jun 2018 20:52:23 -0400 Received: from mail-pg0-x241.google.com ([2607:f8b0:400e:c05::241]:34757) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1fSXXo-0007by-4z for qemu-devel@nongnu.org; Mon, 11 Jun 2018 20:52:20 -0400 Received: by mail-pg0-x241.google.com with SMTP id q4-v6so9531493pgr.1 for ; Mon, 11 Jun 2018 17:52:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=hX+TGhTD2p7o+3KWTnd17f+9q3W7+N7q89nDFWGJYTg=; b=HouDKOrYkyqaTaDmQn4gwvx5Bk5SlrkwEFlP9YT9A7x5QW6DlWjSX4ezl39MI97CQi fn1uNDbec3kW3wtzqyDBN9cvY5bChPY5RbYSQjko0bY2r4sZL4ez6vm4+VkOn9xrZWQ8 4gtyyrzMWng388o9tG9oDZ89CMnI047Mq1Kmo= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=hX+TGhTD2p7o+3KWTnd17f+9q3W7+N7q89nDFWGJYTg=; b=izRCpaWFiwlk61WyrULmFlCPT+vq6O0ZPzXJKWjhxlEeRrRnuYd7K/5/A8PKZY+MMD zgppa3zjiwFkW11JWCHpdQek8YEUrLBcL25SAm5PWoLxgyNw7AzeA04WY8KkgI7v3BBn serPc1ruSo4NMdOuyk5RbfSYAPVdxvdyrVEtn57FK3NjWWimM+wyFMJL2PGScd4BK7EJ F1GZT5bcrUZMiLKkOOBHY44IfqS5gfxd3AQZPpjky5o/DsfBouq2+zdcdqh6XrZzMLF3 +AvPrCigAqNAP41LFGLIjIAQ3oCmzQyk7SCuok4KXdYR87kO0vkGUeopFO0rQOoEU9TN Xe/g== X-Gm-Message-State: APt69E20G+VO4HKJ4rM3TiMENFjURbZo5ZMQL7cA/vK8sHUhbJpXXUnq 6V04mKOdTdAeySi5RqLOOqrKQda0m2A= X-Received: by 2002:a65:5b0a:: with SMTP id y10-v6mr1257250pgq.112.1528764738338; Mon, 11 Jun 2018 17:52:18 -0700 (PDT) Received: from cloudburst.twiddle.net (rrcs-173-198-77-219.west.biz.rr.com. [173.198.77.219]) by smtp.gmail.com with ESMTPSA id l26-v6sm43362802pgn.55.2018.06.11.17.52.16 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Mon, 11 Jun 2018 17:52:17 -0700 (PDT) From: Richard Henderson To: qemu-devel@nongnu.org Date: Mon, 11 Jun 2018 14:51:38 -1000 Message-Id: <20180612005145.3375-13-richard.henderson@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180612005145.3375-1-richard.henderson@linaro.org> References: <20180612005145.3375-1-richard.henderson@linaro.org> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2607:f8b0:400e:c05::241 Subject: [Qemu-devel] [PATCH v3 12/19] linux-user: Setup split syscall infrastructure X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: peter.maydell@linaro.org, laurent@vivier.eu Errors-To: qemu-devel-bounces+patch=linaro.org@nongnu.org Sender: "Qemu-devel" Defines a unified structure for implementation and strace. Supplies a generator script to build the declarations and the lookup function. Signed-off-by: Richard Henderson --- linux-user/syscall.h | 178 +++++++++++++++ linux-user/strace.c | 386 ++++++++++++++++++++++++--------- linux-user/syscall.c | 113 ++++------ linux-user/Makefile.objs | 10 + linux-user/gen_syscall_list.py | 82 +++++++ 5 files changed, 595 insertions(+), 174 deletions(-) create mode 100644 linux-user/syscall.h create mode 100644 linux-user/gen_syscall_list.py -- 2.17.1 diff --git a/linux-user/syscall.h b/linux-user/syscall.h new file mode 100644 index 0000000000..7eb078c3e5 --- /dev/null +++ b/linux-user/syscall.h @@ -0,0 +1,178 @@ +/* + * Linux syscalls internals + * Copyright (c) 2018 Linaro, Limited. + * + * 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 . + */ + +typedef struct SyscallDef SyscallDef; + +/* This hook extracts max 6 arguments from max 8 input registers. + * In the process, register pairs that store 64-bit arguments are merged. + * Finally, syscalls are demultipliexed; e.g. the hook for socketcall will + * return the SyscallDef for bind, listen, etc. In the process the hook + * may need to read from guest memory, or otherwise validate operands. + * On failure, set errno (to a host value) and return NULL; + * the (target adjusted) errno will be returned to the guest. + */ +typedef const SyscallDef *SyscallArgsFn(const SyscallDef *, int64_t out[6], + abi_long in[8]); + +/* This hook implements the syscall. */ +typedef abi_long SyscallImplFn(CPUArchState *, int64_t, int64_t, int64_t, + int64_t, int64_t, int64_t); + +/* This hook prints the arguments to the syscall for strace. */ +typedef void SyscallPrintFn(const SyscallDef *, int64_t arg[6]); + +/* This hook print the return value from the syscall for strace. */ +typedef void SyscallPrintRetFn(const SyscallDef *, abi_long); + +/* These flags describe the arguments for the generic fallback to + * SyscallPrintFn. ARG_NONE indicates that the argument is not present. + */ +typedef enum { + ARG_NONE = 0, + + /* These print as numbers of abi_long. */ + ARG_DEC, + ARG_HEX, + ARG_OCT, + + /* These print as sets of flags. */ + ARG_ATDIRFD, + ARG_MODEFLAG, + ARG_OPENFLAG, + + /* These are interpreted as pointers. */ + ARG_PTR, + ARG_STR, + ARG_BUF, + + /* For a 32-bit host, force printing as a 64-bit operand. */ +#if TARGET_ABI_BITS == 32 + ARG_DEC64, +#else + ARG_DEC64 = ARG_DEC, +#endif +} SyscallArgType; + +struct SyscallDef { + const char *name; + SyscallArgsFn *args; + SyscallImplFn *impl; + SyscallPrintFn *print; + SyscallPrintRetFn *print_ret; + SyscallArgType arg_type[6]; +}; + +void print_syscall_def(const SyscallDef *def, int64_t args[6]); +void print_syscall_def_ret(const SyscallDef *def, abi_long ret); +void print_syscall_ptr_ret(const SyscallDef *def, abi_long ret); + +/* Emit the signature for a SyscallArgsFn. */ +#define SYSCALL_ARGS(NAME) \ + static const SyscallDef *args_##NAME(const SyscallDef *def, \ + int64_t out[6], abi_long in[8]) + +/* Emit the signature for a SyscallImplFn. */ +#define SYSCALL_IMPL(NAME) \ + static abi_long impl_##NAME(CPUArchState *cpu_env, int64_t arg1, \ + int64_t arg2, int64_t arg3, int64_t arg4, \ + int64_t arg5, int64_t arg6) + +/* Emit the definition for a "simple" syscall. Such does not use + * SyscallArgsFn and only uses arg_type for strace. + */ +#define SYSCALL_DEF(NAME, ...) \ + const SyscallDef def_##NAME = { \ + .name = #NAME, .impl = impl_##NAME, .arg_type = { __VA_ARGS__ } \ + } + +/* Emit the definition for a syscall that also has an args hook, + * and uses arg_type for strace. + */ +#define SYSCALL_DEF_ARGS(NAME, ...) \ + const SyscallDef def_##NAME = { \ + .name = #NAME, .args = args_##NAME, .impl = impl_##NAME, \ + .arg_type = { __VA_ARGS__ } \ + } + +/* Declarations from the main syscall.c for use in syscall_foo.c, + * or for the moment, vice versa. + */ + +int host_to_target_errno(int err); + +static inline abi_long get_errno(abi_long ret) +{ + return unlikely(ret == -1) ? -host_to_target_errno(errno) : ret; +} + +static inline int is_error(abi_ulong ret) +{ + return ret >= -4096; +} + +/* Declarators for interruptable system calls. */ + +#define safe_syscall0(type, name) \ +static type safe_##name(void) \ +{ \ + return safe_syscall(__NR_##name); \ +} + +#define safe_syscall1(type, name, type1, arg1) \ +static type safe_##name(type1 arg1) \ +{ \ + return safe_syscall(__NR_##name, arg1); \ +} + +#define safe_syscall2(type, name, type1, arg1, type2, arg2) \ +static type safe_##name(type1 arg1, type2 arg2) \ +{ \ + return safe_syscall(__NR_##name, arg1, arg2); \ +} + +#define safe_syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ +static type safe_##name(type1 arg1, type2 arg2, type3 arg3) \ +{ \ + return safe_syscall(__NR_##name, arg1, arg2, arg3); \ +} + +#define safe_syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4) \ +static type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \ +{ \ + return safe_syscall(__NR_##name, arg1, arg2, arg3, arg4); \ +} + +#define safe_syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5) \ +static type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) \ +{ \ + return safe_syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5); \ +} + +#define safe_syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6) \ +static type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) \ +{ \ + return safe_syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5, arg6); \ +} + +/* Include declarations of syscall definitions. */ +#include "syscall_list.h" diff --git a/linux-user/strace.c b/linux-user/strace.c index bd897a3f20..6375feb747 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -10,6 +10,7 @@ #include #include #include "qemu.h" +#include "syscall.h" int do_strace=0; @@ -796,7 +797,7 @@ UNUSED static struct flags unlinkat_flags[] = { FLAG_END, }; -UNUSED static struct flags mode_flags[] = { +static struct flags const mode_flags[] = { FLAG_GENERIC(S_IFSOCK), FLAG_GENERIC(S_IFLNK), FLAG_GENERIC(S_IFREG), @@ -807,14 +808,14 @@ UNUSED static struct flags mode_flags[] = { FLAG_END, }; -UNUSED static struct flags open_access_flags[] = { +static struct flags const open_access_flags[] = { FLAG_TARGET(O_RDONLY), FLAG_TARGET(O_WRONLY), FLAG_TARGET(O_RDWR), FLAG_END, }; -UNUSED static struct flags open_flags[] = { +static struct flags const open_flags[] = { FLAG_TARGET(O_APPEND), FLAG_TARGET(O_CREAT), FLAG_TARGET(O_DIRECTORY), @@ -989,84 +990,86 @@ get_comma(int last) return ((last) ? "" : ","); } +static int add_flags(char *buf, int size, const struct flags *f, + int flags, bool octal) +{ + const char *sep = ""; + int off = 0; + + if (flags == 0 && f->f_value == 0) { + return snprintf(buf, size, "%s", f->f_string); + } + + for (; f->f_string != NULL; f++) { + if (f->f_value != 0 && (flags & f->f_value) == f->f_value) { + off += snprintf(buf + off, size - off, "%s%s", sep, f->f_string); + flags &= ~f->f_value; + sep = "|"; + } + } + + /* Print rest of the flags as numeric. */ + if (flags) { + if (octal) { + off += snprintf(buf + off, size - off, "%s%#o", sep, flags); + } else { + off += snprintf(buf + off, size - off, "%s%#x", sep, flags); + } + } + return off; +} + static void print_flags(const struct flags *f, abi_long flags, int last) { - const char *sep = ""; - int n; + char buf[256]; + add_flags(buf, sizeof(buf), f, flags, false); + gemu_log("%s%s", buf, get_comma(last)); +} - if ((flags == 0) && (f->f_value == 0)) { - gemu_log("%s%s", f->f_string, get_comma(last)); - return; - } - for (n = 0; f->f_string != NULL; f++) { - if ((f->f_value != 0) && ((flags & f->f_value) == f->f_value)) { - gemu_log("%s%s", sep, f->f_string); - flags &= ~f->f_value; - sep = "|"; - n++; - } - } - - if (n > 0) { - /* print rest of the flags as numeric */ - if (flags != 0) { - gemu_log("%s%#x%s", sep, (unsigned int)flags, get_comma(last)); - } else { - gemu_log("%s", get_comma(last)); - } +static int add_atdirfd(char *buf, int size, int fd) +{ + if (fd == AT_FDCWD) { + return snprintf(buf, size, "AT_FDCWD"); } else { - /* no string version of flags found, print them in hex then */ - gemu_log("%#x%s", (unsigned int)flags, get_comma(last)); + return snprintf(buf, size, "%d", fd); } } static void print_at_dirfd(abi_long dirfd, int last) { -#ifdef AT_FDCWD - if (dirfd == AT_FDCWD) { - gemu_log("AT_FDCWD%s", get_comma(last)); - return; - } -#endif - gemu_log("%d%s", (int)dirfd, get_comma(last)); + char buf[16]; + add_atdirfd(buf, sizeof(buf), dirfd); + gemu_log("%s%s", buf, get_comma(last)); } static void print_file_mode(abi_long mode, int last) { - const char *sep = ""; - const struct flags *m; + char buf[256]; + add_flags(buf, sizeof(buf), mode_flags, mode, true); + gemu_log("%s%s", buf, get_comma(last)); +} - for (m = &mode_flags[0]; m->f_string != NULL; m++) { - if ((m->f_value & mode) == m->f_value) { - gemu_log("%s%s", m->f_string, sep); - sep = "|"; - mode &= ~m->f_value; - break; - } +static int add_open_flags(char *buf, int size, int flags) +{ + int off = add_flags(buf, size, open_access_flags, + flags & TARGET_O_ACCMODE, false); + flags &= ~TARGET_O_ACCMODE; + if (flags == 0 || off + 2 >= size) { + return off; } - - mode &= ~S_IFMT; - /* print rest of the mode as octal */ - if (mode != 0) - gemu_log("%s%#o", sep, (unsigned int)mode); - - gemu_log("%s", get_comma(last)); + buf[off++] = '|'; + return off + add_flags(buf + off, size - off, open_flags, flags, true); } static void print_open_flags(abi_long flags, int last) { - print_flags(open_access_flags, flags & TARGET_O_ACCMODE, 1); - flags &= ~TARGET_O_ACCMODE; - if (flags == 0) { - gemu_log("%s", get_comma(last)); - return; - } - gemu_log("|"); - print_flags(open_flags, flags, last); + char buf[256]; + add_open_flags(buf, sizeof(buf), flags); + gemu_log("%s%s", buf, get_comma(last)); } static void @@ -1083,48 +1086,86 @@ print_syscall_epilogue(const struct syscallname *sc) gemu_log(")"); } -static void -print_string(abi_long addr, int last) +static int add_pointer(char *buf, int size, abi_ulong addr) { - char *s; - - if ((s = lock_user_string(addr)) != NULL) { - gemu_log("\"%s\"%s", s, get_comma(last)); - unlock_user(s, addr, 0); + if (addr) { + return snprintf(buf, size, "0x" TARGET_ABI_FMT_lx, addr); } else { - /* can't get string out of it, so print it as pointer */ - print_pointer(addr, last); + return snprintf(buf, size, "NULL"); } } +static int add_string(char *buf, int size, abi_ulong addr) +{ + char *s = lock_user_string(addr); + if (s) { + /* TODO: Escape special characters within the string. */ + /* TODO: Limit the string length for logging. */ + int len = snprintf(buf, size, "\"%s\"", s); + unlock_user(s, addr, 0); + return len; + } + return add_pointer(buf, size, addr); +} + +static void +print_string(abi_long addr, int last) +{ + char buf[256]; + add_string(buf, sizeof(buf), addr); + gemu_log("%s%s", buf, get_comma(last)); +} + #define MAX_PRINT_BUF 40 + +static int add_buffer(char *buf, int size, abi_long addr, abi_ulong len) +{ + unsigned char *p; + int off = 0; + abi_ulong i; + + p = lock_user(VERIFY_READ, addr, MIN(len, MAX_PRINT_BUF), 1); + if (!p) { + return add_pointer(buf, size, addr); + } + + buf[0] = '"'; + off = 1; + + for (i = 0; i < MAX_PRINT_BUF; ++i) { + int len; + + if (isprint(p[i])) { + buf[off] = p[i]; + len = 1; + } else { + len = snprintf(buf + off, size - off, "\\%o", p[i]); + } + off += len; + if (off + 2 >= size) { + goto overflow; + } + } + unlock_user(p, addr, 0); + + if (i == len && off + 2 < size) { + buf[off] = '"'; + buf[off + 1] = 0; + return off + 1; + } + + overflow: + off = MIN(off, size - 5); + strcpy(buf + off, "...\""); + return off + 4; +} + static void print_buf(abi_long addr, abi_long len, int last) { - uint8_t *s; - int i; - - s = lock_user(VERIFY_READ, addr, len, 1); - if (s) { - gemu_log("\""); - for (i = 0; i < MAX_PRINT_BUF && i < len; i++) { - if (isprint(s[i])) { - gemu_log("%c", s[i]); - } else { - gemu_log("\\%o", s[i]); - } - } - gemu_log("\""); - if (i != len) { - gemu_log("..."); - } - if (!last) { - gemu_log(","); - } - unlock_user(s, addr, 0); - } else { - print_pointer(addr, last); - } + char buf[256]; + add_buffer(buf, sizeof(buf), addr, len); + gemu_log("%s%s", buf, get_comma(last)); } /* @@ -1143,10 +1184,9 @@ print_raw_param(const char *fmt, abi_long param, int last) static void print_pointer(abi_long p, int last) { - if (p == 0) - gemu_log("NULL%s", get_comma(last)); - else - gemu_log("0x" TARGET_ABI_FMT_lx "%s", p, get_comma(last)); + char buf[24]; + add_pointer(buf, sizeof(buf), p); + gemu_log("%s%s", buf, get_comma(last)); } /* @@ -2623,32 +2663,168 @@ print_syscall(int num, gemu_log("Unknown syscall %d\n", num); } +static void print_syscall_def1(const SyscallDef *def, int64_t args[6]) +{ + char buf[1024], *b = buf; + int i, rest = sizeof(buf); + + /* Render the argument list into BUF. This allows us to log the + * entire syscall in one write statement at the end. + * While this is still not quite as good as separate files, a-la + * strace -ff, it can minimize confusion with a multithreaded guest. + */ + buf[0] = 0; + for (i = 0; i < 6; ++i) { + SyscallArgType type = def->arg_type[i]; + int64_t arg = args[i]; + int len; + + if (type == ARG_NONE) { + break; + } + + /* Validate remaining space. */ + if (rest < 4) { + goto overflow; + } + + /* Add separator. */ + if (i > 0) { + b[0] = ','; + b[1] = ' '; + b += 2; + rest -= 2; + } + + switch (type) { +#if TARGET_ABI_BITS == 32 + /* ??? We don't have TARGET_ABI_FMT_* macros for exactly + * what we want here. For this case it probably makes + * most sense to just special case. + */ + case ARG_DEC: + len = snprintf(b, rest, "%d", (int32_t)arg); + break; + case ARG_HEX: + len = snprintf(b, rest, "%#x", (uint32_t)arg); + break; + case ARG_OCT: + len = snprintf(b, rest, "%#o", (uint32_t)arg); + break; + case ARG_DEC64: + len = snprintf(b, rest, "%" PRId64, arg); + break; +#else + case ARG_DEC: + len = snprintf(b, rest, "%" PRId64, arg); + break; + case ARG_OCT: + len = snprintf(b, rest, "%" PRIo64, arg); + break; + case ARG_HEX: + len = snprintf(b, rest, "%" PRIx64, arg); + break; +#endif + case ARG_ATDIRFD: + len = add_atdirfd(b, rest, arg); + break; + case ARG_MODEFLAG: + len = add_flags(b, rest, mode_flags, arg, true); + break; + case ARG_OPENFLAG: + len = add_open_flags(b, rest, arg); + break; + case ARG_PTR: + len = add_pointer(b, rest, arg); + break; + case ARG_STR: + len = add_string(b, rest, arg); + break; + case ARG_BUF: + len = add_buffer(b, rest, arg, MAX_PRINT_BUF); + break; + default: + g_assert_not_reached(); + } + + b += len; + rest -= len; + if (rest == 0) { + goto overflow; + } + } + goto done; + + overflow: + strcpy(buf + sizeof(buf) - 4, "..."); + done: + gemu_log("%d %s(%s)", getpid(), def->name, buf); +} + +void print_syscall_def(const SyscallDef *def, int64_t args[6]) +{ + SyscallPrintFn *print = def->print; + if (!print) { + print = print_syscall_def1; + } + print(def, args); +} + +static void print_syscall_def_ret1(const SyscallDef *def, abi_long ret) +{ + if (is_error(ret)) { + const char *errstr = target_strerror(-ret); + if (errstr) { + gemu_log(" = -1 errno=" TARGET_ABI_FMT_ld " (%s)\n", + -ret, errstr); + } else { + gemu_log(" = -1 errno=" TARGET_ABI_FMT_ld "\n", -ret); + } + } else { + gemu_log(" = " TARGET_ABI_FMT_ld "\n", ret); + } +} void print_syscall_ret(int num, abi_long ret) { int i; - const char *errstr = NULL; for(i=0;iprint_ret; + if (!print) { + print = print_syscall_def_ret1; + } + print(def, ret); +} + void print_taken_signal(int target_signum, const target_siginfo_t *tinfo) { /* Print the strace output for a signal being taken: diff --git a/linux-user/syscall.c b/linux-user/syscall.c index fd8d48b0da..a96bbf9093 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -111,6 +111,7 @@ #include "uname.h" #include "qemu.h" +#include "syscall.h" #ifndef CLONE_IO #define CLONE_IO 0x80000000 /* Clone io context */ @@ -879,7 +880,7 @@ static uint16_t host_to_target_errno_table[ERRNO_TABLE_SIZE] = { #endif }; -static inline int host_to_target_errno(int err) +int host_to_target_errno(int err) { if (err >= 0 && err < ERRNO_TABLE_SIZE && host_to_target_errno_table[err]) { @@ -897,19 +898,6 @@ static inline int target_to_host_errno(int err) return err; } -static inline abi_long get_errno(abi_long ret) -{ - if (ret == -1) - return -host_to_target_errno(errno); - else - return ret; -} - -static inline int is_error(abi_long ret) -{ - return (abi_ulong)ret >= (abi_ulong)(-4096); -} - const char *target_strerror(int err) { if (err == TARGET_ERESTARTSYS) { @@ -925,53 +913,6 @@ const char *target_strerror(int err) return strerror(target_to_host_errno(err)); } -#define safe_syscall0(type, name) \ -static type safe_##name(void) \ -{ \ - return safe_syscall(__NR_##name); \ -} - -#define safe_syscall1(type, name, type1, arg1) \ -static type safe_##name(type1 arg1) \ -{ \ - return safe_syscall(__NR_##name, arg1); \ -} - -#define safe_syscall2(type, name, type1, arg1, type2, arg2) \ -static type safe_##name(type1 arg1, type2 arg2) \ -{ \ - return safe_syscall(__NR_##name, arg1, arg2); \ -} - -#define safe_syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ -static type safe_##name(type1 arg1, type2 arg2, type3 arg3) \ -{ \ - return safe_syscall(__NR_##name, arg1, arg2, arg3); \ -} - -#define safe_syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \ - type4, arg4) \ -static type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \ -{ \ - return safe_syscall(__NR_##name, arg1, arg2, arg3, arg4); \ -} - -#define safe_syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \ - type4, arg4, type5, arg5) \ -static type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ - type5 arg5) \ -{ \ - return safe_syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5); \ -} - -#define safe_syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \ - type4, arg4, type5, arg5, type6, arg6) \ -static type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ - type5 arg5, type6 arg6) \ -{ \ - return safe_syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5, arg6); \ -} - safe_syscall3(ssize_t, read, int, fd, void *, buff, size_t, count) safe_syscall3(ssize_t, write, int, fd, const void *, buff, size_t, count) safe_syscall4(int, openat, int, dirfd, const char *, pathname, \ @@ -12412,12 +12353,18 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, return ret; } +/* Include the generated syscall lookup function. */ +#include "syscall_list.inc.c" + abi_long do_syscall(void *cpu_env, int num, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6, abi_long arg7, abi_long arg8) { CPUState *cpu = ENV_GET_CPU(cpu_env); + const SyscallDef *def, *orig_def; + abi_long raw_args[8] = { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 }; + int64_t out_args[6] = { arg1, arg2, arg3, arg4, arg5, arg6 }; abi_long ret; #ifdef DEBUG_ERESTARTSYS @@ -12437,16 +12384,44 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, trace_guest_user_syscall(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); - if (unlikely(do_strace)) { - print_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); - ret = do_syscall1(cpu_env, num, arg1, arg2, arg3, arg4, - arg5, arg6, arg7, arg8); - print_syscall_ret(num, ret); - } else { - ret = do_syscall1(cpu_env, num, arg1, arg2, arg3, arg4, - arg5, arg6, arg7, arg8); + orig_def = def = syscall_table(num); + if (def == NULL) { + /* Unconverted. */ + if (unlikely(do_strace)) { + print_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); + ret = do_syscall1(cpu_env, num, arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8); + print_syscall_ret(num, ret); + } else { + ret = do_syscall1(cpu_env, num, arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8); + } + goto fini; } + if (def->args) { + def = def->args(def, out_args, raw_args); + if (unlikely(def == NULL)) { + ret = -host_to_target_errno(errno); + if (unlikely(do_strace)) { + print_syscall_def(orig_def, out_args); + print_syscall_def_ret(orig_def, ret); + } + goto fini; + } + } + + if (unlikely(do_strace)) { + print_syscall_def(def, out_args); + ret = def->impl(cpu_env, out_args[0], out_args[1], out_args[2], + out_args[3], out_args[4], out_args[5]); + print_syscall_def_ret(def, ret); + } else { + ret = def->impl(cpu_env, out_args[0], out_args[1], out_args[2], + out_args[3], out_args[4], out_args[5]); + } + + fini: trace_guest_user_syscall_ret(cpu, num, ret); return ret; } diff --git a/linux-user/Makefile.objs b/linux-user/Makefile.objs index 59a5c17354..afa69ed6d2 100644 --- a/linux-user/Makefile.objs +++ b/linux-user/Makefile.objs @@ -7,3 +7,13 @@ obj-$(TARGET_HAS_BFLT) += flatload.o obj-$(TARGET_I386) += vm86.o obj-$(TARGET_ARM) += arm/nwfpe/ obj-$(TARGET_M68K) += m68k-sim.o + +GEN_SYSCALL_LIST = $(SRC_PATH)/linux-user/gen_syscall_list.py +SYSCALL_LIST = linux-user/syscall_list.h linux-user/syscall_list.inc.c + +$(SYSCALL_LIST): $(GEN_SYSCALL_LIST) + $(call quiet-command,\ + $(PYTHON) $(GEN_SYSCALL_LIST) $@, "GEN", $(TARGET_DIR)$@) + +linux-user/syscall.o \ +linux-user/strace.o: $(SYSCALL_LIST) diff --git a/linux-user/gen_syscall_list.py b/linux-user/gen_syscall_list.py new file mode 100644 index 0000000000..2e0fc39100 --- /dev/null +++ b/linux-user/gen_syscall_list.py @@ -0,0 +1,82 @@ +# +# Linux syscall table generator +# Copyright (c) 2018 Linaro, Limited. +# +# 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 . +# + +from __future__ import print_function +import sys + +# These are sets of all syscalls that have been converted. +# The lists are in collation order with '_' ignored. + +# These syscalls are supported by all targets. +# Avoiding ifdefs for these can diagnose typos in $cpu/syscall_nr.h +unconditional_syscalls = [ +] + +# These syscalls are only supported by some target or abis. +conditional_syscalls = [ +] + + +def header(f): + # Do not ifdef the declarations -- their use may be complicated. + all = unconditional_syscalls + conditional_syscalls + all.sort() + for s in all: + print("extern const SyscallDef def_", s, ";", sep = '', file = f) + + +def def_syscall(s, f): + print(" case TARGET_NR_", s, ": return &def_", s, ";", + sep = '', file = f); + + +def source(f): + print("static const SyscallDef *syscall_table(int num)", + "{", + " switch (num) {", + sep = '\n', file = f) + + for s in unconditional_syscalls: + def_syscall(s, f) + for s in conditional_syscalls: + print("#ifdef TARGET_NR_", s, sep = '', file = f) + def_syscall(s, f) + print("#endif", file = f) + + print(" }", + " return NULL;", + "}", + sep = '\n', file = f); + + +def main(): + p = sys.argv[1] + f = open(p, "w") + + print("/* This file is autogenerated by gensyscall.py. */\n\n", + file = f) + + if p[len(p) - 1] == 'h': + header(f) + else: + source(f) + + f.close(); + + +main()