summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--NEWS4
-rwxr-xr-xbootstrap1
-rw-r--r--configure.ac8
-rw-r--r--debian/control3
-rw-r--r--debian/strace.install1
-rw-r--r--debian/strace.manpages1
-rw-r--r--strace.spec.in5
-rw-r--r--tools/Makefile.am9
-rw-r--r--tools/asinfo/Makefile.am58
-rw-r--r--tools/asinfo/README-arch37
-rw-r--r--tools/asinfo/arch_definitions.h66
-rw-r--r--tools/asinfo/arch_interface.c607
-rw-r--r--tools/asinfo/arch_interface.h144
-rw-r--r--tools/asinfo/asinfo.1.in264
-rw-r--r--tools/asinfo/asinfo.c312
-rw-r--r--tools/asinfo/dispatchers.c236
-rw-r--r--tools/asinfo/dispatchers.h29
-rw-r--r--tools/asinfo/error_interface.c91
-rw-r--r--tools/asinfo/error_interface.h76
-rwxr-xr-xtools/asinfo/gen_asinfo_files.sh153
-rw-r--r--tools/asinfo/request_msgs.h74
-rw-r--r--tools/asinfo/syscall_interface.c672
-rw-r--r--tools/asinfo/syscall_interface.h123
-rw-r--r--tools/asinfo/tests/Makefile.am45
-rw-r--r--tools/asinfo/tests/com_disp_wrong_keys.c42
-rwxr-xr-xtools/asinfo/tests/com_disp_wrong_keys.test32
-rw-r--r--tools/asinfo/tests/format_output.c17
-rwxr-xr-xtools/asinfo/tests/format_output.test8
-rw-r--r--tools/asinfo/tests/get_arch_abi.c174
-rwxr-xr-xtools/asinfo/tests/get_arch_abi.test7
-rw-r--r--tools/asinfo/tests/get_sname.c26
-rwxr-xr-xtools/asinfo/tests/get_sname.test12
-rw-r--r--tools/asinfo/tests/get_snum.c26
-rwxr-xr-xtools/asinfo/tests/get_snum.test12
-rw-r--r--tools/asinfo/tests/init.sh75
-rw-r--r--tools/asinfo/tests/list_arch.c47
-rwxr-xr-xtools/asinfo/tests/list_arch.test7
-rw-r--r--tools/asinfo/tests/multiarch_get_sname.c31
-rwxr-xr-xtools/asinfo/tests/multiarch_get_sname.test12
-rw-r--r--tools/asinfo/tests/multiarch_get_snum.c39
-rwxr-xr-xtools/asinfo/tests/multiarch_get_snum.test12
-rw-r--r--tools/asinfo/tests/ref_asinfo_output.h40
-rw-r--r--tools/asinfo/tests/set_abi.c9
-rwxr-xr-xtools/asinfo/tests/set_abi.test7
-rw-r--r--tools/asinfo/tests/set_arch.c11
-rwxr-xr-xtools/asinfo/tests/set_arch.test7
-rw-r--r--tools/asinfo/tests/set_mult_abi.c11
-rwxr-xr-xtools/asinfo/tests/set_mult_abi.test7
-rw-r--r--tools/asinfo/tests/set_mult_arch.c12
-rwxr-xr-xtools/asinfo/tests/set_mult_arch.test7
51 files changed, 3709 insertions, 2 deletions
diff --git a/Makefile.am b/Makefile.am
index 2521f043a..677dc8e9f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -15,7 +15,7 @@ endif
if HAVE_MX32_RUNTIME
TESTS_MX32 = tests-mx32
endif
-SUBDIRS = . tests $(TESTS_M32) $(TESTS_MX32)
+SUBDIRS = . tests $(TESTS_M32) $(TESTS_MX32) tools
bin_PROGRAMS = strace
man_MANS = strace.1 strace-log-merge.1
diff --git a/NEWS b/NEWS
index 6e6dda34c..a38a838a5 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,10 @@
Noteworthy changes in release ?.? (????-??-??)
==============================================
+* New tools
+ * Added asinfo tool that is purposed to provide information about
+ system calls and architectures.
+
* Changes in behaviour
* Messages about unknown tracees are now subject to the strace's quietness
setting (-q/--quiet).
diff --git a/bootstrap b/bootstrap
index 1476e9a68..6b8bc4e4e 100755
--- a/bootstrap
+++ b/bootstrap
@@ -11,6 +11,7 @@
./xlat/gen.sh
./tests/gen_pure_executables.sh
./tests/gen_tests.sh
+./tools/asinfo/gen_asinfo_files.sh
for m in m32 mx32; do
m_upper=$(echo $m|tr '[a-z]' '[A-Z]')
diff --git a/configure.ac b/configure.ac
index 7c4f9292f..4a3bb10b6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -18,6 +18,7 @@ AC_INIT([strace],
[https://strace.io])
m4_define([copyright_year], st_esyscmd_s([./copyright-year-gen .year]))
m4_define([manpage_date], st_esyscmd_s([./file-date-gen strace.1.in]))
+m4_define([asinfo_manpage_date], st_esyscmd_s([./file-date-gen tools/asinfo/asinfo.1.in]))
AC_COPYRIGHT([Copyright (c) 1999-]copyright_year[ The strace developers.])
AC_CONFIG_SRCDIR([strace.c])
AC_CONFIG_AUX_DIR([.])
@@ -46,6 +47,9 @@ AC_SUBST([COPYRIGHT_YEAR], [copyright_year])
AC_DEFINE([MANPAGE_DATE], "[manpage_date]", [Date])
AC_SUBST([MANPAGE_DATE], [manpage_date])
+AC_DEFINE([ASINFO_MANPAGE_DATE], "[asinfo_manpage_date]", [Date])
+AC_SUBST([ASINFO_MANPAGE_DATE], [asinfo_manpage_date])
+
AC_C_BIGENDIAN
dnl arch-specific default for --enable-gcc-Werror
@@ -1028,6 +1032,10 @@ AC_CONFIG_FILES([Makefile
tests-mx32/Makefile
strace.1
strace-log-merge.1
+ tools/asinfo/asinfo.1
+ tools/Makefile
+ tools/asinfo/Makefile
+ tools/asinfo/tests/Makefile
strace.spec
debian/changelog])
AC_OUTPUT
diff --git a/debian/control b/debian/control
index 0da25c4b9..b3c691caa 100644
--- a/debian/control
+++ b/debian/control
@@ -20,6 +20,9 @@ Description: System call tracer
System calls and signals are events that happen at the user/kernel
interface. A close examination of this boundary is very useful for bug
isolation, sanity checking and attempting to capture race conditions.
+ .
+ This package also contains an advanced system call information tool
+ (asinfo) which provides information about system calls and architectures.
Package: strace64
Architecture: i386 powerpc s390 sparc
diff --git a/debian/strace.install b/debian/strace.install
index 30b0a6b04..1e2401bbe 100644
--- a/debian/strace.install
+++ b/debian/strace.install
@@ -1,2 +1,3 @@
build/strace usr/bin
strace-log-merge usr/bin
+build/tools/asinfo/asinfo usr/bin
diff --git a/debian/strace.manpages b/debian/strace.manpages
index 9fb376b36..706650b8a 100644
--- a/debian/strace.manpages
+++ b/debian/strace.manpages
@@ -1,2 +1,3 @@
build/strace.1
build/strace-log-merge.1
+build/tools/asinfo/asinfo.1
diff --git a/strace.spec.in b/strace.spec.in
index 24ba84664..6d7dc4c12 100644
--- a/strace.spec.in
+++ b/strace.spec.in
@@ -44,7 +44,9 @@ The strace program intercepts and records the system calls called and
received by a running process. Strace can print a record of each
system call, its arguments and its return value. Strace is useful for
diagnosing problems and debugging, as well as for instructional
-purposes.
+purposes. Also this package contains an advanced system call
+information tool (asinfo) which provides information about system calls
+and architectures.
Install strace if you need a tool to track the system calls made and
received by a process.
@@ -97,6 +99,7 @@ echo 'END OF TEST SUITE INFORMATION'
%doc CREDITS ChangeLog.gz ChangeLog-CVS.gz COPYING NEWS README
%{_bindir}/strace
%{_bindir}/strace-log-merge
+%{_bindir}/asinfo
%{_mandir}/man1/*
%changelog
diff --git a/tools/Makefile.am b/tools/Makefile.am
new file mode 100644
index 000000000..16643e12c
--- /dev/null
+++ b/tools/Makefile.am
@@ -0,0 +1,9 @@
+# Automake input for strace tools.
+#
+# Copyright (c) 2017 Edgar A. Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
+# Copyright (c) 2017-2018 The strace developers.
+# All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+SUBDIRS = asinfo
diff --git a/tools/asinfo/Makefile.am b/tools/asinfo/Makefile.am
new file mode 100644
index 000000000..b38963878
--- /dev/null
+++ b/tools/asinfo/Makefile.am
@@ -0,0 +1,58 @@
+# Automake input for asinfo.
+#
+# Copyright (c) 2017 Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
+# Copyright (c) 2017-2018 The strace developers.
+# All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+SUBDIRS = . tests
+
+bin_PROGRAMS = asinfo
+man_MANS = asinfo.1
+
+OS = linux
+
+AUTOMAKE_OPTIONS = subdir-objects
+
+AM_CFLAGS = $(WARN_CFLAGS)
+AM_CPPFLAGS = -I$(builddir) \
+ -I$(top_builddir)/$(OS) \
+ -I$(top_srcdir)/$(OS) \
+ -I$(top_builddir) \
+ -I$(top_srcdir)
+
+include Makemodule.am
+
+asinfo_CPPFLAGS = $(AM_CPPFLAGS)
+asinfo_CFLAGS = $(AM_CFLAGS)
+
+asinfo_SOURCES = \
+ arch_definitions.h \
+ arch_interface.c \
+ $(ARCH_AUX_FILES) \
+ arch_interface.h \
+ asinfo.c \
+ ../../basic_filters.c \
+ dispatchers.c \
+ dispatchers.h \
+ error_interface.c \
+ error_interface.h \
+ ../../error_prints.c \
+ ../../error_prints.h \
+ ../../filter.h \
+ ../../macros.h \
+ ../../number_set.c \
+ ../../number_set.h \
+ request_msgs.h \
+ ../../string_to_uint.h \
+ ../../string_to_uint.c \
+ syscall_interface.c \
+ syscall_interface.h \
+ ../../sysent_shorthand_defs.h \
+ ../../sysent_shorthand_undefs.h \
+ ../../xmalloc.c \
+ ../../xmalloc.h \
+ #end of asinfo_SOURCES
+
+EXTRA_DIST = gen_asinfo_files.sh README-arch
diff --git a/tools/asinfo/README-arch b/tools/asinfo/README-arch
new file mode 100644
index 000000000..4ef760efb
--- /dev/null
+++ b/tools/asinfo/README-arch
@@ -0,0 +1,37 @@
+This file describes the storage format of arch_definitions.h
+
+Storage format:
+/* [ARCH_SPECIFIC_DEFINE],[ACONST1,ACONST2] */
+ARCH_DESC_DEFINE(ARCH_NAME,ARCH_ABI,PASS({COMPAT_PERS1,COMPAT_PERS2,...}),\
+PASS({ALIAS1,ALIAS2,...}))
+
+ARCH_SPECIFIC_DEFINE:
+One syscallent.h header can contain several set of system calls for each
+compatible mode. And specific set can be switched by passing particular
+definition.
+So ARCH_SPECIFIC_DEFINE allows to forward a given definition, where
+!ARCH_SPECIFIC_DEFINE forwards undefined.
+If it is not required, left empty.
+
+ACONST1,ACONST2:
+It could be used to store architecture specific constants and pass them to code.
+If is is not required each one must be set to zero.
+
+ARCH_NAME:
+The main name of architecture will be used to generate the personality
+constants.
+The personality constant is equal to ARCH_+\$ARCH_ABI+_+\$ARCH_NAME.
+
+ARCH_ABI:
+Application binary interface for a given architecture, i.e. 32bit, 64bit, oabi,
+eabi, o32 etc.
+
+COMPAT_PERS1,COMPAT_PERS2,...:
+Compatible mode for a given architecture. It should be one of personality
+constants.
+If is is not required, left empty.
+
+ALIAS1,ALIAS2,...:
+Other name of the same architecture, like x86 and i386. At least one alias
+must to be set as it shows the name of architecture while printing.
+If current ARCH_NAME is just the compatible ABI mode, left empty.
diff --git a/tools/asinfo/arch_definitions.h b/tools/asinfo/arch_definitions.h
new file mode 100644
index 000000000..b294d6f69
--- /dev/null
+++ b/tools/asinfo/arch_definitions.h
@@ -0,0 +1,66 @@
+/* [],[aarch64/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(aarch64, 64bit, PASS({ARCH_arm_eabi}), PASS({"aarch64", "arm64"}) ),
+/* [],[alpha/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(alpha, 64bit, PASS({}), PASS({"alpha"}) ),
+/* [],[arc/syscallent.h,linux/32/syscallent-time32.h],[0,0] */
+ARCH_DESC_DEFINE(arc, 32bit, PASS({}), PASS({"arc"}) ),
+/* [!__ARM_EABI__],[arm/syscallent.h],[ARM_FIRST_SHUFFLED_SYSCALL,ARM_LAST_SPECIAL_SYSCALL] */
+ARCH_DESC_DEFINE(arm, oabi, PASS({ARCH_arm_eabi}), PASS({"arm"}) ),
+/* [__ARM_EABI__],[arm/syscallent.h],[ARM_FIRST_SHUFFLED_SYSCALL,ARM_LAST_SPECIAL_SYSCALL] */
+ARCH_DESC_DEFINE(arm, eabi, PASS({}), PASS({}) ),
+/* [],[avr32/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(avr32, 32bit, PASS({}), PASS({"avr32"}) ),
+/* [],[bfin/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(blackfin, 32bit, PASS({}), PASS({"blackfin", "bfin"}) ),
+/* [],[ia64/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(ia64, 64bit, PASS({}), PASS({"ia64"}) ),
+/* [],[m68k/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(m68k, 32bit, PASS({}), PASS({"m68k"}) ),
+/* [],[metag/syscallent.h,linux/32/syscallent-time32.h],[0,0] */
+ARCH_DESC_DEFINE(metag, 32bit, PASS({}), PASS({"metag"}) ),
+/* [],[microblaze/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(microblaze, 32bit, PASS({}), PASS({"microblaze"}) ),
+/* [LINUX_MIPSN64],[dummy.h,mips/syscallent-compat.h,mips/syscallent-n64.h],[0,0] */
+ARCH_DESC_DEFINE(mips64, n64, PASS({ARCH_mips64_n32, ARCH_mips_o32}), PASS({"mips64", "mips64le"}) ),
+/* [LINUX_MIPSN32],[dummy.h,mips/syscallent-compat.h,mips/syscallent-n32.h],[0,0] */
+ARCH_DESC_DEFINE(mips64, n32, PASS({ARCH_mips_o32}), PASS({}) ),
+/* [LINUX_MIPSO32],[dummy.h,mips/syscallent-compat.h,mips/syscallent-o32.h],[0,0] */
+ARCH_DESC_DEFINE(mips, o32, PASS({}), PASS({"mips", "mipsle"}) ),
+/* [],[nios2/syscallent.h,linux/32/syscallent-time32.h],[0,0] */
+ARCH_DESC_DEFINE(nios2, 32bit, PASS({}), PASS({"nios2"}) ),
+/* [],[or1k/syscallent.h,linux/32/syscallent-time32.h],[0,0] */
+ARCH_DESC_DEFINE(openrisc, 32bit, PASS({}), PASS({"openrisc", "or1k"}) ),
+/* [],[hppa/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(hppa, 32bit, PASS({}), PASS({"parisc", "hppa"}) ),
+/* [],[powerpc/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(ppc, 32bit, PASS({}), PASS({"ppc", "ppcle", "powerpc"}) ),
+/* [],[powerpc64/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(ppc64, 64bit, PASS({ARCH_ppc_32bit}), PASS({"ppc64", "powerpc64"}) ),
+/* [],[powerpc64le/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(ppc64le, 64bit, PASS({}), PASS({"ppc64le", "powerpc64le"}) ),
+/* [],[riscv64/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(riscv64, 64bit, PASS({}), PASS({"riscv64"}) ),
+/* [],[s390/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(s390, 32bit, PASS({}), PASS({"s390"}) ),
+/* [],[s390x/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(s390x, 64bit, PASS({}), PASS({"s390x"}) ),
+/* [],[sh/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(sh, 32bit, PASS({}), PASS({"sh"}) ),
+/* [],[sh64/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(sh64, 64bit, PASS({}), PASS({"sh64"}) ),
+/* [],[sparc/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(sparc, 32bit, PASS({}), PASS({"sparc"}) ),
+/* [],[sparc64/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(sparc64, 64bit, PASS({ARCH_sparc_32bit}), PASS({"sparc64"}) ),
+/* [],[tile/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(tile, 64bit, PASS({ARCH_tile_32bit}), PASS({"tile", "tilegx"}) ),
+/* [],[tile/syscallent1.h,linux/32/syscallent-time32.h],[0,0] */
+ARCH_DESC_DEFINE(tile, 32bit, PASS({}), PASS({"tilepro"}) ),
+/* [],[x86_64/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(x86_64, 64bit, PASS({ARCH_x86_64_x32, ARCH_x86_32bit}), PASS({"x86_64", "amd64", "EM64T"}) ),
+/* [],[x86_64/syscallent2.h],[0,0] */
+ARCH_DESC_DEFINE(x86_64, x32, PASS({ARCH_x86_32bit}), PASS({}) ),
+/* [],[i386/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(x86, 32bit, PASS({}), PASS({"x86", "i386", "i486", "i586", "i686"}) ),
+/* [],[xtensa/syscallent.h],[0,0] */
+ARCH_DESC_DEFINE(xtensa, 32bit, PASS({}), PASS({"xtensa"}) )
diff --git a/tools/asinfo/arch_interface.c b/tools/asinfo/arch_interface.c
new file mode 100644
index 000000000..04c90be99
--- /dev/null
+++ b/tools/asinfo/arch_interface.c
@@ -0,0 +1,607 @@
+/*
+ * Copyright (c) 2017 Edgar A. Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
+ * Copyright (c) 2017-2018 The strace developers.
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "arch_interface.h"
+#include "defs.h"
+#include "macros.h"
+#include "xmalloc.h"
+
+/* Define these shorthand notations to simplify the syscallent files. */
+#include "sysent_shorthand_defs.h"
+
+/* For the current functionality there is no need
+ to use sen and (*sys_func)() fields in sysent struct */
+#define SEN(syscall_name) 0, NULL
+
+/* Generated file based on arch_definitions.h */
+#include "arch_includes.h"
+
+/* Now undef them since short defines cause wicked namespace pollution. */
+#include "sysent_shorthand_undefs.h"
+
+#define PASS(...) __VA_ARGS__
+#define ARCH_DESC_DEFINE(arch, mode, comp_pers, arch_aliases) \
+ [ARCH_##arch##_##mode] = { \
+ .pers = ARCH_##arch##_##mode, \
+ .arch_name = arch_aliases, \
+ .abi_mode = #mode, \
+ .abi_mode_len = ARRAY_SIZE(#arch) - 1, \
+ .compat_pers = comp_pers, \
+ .max_scn = ARRAY_SIZE(arch##_##mode##_sysent), \
+ .syscall_list = arch##_##mode##_sysent, \
+ .user_num1 = &arch##_##mode##_usr1, \
+ .user_num2 = &arch##_##mode##_usr2, \
+ }
+
+/* Generate array of arch_descriptors for each personality */
+const struct arch_descriptor architectures[] = {
+ #include "arch_definitions.h"
+};
+
+#undef ARCH_DESC_DEFINE
+#undef PASS
+
+struct arch_service *
+al_create(unsigned capacity)
+{
+ ARCH_LIST_DEFINE(as) = NULL;
+
+ if (!capacity)
+ return NULL;
+ as = xcalloc(sizeof(*as), 1);
+ as->arch_list = xcalloc(sizeof(*(as->arch_list)), capacity);
+ as->flag = xcalloc(sizeof(*(as->flag)), capacity);
+ as->in_aname = xcalloc(sizeof(*(as->in_aname)), capacity);
+ as->err = es_create();
+ as->capacity = capacity;
+ as->next_free = 0;
+ return as;
+}
+
+int
+al_push(struct arch_service *m, const struct arch_descriptor *element)
+{
+ if (m->next_free >= m->capacity)
+ return -1;
+ m->arch_list[m->next_free] = element;
+ m->flag[m->next_free] = AD_FLAG_EMPTY;
+ m->next_free++;
+ return 0;
+}
+
+static inline int
+al_is_index_ok(struct arch_service *m, unsigned index)
+{
+ if (index >= m->next_free)
+ return -1;
+ return 0;
+}
+
+int
+al_set_flag(struct arch_service *m, unsigned index, int flag)
+{
+ if (al_is_index_ok(m, index) == 0) {
+ m->flag[index] = flag;
+ return 0;
+ }
+ return -1;
+}
+
+int
+al_add_flag(struct arch_service *m, unsigned index, int flag)
+{
+ if (al_is_index_ok(m, index) == 0) {
+ m->flag[index] = m->flag[index] | flag;
+ return 0;
+ }
+ return -1;
+}
+
+int
+al_sub_flag(struct arch_service *m, unsigned index, int flag)
+{
+ if (al_is_index_ok(m, index) == 0) {
+ m->flag[index] = m->flag[index] & ~flag;
+ return 0;
+ }
+ return -1;
+}
+
+const struct arch_descriptor *
+al_get(struct arch_service *m, unsigned index)
+{
+ if (al_is_index_ok(m, index) != 0)
+ return NULL;
+ return m->arch_list[index];
+}
+
+unsigned int
+al_size(struct arch_service *m)
+{
+ return m->next_free;
+}
+
+void
+al_free(struct arch_service *m)
+{
+ int i;
+ int size = al_size(m);
+
+ for (i = 0; i < size; i++)
+ if (al_in_aname(m, i) != NULL)
+ free(al_in_aname(m, i));
+ free(m->arch_list);
+ free(m->flag);
+ free(m->in_aname);
+ es_free(m->err);
+ free(m);
+}
+
+struct error_service *al_err(struct arch_service *m)
+{
+ return m->err;
+}
+
+enum arch_pers
+al_pers(struct arch_service *m, unsigned index)
+{
+ const struct arch_descriptor *elem = al_get(m, index);
+
+ return (elem ? elem->pers : ARCH_no_pers);
+}
+
+const char **
+al_arch_name(struct arch_service *m, unsigned index)
+{
+ const struct arch_descriptor *elem = al_get(m, index);
+
+ return (elem ? (const char **)elem->arch_name : NULL);
+}
+
+enum arch_pers *
+al_cpers(struct arch_service *m, unsigned index)
+{
+ const struct arch_descriptor *elem = al_get(m, index);
+
+ return (elem ? (enum arch_pers *)elem->compat_pers : NULL);
+}
+
+const char *
+al_abi_mode(struct arch_service *m, unsigned index)
+{
+ const struct arch_descriptor *elem = al_get(m, index);
+
+ return (elem ? elem->abi_mode : NULL);
+}
+
+int
+al_abi_mode_len(struct arch_service *m, unsigned index)
+{
+ const struct arch_descriptor *elem = al_get(m, index);
+
+ return (elem ? elem->abi_mode_len : -1);
+}
+
+int
+al_flag(struct arch_service *m, unsigned index)
+{
+ int status = al_is_index_ok(m, index);
+
+ return (!status ? m->flag[index] : -1);
+}
+
+int
+al_set_in_aname(struct arch_service *m, unsigned index, char *aname)
+{
+ int status = al_is_index_ok(m, index);
+
+ if (status)
+ return -1;
+ m->in_aname[index] = aname;
+ return 0;
+}
+
+char *
+al_in_aname(struct arch_service *m, unsigned index)
+{
+ int status = al_is_index_ok(m, index);
+
+ return (!status ? m->in_aname[index] : NULL);
+}
+
+int
+al_psize(struct arch_service *m)
+{
+ int i;
+ int a_size = al_size(m);
+ int psize = 0;
+
+ for (i = 0; i < a_size; i++)
+ if (al_flag(m, i) & AD_FLAG_PRINT)
+ psize++;
+ return psize;
+}
+
+int
+al_arch_name_len(struct arch_service *m, unsigned index, int delim_len)
+{
+ const char **arch_name = NULL;
+ int i;
+ int final_len = 0;
+
+ while (!(al_flag(m, index) & AD_FLAG_MPERS))
+ index--;
+ arch_name = al_arch_name(m, index);
+ for (i = 0; (arch_name[i] != NULL) && (i < MAX_ALIASES); i++) {
+ final_len += strlen(arch_name[i]);
+ final_len += delim_len;
+ }
+ final_len -= delim_len;
+ return final_len;
+}
+
+int
+al_syscall_impl(struct arch_service *m, unsigned index)
+{
+ const struct arch_descriptor *elem = al_get(m, index);
+ int count = 0;
+
+ if (!elem)
+ return -1;
+ for (unsigned int i = 0; i < elem->max_scn; ++i) {
+ if (elem->syscall_list[i].sys_name &&
+ !(elem->syscall_list[i].sys_flags &
+ TRACE_INDIRECT_SUBCALL))
+ count++;
+ }
+ return count;
+}
+
+/* This method is purposed to count the supported ABI modes for the given
+ arch */
+int
+al_get_abi_modes(struct arch_service *m, unsigned index)
+{
+ const struct arch_descriptor *elem = al_get(m, index);
+ int i = 0;
+ int abi_count = 1;
+
+ if (!elem)
+ return -1;
+ for (i = 0; i < MAX_ALT_ABIS; i++)
+ if (elem->compat_pers[i] != ARCH_no_pers)
+ abi_count++;
+ return abi_count;
+}
+
+/* This method is purposed to find next one name of the same architecture.
+ For instance, x86_64 = amd64 */
+const char *
+al_next_alias(struct arch_service *m, unsigned index)
+{
+ static int next_alias = -1;
+ static const char **arch_name = NULL;
+ static unsigned lindex = 0;
+
+ if (lindex != index) {
+ lindex = index;
+ next_alias = -1;
+ }
+ if (al_pers(m, index) == ARCH_no_pers)
+ return NULL;
+ if (next_alias == -1) {
+ next_alias = 0;
+ while (!(al_flag(m, index) & AD_FLAG_MPERS))
+ index--;
+ arch_name = al_arch_name(m, index);
+ } else
+ next_alias++;
+ if (next_alias >= MAX_ALIASES || arch_name[next_alias] == NULL) {
+ next_alias = -1;
+ return NULL;
+ }
+ return arch_name[next_alias];
+}
+
+/* This method is purposed to return next one compat personality of the
+ same architecture */
+enum arch_pers
+al_next_cpers(struct arch_service *m, unsigned index)
+{
+ static int next_pers = -1;
+ enum arch_pers *a_pers = al_cpers(m, index);
+ static unsigned lindex = 0;
+
+ if (al_pers(m, index) == ARCH_no_pers)
+ return ARCH_no_pers;
+ if (lindex != index) {
+ lindex = index;
+ next_pers = -1;
+ }
+ if (next_pers == -1)
+ next_pers = 0;
+ else
+ next_pers++;
+ if (next_pers >= MAX_ALT_ABIS ||
+ a_pers[next_pers] == ARCH_no_pers) {
+ next_pers = -1;
+ return ARCH_no_pers;
+ }
+ return a_pers[next_pers];
+}
+
+static enum impl_type
+al_indirect_subcall(struct arch_service *m, unsigned index, const char *name)
+{
+ const struct arch_descriptor *elem = al_get(m, index);
+ unsigned int impl = IMPL_none;
+
+ for (unsigned int i = 0; i < elem->max_scn; ++i) {
+ if (!elem->syscall_list[i].sys_name)
+ continue;
+ if (!strcmp(elem->syscall_list[i].sys_name, name)) {
+ if (!(elem->syscall_list[i].sys_flags &
+ TRACE_INDIRECT_SUBCALL))
+ impl |= IMPL_ext;
+ else
+ impl |= IMPL_int;
+ }
+ }
+ return impl;
+}
+
+/* This method is purposed to create extended list of architectures */
+struct arch_service *
+al_create_filled(void)
+{
+ static const int architectures_size = ARRAY_SIZE(architectures) - 1;
+ ARCH_LIST_DEFINE(as) = al_create(architectures_size);
+ ARCH_LIST_DEFINE(f_as);
+ enum arch_pers cpers;
+ int esize = 0;
+ const char **arch_name = NULL;
+ int i;
+
+ /* Push and calculate size of extended table */
+ for (i = 0; i < architectures_size; i++) {
+ al_push(as, &(architectures[i + 1]));
+ arch_name = al_arch_name(as, i);
+ if (arch_name[0] != NULL)
+ esize += al_get_abi_modes(as, i);
+ }
+ f_as = al_create(esize);
+ /* Fill extended teble */
+ for (i = 0; i < architectures_size; i++) {
+ arch_name = al_arch_name(as, i);
+ if (arch_name[0] == NULL)
+ continue;
+ al_push(f_as, al_get(as, i));
+ al_add_flag(f_as, al_size(f_as) - 1, AD_FLAG_MPERS);
+ while ((cpers = al_next_cpers(as, i)) != ARCH_no_pers)
+ al_push(f_as, &(architectures[cpers]));
+ }
+ free(as);
+ return f_as;
+}
+
+/* To look up arch in arch_descriptor array */
+int
+al_mark_matches(struct arch_service *m, char *arch_str)
+{
+ int arch_match = -1;
+ char *match_pointer = NULL;
+ const char *a_name = NULL;
+ int al_size_full = al_size(m);
+ unsigned prev_arch_len = 0;
+ int i;
+ int a_abi;
+ char *in_aname;
+
+ if (arch_str == NULL)
+ return -1;
+ /* Here we find the best match for arch_str in architecture list.
+ Best match means here that we have to find the longest name of
+ architecture in a_full_list with arch_str substring, beginning
+ from the first letter */
+ for (i = 0; i < al_size_full; i++) {
+ if (!(al_flag(m, i) & AD_FLAG_MPERS))
+ continue;
+ while ((a_name = al_next_alias(m, i)) != NULL) {
+ match_pointer = strstr(arch_str, a_name);
+ if (match_pointer == NULL || match_pointer != arch_str)
+ continue;
+ if (arch_match == -1 ||
+ strlen(a_name) > prev_arch_len) {
+ prev_arch_len = strlen(a_name);
+ arch_match = i;
+ }
+ }
+ }
+ if (arch_match == -1)
+ return -1;
+ /* Now we find all ABI modes related to the architecture */
+ if ((a_abi = al_get_abi_modes(m, arch_match)) == -1)
+ return -1;
+ for (i = arch_match; i < (arch_match + a_abi); i++) {
+ al_add_flag(m, i, AD_FLAG_PRINT);
+ in_aname = xcalloc(sizeof(*in_aname), strlen(arch_str) + 1);
+ strcpy(in_aname, arch_str);
+ al_set_in_aname(m, i, in_aname);
+ }
+ return 0;
+}
+
+/* Join all architectures from 'f' and architectures with AD_FLAG_PRINT
+ from 's' arch_service structures */
+struct arch_service *
+al_join_print(struct arch_service *f, struct arch_service *s)
+{
+ int size1 = (f ? al_size(f) : 0);
+ int psize2 = al_psize(s);
+ int size2 = al_size(s);
+ int i;
+ int start_point = 0;
+ ARCH_LIST_DEFINE(final) = al_create(size1 + psize2);
+
+ for (i = 0; i < size2; i++)
+ if (al_flag(s, i) & AD_FLAG_PRINT) {
+ start_point = i;
+ break;
+ }
+ for (i = 0; i < size1; i++) {
+ al_push(final, al_get(f, i));
+ al_set_flag(final, i, al_flag(f, i));
+ al_set_in_aname(final, i, al_in_aname(f, i));
+ al_set_in_aname(f, i, NULL);
+ }
+ for (i = 0; i < psize2; i++) {
+ al_push(final, al_get(s, start_point + i));
+ al_set_flag(final, size1 + i , al_flag(s, start_point + i));
+ al_set_in_aname(final, size1 + i,
+ al_in_aname(s, start_point + i));
+ al_set_in_aname(s, start_point + i, NULL);
+ al_sub_flag(s, start_point + i, AD_FLAG_PRINT);
+ }
+ if (f)
+ al_free(f);
+ return final;
+}
+
+/* To avoid duplication of for(;;) construction */
+void
+al_unmark_all(struct arch_service *m, int flag)
+{
+ int a_size = al_size(m);
+ int i;
+
+ for (i = 0; i < a_size; i++)
+ al_sub_flag(m, i, flag);
+}
+
+/* Select one compatible personality in range of one architecture */
+int
+al_mark_pers4arch(struct arch_service *m, unsigned index, const char *abi_mode)
+{
+ unsigned i = index;
+
+ while (!(al_flag(m, i) & AD_FLAG_MPERS) || (i == index)) {
+ if (strcmp(abi_mode, "all") == 0) {
+ al_add_flag(m, i, AD_FLAG_PRINT);
+ i++;
+ if ((al_is_index_ok(m, i)) ||
+ (al_flag(m, i) & AD_FLAG_MPERS))
+ return 0;
+ else
+ continue;
+ }
+ if (strcmp(al_abi_mode(m, i), abi_mode) == 0) {
+ al_add_flag(m, i, AD_FLAG_PRINT);
+ return 0;
+ }
+ i++;
+ }
+ return -1;
+}
+
+void
+al_dump(struct arch_service *m, int is_raw)
+{
+ static const char *title[] = {
+ "N",
+ "Architecture name",
+ "ABI mode",
+ /* Implemented syscalls */
+ "IMPL syscalls",
+ /* IPC implementation */
+ "IPC IMPL",
+ /* SOCKET implementation */
+ "SOCKET IMPL"
+ };
+ int title_len[] = {
+ 0,
+ strlen(title[1]),
+ strlen(title[2]),
+ strlen(title[3]),
+ strlen(title[4]),
+ strlen(title[5]),
+ };
+ static const char *impl_st[] = {
+ "none",
+ "external",
+ "internal",
+ "int/ext"
+ };
+ static const char *delim = "/";
+ int i = 0;
+ int N = 0;
+ int temp_len = 0;
+ int arch_size = al_size(m);
+ int arch_psize = al_psize(m);
+ const char *next_alias = NULL;
+ char *whole_arch_name;
+
+ /* Calculate length of the column with the number of architectures */
+ for (i = 1; arch_psize/i != 0; i *= 10)
+ title_len[0]++;
+ for (i = 0; i < arch_size; i++) {
+ if (!(al_flag(m, i) & AD_FLAG_PRINT))
+ continue;
+ /* Calculate length of the column with the
+ architectures name */
+ temp_len = al_arch_name_len(m, i, strlen(delim));
+ if (temp_len > title_len[1])
+ title_len[1] = temp_len;
+ /* Calculate length of the column with the ABI mode */
+ if (al_abi_mode_len(m, i) > title_len[2])
+ title_len[2] = al_abi_mode_len(m, i);
+ }
+
+ whole_arch_name = xcalloc(title_len[1] + 1, sizeof(*whole_arch_name));
+ /* Output title */
+ if (!is_raw)
+ printf("| %*s | %*s | %*s | %*s | %*s | %*s |\n",
+ title_len[0], title[0], title_len[1], title[1],
+ title_len[2], title[2], title_len[3], title[3],
+ title_len[4], title[4], title_len[5], title[5]);
+ /* Output architectures */
+ for (i = 0; i < arch_size; i++) {
+ if (!(al_flag(m, i) & AD_FLAG_PRINT))
+ continue;
+ N++;
+ memset(whole_arch_name, 0, title_len[1]);
+ /* Put all the same arch back together */
+ next_alias = al_next_alias(m, i);
+ strcat(whole_arch_name, next_alias);
+ while ((next_alias = al_next_alias(m, i)) != NULL) {
+ strcat(whole_arch_name, delim);
+ strcat(whole_arch_name, next_alias);
+ }
+ if (is_raw) {
+ printf("%u;%s;%s;%d;%s;%s;\n", N, whole_arch_name,
+ al_abi_mode(m, i), al_syscall_impl(m, i),
+ impl_st[al_indirect_subcall(m, i, "semctl")],
+ impl_st[al_indirect_subcall(m, i, "socket")]);
+ continue;
+ }
+ printf("| %*u | ", title_len[0], N);
+ printf("%*s | ", title_len[1], whole_arch_name);
+ printf("%*s | ", title_len[2], al_abi_mode(m, i));
+ printf("%*d | ", title_len[3], al_syscall_impl(m, i));
+ printf("%*s | ", title_len[4], impl_st[al_indirect_subcall(m, i, "semctl")]);
+ printf("%*s |\n", title_len[5], impl_st[al_indirect_subcall(m, i, "socket")]);
+ }
+ free(whole_arch_name);
+}
diff --git a/tools/asinfo/arch_interface.h b/tools/asinfo/arch_interface.h
new file mode 100644
index 000000000..3281e9249
--- /dev/null
+++ b/tools/asinfo/arch_interface.h
@@ -0,0 +1,144 @@
+/*
+ * The arch_interface.h is purposed to interact with the basic data structure
+ * based on arch_descriptor struct. Mainly this set of methods are used by
+ * arch_dispatcher.
+ *
+ * Copyright (c) 2017 Edgar A. Kaziakhmedov <edgar.kaziakhmedv@virtuozzo.com>
+ * Copyright (c) 2017-2018 The strace developers.
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef ASINFO_ARCH_INTERFACE_H
+#define ASINFO_ARCH_INTERFACE_H
+
+#include "error_interface.h"
+#include "sysent.h"
+
+/* Type implementaion of syscall, internal means as a subcall,
+ external means a separate syscall, this enum is purposed for
+ well-known ipc and socket subcall group */
+enum impl_type {
+ IMPL_none = 0,
+ IMPL_ext = 1,
+ IMPL_int = 2,
+ IMPL_int_ext = IMPL_ext | IMPL_int
+};
+
+/* Names of personalities
+ * arch_pers = ARCH_ + kernel_kernel/other_name + abi_mode */
+enum arch_pers {
+ #include "arch_personalities.h"
+};
+
+#define MAX_ALIASES 6
+#define MAX_ALT_ABIS 3
+
+struct arch_descriptor {
+ enum arch_pers pers;
+ const char *arch_name[MAX_ALIASES];
+ const char *abi_mode;
+ const int abi_mode_len;
+ enum arch_pers compat_pers[MAX_ALT_ABIS];
+ const unsigned int max_scn;
+ const struct_sysent *syscall_list;
+ /* In the most cases these fields are purposed to store specific for
+ given arch constants, for instance, ARM_FIRST_SHUFFLED_SYSCALL */
+ const int *user_num1;
+ const int *user_num2;
+};
+
+#define AD_FLAG_EMPTY 0
+/* to hide some abi modes belonging to one architecture */
+#define AD_FLAG_PRINT (1 << 0)
+/* main personality, like x86_64 64bit */
+#define AD_FLAG_MPERS (1 << 1)
+
+/* To provide push-back interface with arch_list */
+struct arch_service {
+ /* immutable field */
+ const struct arch_descriptor **arch_list;
+ /* User flags for each arch_descriptor */
+ int *flag;
+ /* To support conformity between ABI and ARCH */
+ char **in_aname;
+ struct error_service *err;
+ unsigned capacity;
+ unsigned next_free;
+};
+
+#define ARCH_LIST_DEFINE(name) \
+ struct arch_service *(name)
+
+/* Push-back interface is purposed to simplify interaction with
+ arch_service struct
+ NOTE: al - architecture list */
+
+/* base methods */
+struct arch_service *al_create(unsigned capacity);
+
+int al_push(struct arch_service *m, const struct arch_descriptor *element);
+
+int al_set_flag(struct arch_service *m, unsigned index, int flag);
+
+int al_add_flag(struct arch_service *m, unsigned index, int flag);
+
+int al_sub_flag(struct arch_service *m, unsigned index, int flag);
+
+const struct arch_descriptor *al_get(struct arch_service *m, unsigned index);
+
+unsigned int al_size(struct arch_service *m);
+
+void al_free(struct arch_service *m);
+
+struct error_service *al_err(struct arch_service *m);
+
+/* methods returning fields with error check */
+enum arch_pers al_pers(struct arch_service *m, unsigned index);
+
+const char **al_arch_name(struct arch_service *m, unsigned index);
+
+enum arch_pers *al_cpers(struct arch_service *m, unsigned index);
+
+const char *al_abi_mode(struct arch_service *m, unsigned index);
+
+int al_abi_mode_len(struct arch_service *m, unsigned index);
+
+int al_flag(struct arch_service *m, unsigned index);
+
+int al_set_in_aname(struct arch_service *m, unsigned index, char *aname);
+
+char *al_in_aname(struct arch_service *m, unsigned index);
+
+/* calculating methods */
+int al_psize(struct arch_service *m);
+
+int al_arch_name_len(struct arch_service *m, unsigned index, int delim_len);
+
+int al_syscall_impl(struct arch_service *m, unsigned index);
+
+int al_get_abi_modes(struct arch_service *m, unsigned index);
+
+const char *al_next_alias(struct arch_service *m, unsigned index);
+
+enum arch_pers al_next_cpers(struct arch_service *m, unsigned index);
+
+enum impl_type al_ipc_syscall(struct arch_service *m, unsigned index);
+
+enum impl_type al_sck_syscall(struct arch_service *m, unsigned index);
+
+struct arch_service *al_create_filled(void);
+
+int al_mark_matches(struct arch_service *m, char *arch_str);
+
+struct arch_service *al_join_print(struct arch_service *f,
+ struct arch_service *s);
+
+void al_unmark_all(struct arch_service *m, int flag);
+
+int al_mark_pers4arch(struct arch_service *m, unsigned index,
+ const char *abi_mode);
+
+void al_dump(struct arch_service *m, int is_raw);
+
+#endif /* !ASINFO_ARCH_INTERFACE_H */
diff --git a/tools/asinfo/asinfo.1.in b/tools/asinfo/asinfo.1.in
new file mode 100644
index 000000000..63175710a
--- /dev/null
+++ b/tools/asinfo/asinfo.1.in
@@ -0,0 +1,264 @@
+.\" Copyright (c) 2017 Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
+.\" Copyright (c) 1996-2018 The strace developers.
+.\" All rights reserved.
+.\"
+.\" SPDX-License-Identifier: GPL-2.0-or-later
+
+.\" Required option.
+.de OR
+. ie \\n(.$-1 \
+. RI "\fB\\$1\fP" "\ \\$2"
+. el \
+. BR "\\$1"
+..
+.TH ASINFO 1 "@ASINFO_MANPAGE_DATE@" "strace package @VERSION@"
+.SH NAME
+asinfo \- Advanced System call INFOrmation tool
+.SH SYNOPSIS
+.SY asinfo
+.BR "" \fR[{
+.OR \-\-set\-arch arch
+.BR "" |
+.OR \-\-get\-arch
+.BR "" }
+.OR \fR[\fP\-\-set\-abi abi
+.BR "" |
+.OR \-\-list\-abi\fR]\fR]
+.BR "" {
+.BR "" {
+.OR \-\-get\-sname expr
+.BR "" |
+.OR \-\-get\-snum expr
+.BR "" }
+.OP \-\-nargs
+.BR "" }
+.OP "\-\-raw"
+.YS
+.SY asinfo
+.BR "" {
+.OR \-\-set\-arch arch
+.BR "" |
+.OR \-\-get\-arch
+.BR "" |
+.OR \-\-list\-arch
+.BR "" }
+.BD "" [
+.OR \fR[\fP\-\-set\-abi abi
+.BR "" |
+.OR \-\-list\-abi\fR]
+.OP "\-\-raw"
+
+.SH DESCRIPTION
+.B asinfo
+provides information about various aspects of system calls across architectures
+using
+.B strace
+knowledge base.
+
+In the simplest
+case it provides mapping from system call name to number and reverse.
+The main advantage of tool is it can work in single/multi arch modes with
+the opportunity to show discrepancies in system call characteristics.
+Also, the single arch mode allows program to take a guess about the
+current architecture and ABI, if they are not specified. Furthermore,
+.B asinfo
+provides convenient filtering for selecting system calls.
+
+.SH OPTIONS
+.SS "Architecture parameters"
+.TP 7
+.BI "\-\-set\-arch " arch
+Specify architecture/architectures manually. The format of the
+.I arch
+expression is:
+.RS 9
+.IP
+\fIarch1\/\fR[\fB,\fIarch2\/\fR]...
+.RE
+.IP
+.TP
+.B \-\-get\-arch
+Select achitecture based on the current machine.
+.TP
+.B \-\-list-arch
+Print out all supported architectures.
+Combined use with any ABI option is permitted.
+.SS "ABI parameters"
+.TP 7
+.BI "\-\-set\-abi " abi
+Specify ABI/ABIs manually. The format of the experession is:
+.RS 9
+.IP
+\fIabi1\/\fR[\fB,\fIabi2\/\fR]...
+.RE
+.IP
+.IP
+Note that ABI should be selected for each corresponding architecture.
+In addition, the special value
+.B all
+allows to choose all ABIs for the respective architecture.
+.TP
+.B "\-\-list\-abi "
+Select all ABIs for the chosen architecture/architectures.
+.IP
+If ABI parameters are not used and only single architecture is selected, tool
+will take a guess about ABI based on the strace package build.
+.SS "System call parameters"
+.TP 7
+.BI "\-\-get\-sname " expr
+Select system calls that satisfy a filtering expression
+.I
+expr
+and print out name of system calls and numbers for each architecture/ABI.
+.TP
+.BI "\-\-get\-snum " expr
+Select system calls that satisfy a filtering expression
+.I
+expr
+and print out number of system calls and names for each architecture/ABI.
+.TP
+.B \-\-nargs
+Switch the second output system call characteristic to number of arguments.
+.SS Output formatting
+.TP 7
+.B "\-\-raw"
+Reset alignment and remove titles, use ';' as a delimiter.
+.SS Miscellaneous
+.TP 7
+.B \-h
+Print the help summary.
+.TP
+.B \-v
+Print the version number.
+
+.SH "FILTERING EXPRESSION"
+A filtering expression is a pattern that describes a set of syscall names,
+syscall numbers, and syscall group. The format of the expression is:
+.RS 2
+.IP
+[\fB!\fR][\fB?\fR]\,\fIvalue1\/\fR[\fB,\fR[\fB?\fR]\,\fIvalue2\/\fR]...
+.RE
+.LP
+where
+.I value
+is a symbol or number. Using an exclamation mark negates the set of values.
+For example,
+.BR \fIvalue\fR = write
+means print strictly the write system call. By contrast,
+.BR \fIvalue\fR = !write
+means to dump every system call except write. Question mark before the
+syscall qualification allows suppression of error in case no syscalls matched
+the qualification provided, that can be particularly useful in multiarch mode,
+when system call is not presented in all selected architectures. In addition,
+the special values
+.B all
+and
+.B none
+have the obvious meanings.
+.LP
+Note that some shells use the exclamation point for history
+expansion even inside quoted arguments. If so, you must escape
+the exclamation point with a backslash.
+.SS "Strict match"
+.TP 7
+.B \fIvalue\fR=\,\fIset\fR
+Print out only the specified set of system calls. For example,
+.BR \fIvalue\fR = open,close,read,write
+means to only show those four system calls.
+.SS "Regex match"
+.TP 7
+.B \fIvalue\fR=/\,\fIregex\fR
+Show only those system calls that match the
+.IR regex .
+You can use
+.B POSIX
+Extended Regular Expression syntax (see
+.BR regex (7)).
+.SS "Class match"
+.TP 7
+.BR \fIvalue\fR = %file
+.TQ
+.BR \fIvalue\fR = file " (deprecated)"
+Show all system calls which take a file name as an argument. You
+can think of this as an abbreviation for
+.BR \fIvalue\fR = open,stat,chmod,unlink,...
+Furthermore, using the abbreviation will ensure that you don't
+accidentally forget to include a call like
+.B lstat
+in the list.
+.PP
+.BR \fIvalue\fR = %process
+.TQ
+.BR \fIvalue\fR = process " (deprecated)"
+Show all system calls which involve process management.
+.PP
+.BR \fIvalue\fR = %network
+.TQ
+.BR \fIvalue\fR = network " (deprecated)"
+Show all the network related system calls.
+.PP
+.BR \fIvalue\fR = %signal
+.TQ
+.BR \fIvalue\fR = signal " (deprecated)"
+Show all signal related system calls.
+.PP
+.BR \fIvalue\fR = %ipc
+.TQ
+.BR \fIvalue\fR = ipc " (deprecated)"
+Show all IPC related system calls.
+.PP
+.BR \fIvalue\fR = %desc
+.TQ
+.BR \fIvalue\fR = desc " (deprecated)"
+Show all file descriptor related system calls.
+.PP
+.BR \fIvalue\fR = %memory
+.TQ
+.BR \fIvalue\fR = memory " (deprecated)"
+Show all memory mapping related system calls.
+.TP
+.BR \fIvalue\fR = %stat
+Show stat syscall variants.
+.TP
+.BR \fIvalue\fR = %lstat
+Show lstat syscall variants.
+.TP
+.BR \fIvalue\fR = %fstat
+Show fstat and fstatat syscall variants.
+.TP
+.BR \fIvalue\fR = %%stat
+Show syscalls used for requesting file status (stat, lstat, fstat, fstatat,
+statx, and their variants).
+.TP
+.BR \fIvalue\fR = %statfs
+Show statfs, statfs64, statvfs, osf_statfs, and osf_statfs64 system calls.
+The same effect can be achieved with
+.BR \fIvalue\fR = /^(.*_)?statv?fs
+regular expression.
+.TP
+.BR \fIvalue\fR = %fstatfs
+Show fstatfs, fstatfs64, fstatvfs, osf_fstatfs, and osf_fstatfs64 system calls.
+The same effect can be achieved with
+.BR \fIvalue\fR = /fstatv?fs
+regular expression.
+.TP
+.BR \fIvalue\fR = %%statfs
+Show syscalls related to file system statistics (statfs-like, fstatfs-like,
+and ustat). The same effect can be achieved with
+.BR \fIvalue\fR = /statv?fs|fsstat|ustat
+regular expression.
+
+.SH "EXIT STATUS"
+On success,
+.B asinfo
+returns 0. Otherwise, in case of wrong input or no matches found, 1.
+
+.SH "REPORTING BUGS"
+Problems with
+.B asinfo
+should be reported to the
+.B strace
+mailing list at <strace-devel@lists.sourceforge.net>.
+
+.SH "SEE ALSO"
+.BR strace (1)
diff --git a/tools/asinfo/asinfo.c b/tools/asinfo/asinfo.c
new file mode 100644
index 000000000..44f11a3bf
--- /dev/null
+++ b/tools/asinfo/asinfo.c
@@ -0,0 +1,312 @@
+/*
+ * The asinfo main source. The asinfo tool is purposed to operate
+ * with system calls and provide information about it.
+ *
+ * Copyright (c) 2017 Edgar A. Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
+ * Copyright (c) 2017-2018 The strace developers.
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <errno.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "arch_interface.h"
+#include "dispatchers.h"
+#include "error_interface.h"
+#include "error_prints.h"
+#include "macros.h"
+#include "request_msgs.h"
+#include "syscall_interface.h"
+#include "xmalloc.h"
+
+#ifndef HAVE_PROGRAM_INVOCATION_NAME
+char *program_invocation_name;
+#endif
+
+static void
+usage(void)
+{
+ puts(
+ "usage: asinfo (--set-arch arch | --get-arch | --list-arch)\n"
+ " [--set-abi abi | --list-abi] [--raw]\n"
+ " or: asinfo [(--set-arch arch | --get-arch) [--set-abi abi | --list-abi]]\n"
+ " ((--get-sname expr | --get-snum expr) [--nargs]) [--raw]\n"
+ "\n"
+ "Architecture:\n"
+ " --set-arch arch use architecture ARCH for further work\n"
+ " argument format: arch1,arch2,...\n"
+ " --get-arch use architecture returned by uname for further work\n"
+ " --list-arch print out all architectures supported by strace\n"
+ " (combined use list-arch and any ABI option is permitted)\n"
+ "\n"
+ "ABI:\n"
+ " --set-abi abi use application binary interface ABI for further work\n"
+ " ('all' can be used as ABI to use all compatible personalities\n"
+ " for corresponding architecture)\n"
+ " argument format: abi1,abi2,...\n"
+ " --list-abi use all ABIs for specified architecture\n"
+ "\n"
+ "System call:\n"
+ " --get-sname expr print all system calls that satisfy a filtering expression:\n"
+ " [!]all or [!][?]val1[,[?]val2]...\n"
+ " with the following format:\n"
+ " | N | syscall name | snum1 | snum2 | ...\n"
+ " --get-snum expr print all system calls that satisfy a filtering expression:\n"
+ " [!]all or [!][?]val1[,[?]val2]...\n"
+ " with the following format:\n"
+ " | N | syscall number | sname1 | sname2 | ...\n"
+ " --nargs change output format as follows:\n"
+ " | N | syscall name or number | nargs1 | sname2 | ...\n"
+ "\n"
+ "Output formatting:\n"
+ " --raw reset alignment and remove titles, use ';' as a delimiter\n"
+ "\n"
+ "Miscellaneous:\n"
+ " -h print help message\n"
+ " -v print version");
+ exit(0);
+}
+
+static void
+print_version(void)
+{
+ printf("asinfo (%s package) -- version %s\n"
+ "Copyright (c) 1991-%s The strace developers <%s>.\n"
+ "This is free software; see the source for copying conditions. There is NO\n"
+ "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
+ PACKAGE_NAME, PACKAGE_VERSION, COPYRIGHT_YEAR, PACKAGE_URL);
+ exit(0);
+}
+
+void
+die(void)
+{
+ exit(1);
+}
+
+static int
+is_more1bit(unsigned int num)
+{
+ return !(num & (num - 1));
+}
+
+static unsigned
+strpar2req(char *option)
+{
+ /* Convertion table to store string with options */
+ static const char *options[] = {
+ [SD_REQ_GET_SNAME_BIT] = "--get-sname",
+ [SD_REQ_GET_SNUM_BIT] = "--get-snum",
+ [SD_REQ_NARGS_BIT] = "--nargs",
+ [AD_REQ_SET_ARCH_BIT] = "--set-arch",
+ [AD_REQ_GET_ARCH_BIT] = "--get-arch",
+ [AD_REQ_LIST_ARCH_BIT] = "--list-arch",
+ [ABD_REQ_SET_ABI_BIT] = "--set-abi",
+ [ABD_REQ_LIST_ABI_BIT] = "--list-abi",
+ [SERV_REQ_RAW_BIT] = "--raw",
+ [SERV_REQ_HELP_BIT] = "-h",
+ [SERV_REQ_VERSION_BIT] = "-v"
+ };
+ unsigned i;
+
+ for (i = 0; i < ARRAY_SIZE(options); i++) {
+ if (options[i] && strcmp(option, options[i]) == 0)
+ return i;
+ }
+ return SERV_REQ_ERROR_BIT;
+}
+
+static char **
+arg2list(char *argument)
+{
+ int i;
+ int len = strlen(argument);
+ int occur = 1;
+ char **arg_list;
+
+ for (i = 0; i < len; i++) {
+ if (argument[i] == ',') {
+ if (i == 0 || i == len - 1 || argument[i + 1] == ',')
+ return NULL;
+ occur++;
+ }
+ }
+ arg_list = xcalloc(sizeof(*arg_list), occur + 1);
+ for (i = 0; i < occur; i++) {
+ arg_list[i] = argument;
+ argument = strchr(argument, ',');
+ if (argument) {
+ *argument = '\0';
+ argument++;
+ }
+ }
+ return arg_list;
+}
+
+/* The purpose of this function is to convert input parameters to number with
+ set bits, where each bit means specific work mode. Moreover, it checks input
+ for correctness and outputs error messages in case of wrong input */
+static unsigned
+command_dispatcher(int argc, char *argv[], char **args[])
+{
+ int i;
+ unsigned final_req = 0;
+ unsigned temp_req = 0;
+ int mult_arch = 0;
+ int mult_abi = 0;
+ unsigned non_req_arg = AD_REQ_GET_ARCH | AD_REQ_LIST_ARCH |
+ ABD_REQ_LIST_ABI | SD_REQ_NARGS |
+ SERV_REQ_RAW;
+
+ if (!program_invocation_name || !*program_invocation_name) {
+ static char name[] = "asinfo";
+ program_invocation_name =
+ (argv[0] && *argv[0]) ? argv[0] : name;
+ }
+
+ /* Try to find help or version parameter first */
+ for (i = 1; i < argc; i++) {
+ if (strpar2req(argv[i]) == SERV_REQ_HELP_BIT)
+ usage();
+ if (strpar2req(argv[i]) == SERV_REQ_VERSION_BIT)
+ print_version();
+ }
+ /* For now, is is necessary to convert string parameter to number of
+ request and make basic check */
+ for (i = 1; i < argc; i++) {
+ if ((temp_req = strpar2req(argv[i])) == SERV_REQ_ERROR_BIT)
+ error_msg_and_help("unrecognized option '%s'",
+ argv[i]);
+ if (final_req & 1 << temp_req)
+ error_msg_and_help("parameter '%s' has been used "
+ "more than once", argv[i]);
+ if (!((1 << temp_req) & non_req_arg) &&
+ (i + 1 >= argc || strlen(argv[i + 1]) == 0 ||
+ strpar2req(argv[i + 1]) != SERV_REQ_ERROR_BIT))
+ error_msg_and_help("parameter '%s' requires "
+ "argument", argv[i]);
+ final_req |= 1 << temp_req;
+ if (!((1 << temp_req) & non_req_arg)) {
+ if ((1 << temp_req) & SD_REQ_MASK) {
+ args[temp_req] = &argv[i + 1];
+ i++;
+ continue;
+ }
+ if ((args[temp_req] = arg2list(argv[i + 1])) != NULL) {
+ i++;
+ continue;
+ }
+ error_msg_and_help("argument '%s' of '%s' parameter "
+ "has a wrong format",
+ argv[i + 1], argv[i]);
+ }
+ }
+ /* Count our multuarchness */
+ if (args[AD_REQ_SET_ARCH_BIT])
+ while (args[AD_REQ_SET_ARCH_BIT][mult_arch] != NULL)
+ mult_arch++;
+ if (args[ABD_REQ_SET_ABI_BIT])
+ while (args[ABD_REQ_SET_ABI_BIT][mult_abi] != NULL)
+ mult_abi++;
+ /* final_req should be logically checked */
+ /* More than one option from one request group couldn't be set */
+ if ((is_more1bit(final_req & SD_REQ_MASK & ~SD_REQ_NARGS) == 0) ||
+ (is_more1bit(final_req & AD_REQ_MASK) == 0) ||
+ (is_more1bit(final_req & ABD_REQ_MASK) == 0))
+ error_msg_and_help("exclusive parameters");
+ /* Check on mutually exclusive options chain */
+ /* If at least one syscall option has been typed, therefore
+ arch_options couldn't be list-arch and
+ abi_option couldn't be list-abi */
+ if ((final_req & SD_REQ_MASK) &&
+ (((final_req & AD_REQ_MASK) && (final_req & AD_REQ_LIST_ARCH))))
+ error_msg_and_help("wrong parameters");
+ /* list-arch couldn't be used with any abi options */
+ if ((final_req & AD_REQ_LIST_ARCH) &&
+ (final_req & ABD_REQ_MASK))
+ error_msg_and_help("'--list-arch' cannot be used with any "
+ "ABI parameters");
+ /* ABI requests could be used just in a combination with arch
+ requests */
+ if ((final_req & ABD_REQ_MASK) &&
+ !(final_req & AD_REQ_MASK))
+ error_msg_and_help("ABI parameters could be used only with "
+ "architecture parameter");
+ /* set-abi must be used in case of multiple arch */
+ if ((mult_arch > 1) && !(final_req & ABD_REQ_MASK))
+ error_msg_and_help("ABI modes cannot be automatically "
+ "detected for multiple "
+ "architectures");
+ /* set-abi and set-arch have to take the same number of args */
+ if ((final_req & AD_REQ_SET_ARCH) && (final_req & ABD_REQ_SET_ABI) &&
+ (mult_arch != mult_abi))
+ error_msg_and_help("each architecture needs respective "
+ "ABI mode, and vice versa");
+ /* --nargs cannot be used alone */
+ if ((final_req & SD_REQ_NARGS) &&
+ !(final_req & SD_REQ_MASK & ~SD_REQ_NARGS))
+ error_msg_and_help("first set main output syscall "
+ "characteristics");
+ /* raw should not be single */
+ if (final_req == SERV_REQ_RAW)
+ error_msg_and_help("raw data implies existing data");
+ return final_req;
+}
+
+static char *
+seek_sc_arg(char **input_args[])
+{
+ int i;
+
+ for (i = SD_REQ_GET_SNAME_BIT; i < SYSCALL_REQ_BIT_LAST; i++)
+ if (input_args[i] != NULL)
+ return input_args[i][0];
+ return NULL;
+}
+
+int
+main(int argc, char *argv[])
+{
+ ARCH_LIST_DEFINE(arch_list);
+ SYSCALL_LIST_DEFINE(sc_list);
+ /* This array is purposed to store arguments for options in the
+ most convenient way */
+ char ***in_args = xcalloc(sizeof(*in_args), REQ_LAST_BIT);
+ unsigned reqs;
+
+ /* command_dispatcher turn */
+ reqs = command_dispatcher(argc, argv, in_args);
+ if (reqs == 0)
+ error_msg_and_help("must have OPTIONS");
+
+ /* arch_dispatcher turn */
+ arch_list = arch_dispatcher(reqs, in_args[AD_REQ_SET_ARCH_BIT]);
+ if (es_error(al_err(arch_list)))
+ perror_msg_and_die("%s", es_get_serror(al_err(arch_list)));
+ /* abi_dispatcher turn */
+ abi_dispatcher(arch_list, reqs, in_args[ABD_REQ_SET_ABI_BIT]);
+ if (es_error(al_err(arch_list)))
+ perror_msg_and_die("%s", es_get_serror(al_err(arch_list)));
+ /* syscall_dispatcher turn */
+ sc_list = syscall_dispatcher(arch_list, reqs, seek_sc_arg(in_args));
+ if (es_error(ss_err(sc_list)))
+ perror_msg_and_die("%s", es_get_serror(ss_err(sc_list)));
+ /* If we want to get info about only architectures thus we print out
+ architectures, otherwise system calls */
+ if (!(reqs & SD_REQ_MASK))
+ al_dump(arch_list, reqs & SERV_REQ_RAW);
+ else
+ ss_dump(sc_list, reqs & SERV_REQ_RAW);
+ return 0;
+}
diff --git a/tools/asinfo/dispatchers.c b/tools/asinfo/dispatchers.c
new file mode 100644
index 000000000..736bcd40a
--- /dev/null
+++ b/tools/asinfo/dispatchers.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2017 Edgar A. Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
+ * Copyright (c) 2017-2018 The strace developers.
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/utsname.h>
+
+#include "arch_interface.h"
+#include "dispatchers.h"
+#include "macros.h"
+#include "request_msgs.h"
+#include "syscall_interface.h"
+#include "sysent.h"
+#include "xmalloc.h"
+
+struct arch_service *
+arch_dispatcher(unsigned request_type, char *arch[])
+{
+ struct utsname info_uname;
+ int i;
+ ARCH_LIST_DEFINE(arch_list) = al_create_filled();
+ ARCH_LIST_DEFINE(arch_final) = NULL;
+
+ /* If user don't type any option in ARCH_REQ group, it means
+ get current arch */
+ if ((request_type & AD_REQ_GET_ARCH) ||
+ (!(request_type & AD_REQ_MASK))) {
+ uname(&info_uname);
+ if (al_mark_matches(arch_list, info_uname.machine) == -1) {
+ es_set_error(al_err(arch_list), AD_UNSUP_ARCH);
+ es_set_option(al_err(arch_list), info_uname.machine,
+ NULL, NULL);
+ goto fail;
+ }
+ /* Cut off useless archs */
+ arch_final = al_join_print(arch_final, arch_list);
+ al_unmark_all(arch_final, AD_FLAG_PRINT);
+ free(arch_list);
+ goto done;
+ }
+
+ if (request_type & AD_REQ_SET_ARCH) {
+ for (i = 0; arch[i] != NULL; i++) {
+ if (al_mark_matches(arch_list, arch[i]) == -1) {
+ es_set_error(al_err(arch_list), AD_UNSUP_ARCH);
+ es_set_option(al_err(arch_list), arch[i],
+ NULL, NULL);
+ goto fail;
+ }
+ arch_final = al_join_print(arch_final, arch_list);
+ }
+ al_unmark_all(arch_final, AD_FLAG_PRINT);
+ al_free(arch_list);
+ goto done;
+ }
+
+ if ((request_type & AD_REQ_LIST_ARCH)) {
+ int a_size = al_size(arch_list);
+ for (i = 0; i < a_size; i++) {
+ al_add_flag(arch_list, i, AD_FLAG_PRINT);
+ }
+ arch_final = arch_list;
+ goto done;
+ }
+fail:
+ return arch_list;
+done:
+ return arch_final;
+}
+
+int
+abi_dispatcher(struct arch_service *a_serv, unsigned request_type,
+ char *abi[])
+{
+ int i = 0;
+ enum arch_pers pers;
+ int arch_size = 0;
+ int a_pos = 0;
+
+ arch_size = al_size(a_serv);
+ /* The strace package could be compiled as 32bit app on 64bit
+ architecture, therefore asinfo has to detect it and print out
+ corresponding personality. Frankly speaking, it is necessary to
+ detect strace package personality when it is possible */
+ if (!(request_type & ABD_REQ_MASK) &&
+ !(request_type & AD_REQ_LIST_ARCH)) {
+ pers = al_pers(a_serv, a_pos);
+ switch (pers) {
+#if defined(MIPS)
+ case ARCH_mips_o32:
+ case ARCH_mips64_n64:
+ al_mark_pers4arch(a_serv, a_pos,
+#if defined(LINUX_MIPSO32)
+ "o32"
+#elif defined(LINUX_MIPSN32)
+ "n32"
+#elif defined(LINUX_MIPSN64)
+ "n64"
+#endif
+ );
+ break;
+#endif
+#if defined(ARM)
+ case ARCH_arm_oabi:
+ al_mark_pers4arch(a_serv, a_pos,
+#if defined(__ARM_EABI__) || !defined(ENABLE_ARM_OABI)
+ "eabi"
+#else
+ "oabi"
+#endif
+ );
+ break;
+#endif
+#if defined(AARCH64)
+ case ARCH_aarch64_64bit:
+ al_mark_pers4arch(a_serv, a_pos,
+#if defined(__ARM_EABI__)
+ "eabi"
+#else
+ "64bit"
+#endif
+ );
+ break;
+#endif
+#if defined(X86_64) || defined(X32)
+ case ARCH_x86_64_64bit:
+ al_mark_pers4arch(a_serv, a_pos,
+#if defined(X86_64)
+ "64bit"
+#elif defined(X32)
+ "x32"
+#endif
+ );
+ break;
+#endif
+/* Especially for x86_64 32bit ABI, because configure.ac defines it
+ as I386 arch */
+#if defined(I386)
+ case ARCH_x86_64_64bit:
+ al_mark_pers4arch(a_serv, a_pos, "32bit");
+ break;
+#endif
+#if defined(POWERPC) && !defined(POWERPC64LE)
+ case ARCH_ppc64_64bit:
+ al_mark_pers4arch(a_serv, a_pos,
+# if defined(POWERPC64)
+ "64bit"
+# else
+ "32bit"
+# endif
+ );
+ break;
+#endif
+#if defined(TILE)
+ case ARCH_tile_64bit:
+ al_mark_pers4arch(a_serv, a_pos,
+#if defined(__tilepro__)
+ "32bit"
+#else
+ "64bit"
+#endif
+ );
+ break;
+#endif
+ default:
+ if (arch_size == 1) {
+ al_add_flag(a_serv, a_pos, AD_FLAG_PRINT);
+ goto done;
+ }
+ es_set_error(al_err(a_serv), ABI_CANNOT_DETECT);
+ es_set_option(al_err(a_serv),
+ al_in_aname(a_serv, a_pos), NULL, NULL);
+ }
+ goto done;
+ }
+
+ if (request_type & ABD_REQ_LIST_ABI) {
+ while (a_pos != arch_size) {
+ if (!al_mark_pers4arch(a_serv, a_pos, "all")) {
+ a_pos += al_get_abi_modes(a_serv, a_pos);
+ continue;
+ }
+ break;
+ }
+ goto done;
+ }
+
+ if (request_type & ABD_REQ_SET_ABI) {
+ for (i = 0; abi[i] != NULL; i++) {
+ if (!al_mark_pers4arch(a_serv, a_pos, abi[i])) {
+ a_pos += al_get_abi_modes(a_serv, a_pos);
+ continue;
+ }
+ es_set_error(al_err(a_serv), ABI_WRONG4ARCH);
+ es_set_option(al_err(a_serv),
+ al_in_aname(a_serv, a_pos),
+ abi[i], NULL);
+ break;
+ }
+ }
+done:
+ return 0;
+}
+
+struct syscall_service *
+syscall_dispatcher(struct arch_service *arch, int request_type, char *sysc)
+{
+ SYSCALL_LIST_DEFINE(sysc_serv) = ss_create(arch, request_type);
+ int narch = ss_size(sysc_serv);
+ int i = 0;
+ int ret = 0;
+ int count = 0;
+
+ if (request_type & SD_REQ_MASK) {
+ for (i = 0; i < narch; i++) {
+ ss_update_sc_num(sysc_serv, i);
+ ret = ss_mark_matches(sysc_serv, i, sysc);
+ if (ret == SD_NO_MATCHES_FND)
+ count++;
+ }
+ }
+ /* Clear error if we are in multiarch mode */
+ if (count != narch && narch != 1)
+ es_set_error(ss_err(sysc_serv), NO_ERROR);
+
+ return sysc_serv;
+}
diff --git a/tools/asinfo/dispatchers.h b/tools/asinfo/dispatchers.h
new file mode 100644
index 000000000..ec406c0cf
--- /dev/null
+++ b/tools/asinfo/dispatchers.h
@@ -0,0 +1,29 @@
+/*
+ * The dispatchers.h contains all necessary functions to perform main
+ * work in the asinfo tool.
+ *
+ * Copyright (c) 2017 Edgar A. Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
+ * Copyright (c) 2017-2018 The strace developers.
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef ASINFO_DISPATCHERS_H
+#define ASINFO_DISPATCHERS_H
+
+#include "arch_interface.h"
+#include "syscall_interface.h"
+
+/* The function is purposed to provide correct list of architectures */
+struct arch_service *arch_dispatcher(unsigned request_type, char *arch[]);
+
+/* Final arch filtering based on personality */
+int abi_dispatcher(struct arch_service *a_serv, unsigned request_type,
+ char *abi[]);
+
+/* The last stage of main filtering */
+struct syscall_service *syscall_dispatcher(struct arch_service *arch,
+ int request_type, char *sysc);
+
+#endif /* !ASINFO_DISPATCHERS_H */
diff --git a/tools/asinfo/error_interface.c b/tools/asinfo/error_interface.c
new file mode 100644
index 000000000..b896c4d1d
--- /dev/null
+++ b/tools/asinfo/error_interface.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2017 Edgar A. Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
+ * Copyright (c) 2017-2018 The strace developers.
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "error_interface.h"
+#include "xmalloc.h"
+
+static const char *errors[] = {
+ [AD_UNSUP_ARCH_BIT] = "architecture '%s' is unsupported",
+ [ABI_CANNOT_DETECT_BIT] = "ABI mode cannot be automatically "
+ "detected for non-target architecture '%s'",
+ [ABI_WRONG4ARCH_BIT] = "architecture '%s' does not have ABI mode "
+ "'%s'",
+ [SD_NO_MATCHES_FND_BIT] = "no matches found",
+};
+
+struct error_service *
+es_create(void)
+{
+ struct error_service *err = xcalloc(sizeof(*err), 1);
+
+ return err;
+}
+
+enum common_error
+es_error(struct error_service *e)
+{
+ return e->last_error;
+}
+
+void
+es_set_error(struct error_service *e, enum common_error error)
+{
+ e->last_error = error;
+}
+
+void
+es_set_option(struct error_service *e, char *arch, char *abi, char *sc)
+{
+ if (arch) {
+ if (e->last_arch)
+ free(e->last_arch);
+ e->last_arch = xcalloc(sizeof(*(e->last_arch)),
+ strlen(arch) + 1);
+ strcpy(e->last_arch, arch);
+ }
+ if (abi) {
+ if (e->last_abi)
+ free(e->last_abi);
+ e->last_abi = xcalloc(sizeof(*(e->last_abi)), strlen(abi) + 1);
+ strcpy(e->last_abi, abi);
+ }
+ if (sc) {
+ if (e->last_sc)
+ free(e->last_sc);
+ e->last_sc = xcalloc(sizeof(*(e->last_sc)), strlen(sc) + 1);
+ strcpy(e->last_sc, sc);
+ }
+}
+
+const char *
+es_get_serror(struct error_service *e)
+{
+ int err = 1 << e->last_error;
+ if (err & ERROR_ARCH_MASK)
+ sprintf(e->string, errors[e->last_error], e->last_arch);
+ else if (err & ERROR_NO_ARG_MASK)
+ sprintf(e->string, "%s", errors[e->last_error]);
+ else if (err & ERROR_ARCH_ABI_MASK)
+ sprintf(e->string, errors[e->last_error], e->last_arch,
+ e->last_abi);
+ return (const char *)(e->string);
+}
+
+void
+es_free(struct error_service *e)
+{
+ free(e);
+}
diff --git a/tools/asinfo/error_interface.h b/tools/asinfo/error_interface.h
new file mode 100644
index 000000000..be0a47f49
--- /dev/null
+++ b/tools/asinfo/error_interface.h
@@ -0,0 +1,76 @@
+/*
+ * As each dispatcher has a wide range of possible errors, there is need
+ * use separate and basic error interface.
+ *
+ * Copyright (c) 2017 Edgar A. Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
+ * Copyright (c) 2017-2018 The strace developers.
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef ASINFO_ERROR_INTERFACE_H
+#define ASINFO_ERROR_INTERFACE_H
+
+/* errors which using last_arch */
+enum error_arch {
+ NO_ERROR_BIT,
+ AD_UNSUP_ARCH_BIT = 1,
+ ABI_CANNOT_DETECT_BIT,
+
+ ERROR_ARCH_LAST
+};
+
+enum error_no_arg {
+ SD_NO_MATCHES_FND_BIT = ERROR_ARCH_LAST,
+
+ ERROR_NO_ARG_LAST
+};
+
+enum error_arch_abi {
+ ABI_WRONG4ARCH_BIT = ERROR_NO_ARG_LAST,
+
+ ERROR_ARCH_ABI_LAST
+};
+
+#define ENUM_FLAG(name) name = name##_BIT
+enum common_error {
+ ENUM_FLAG(NO_ERROR),
+ /* arch dispatcher range */
+ ENUM_FLAG(AD_UNSUP_ARCH),
+ /* abi dipatcher range */
+ ENUM_FLAG(ABI_CANNOT_DETECT),
+ ENUM_FLAG(ABI_WRONG4ARCH),
+ /* syscall dispatcher range */
+ ENUM_FLAG(SD_NO_MATCHES_FND)
+};
+#undef ENUM_FLAG
+
+#define BITMASK(hi, lo) ((1 << (hi)) - (1 << (lo)))
+#define ERROR_ARCH_MASK BITMASK(ERROR_ARCH_LAST, 0)
+#define ERROR_NO_ARG_MASK BITMASK(ERROR_NO_ARG_LAST, ERROR_ARCH_LAST)
+#define ERROR_ARCH_ABI_MASK BITMASK(ERROR_ARCH_ABI_LAST, ERROR_NO_ARG_LAST)
+
+#define ERROR_MSG_MAX_LEN 255
+
+struct error_service {
+ char string[ERROR_MSG_MAX_LEN];
+ enum common_error last_error;
+ char *last_arch;
+ char *last_abi;
+ char *last_sc;
+};
+
+struct error_service *es_create(void);
+
+enum common_error es_error(struct error_service *s);
+
+void es_set_error(struct error_service *s, enum common_error se);
+
+void es_set_option(struct error_service *e, char *arch, char *abi, char *sc);
+
+const char *es_get_serror(struct error_service *e);
+
+void es_free(struct error_service *e);
+
+#endif /* !ASINFO_ERROR_INTERFACE_H */
diff --git a/tools/asinfo/gen_asinfo_files.sh b/tools/asinfo/gen_asinfo_files.sh
new file mode 100755
index 000000000..804f92f2f
--- /dev/null
+++ b/tools/asinfo/gen_asinfo_files.sh
@@ -0,0 +1,153 @@
+#!/bin/sh
+#
+# Code generator simplifies addition of new architecture to asinfo tool
+#
+# Copyright (c) 2017 Edgar A. Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
+# Copyright (c) 2017-2018 The strace developers.
+# All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+cur_pers=""
+
+gen_pers_line()
+{
+ local out_file="$1"
+ local line="$2"
+ local arch_abi=""
+
+ LC_COLLATE=C
+ arch_abi="$(printf %s "${line}" |
+ sed 's/ARCH_DESC_DEFINE(//' | cut -d, -f 1,2 |
+ sed 's/,/_/g')"
+ cur_pers="${arch_abi}"
+ echo "ARCH_${arch_abi}," >> "${out_file}"
+}
+
+gen_includes_block()
+{
+ local out_file="$1"
+ local line="$2"
+ local def is_def includes include nums num count
+
+ (
+ LC_COLLATE=C
+ echo "/* ${cur_pers} */"
+ def="$(printf %s "${line#*\*}" | cut -d] -f 1 | sed 's/.*[][]//g')"
+ #Generate define construction
+ if [ "${def#!}" != "" ] && [ $(printf %.1s "${def}") = "!" ]; then
+ cat <<-EOF
+ #ifdef ${def#!}
+ # undef ${def#!}
+ # define ${def#!}_DUMMY_UNDEFINE
+ #endif
+ EOF
+ is_def="def"
+ else if [ "${def#!}" != "" ]; then
+ cat <<-EOF
+ #ifndef ${def}
+ # define ${def}
+ # define ${def}_DUMMY_DEFINE
+ #endif
+ EOF
+ is_def="undef"
+ fi
+ fi
+ #Generate includes
+ includes="$(printf %s "${line#*\*}" | cut -d] -f 2 | sed 's/.*[][]//g')"
+ echo "static const struct_sysent ${cur_pers}_sysent[] = {"
+ for include in $(echo "${includes}" | sed "s/,/ /g")
+ do
+ echo " #include \"${include}\""
+ done
+ echo "};"
+ #Undefine definitions, if it is required
+ if [ "${is_def}" = "def" ]; then
+ cat <<-EOF
+ #ifdef ${def#!}_DUMMY_UNDEFINE
+ # define ${def#!} 1
+ # undef ${def#!}_DUMMY_UNDEFINE
+ #endif
+ EOF
+ else if [ "${is_def}" = "undef" ]; then
+ cat <<-EOF
+ #ifdef ${def#!}_DUMMY_DEFINE
+ # undef ${def#!}
+ # undef ${def#!}_DUMMY_DEFINE
+ #endif
+ EOF
+ fi
+ fi
+ #Generate arch specific numbers
+ nums="$(printf %s "${line#*\*}" | cut -d] -f 3 | sed 's/.*[][]//g')"
+ count=1
+ for num in $(echo "${nums}" | sed "s/,/ /g")
+ do
+ echo "static const int ${cur_pers}_usr${count} = ${num};"
+ count=$((count+1))
+ case "${num}" in
+ *[A-Za-z_]*) echo "#undef ${num}" ;;
+ *) ;;
+ esac
+ done
+ if [ $count -eq 1 ]; then
+ echo "static const int ${cur_pers}_usr${count} = 0;"
+ fi
+ echo "#undef SYS_socket_subcall"
+ echo "#undef BASE_NR"
+ ) >> "${out_file}"
+ echo "${def}" >> "${out_file}"
+}
+
+main()
+{
+ set -- "${0%/*}" "${0%/*}"
+
+ local input="$1"
+ local output="$2"
+ local defs_file="arch_definitions.h"
+ local pers_file="arch_personalities.h"
+ local includes_file="arch_includes.h"
+ local pline=""
+
+ echo "ARCH_no_pers," > "${output}/${pers_file}"
+ echo -n > "${output}/${includes_file}"
+
+ #Main work
+ while read line; do
+ line="$(printf %s "${line}" | sed 's/[[:space:]]//g')"
+ if $(printf %s "${line}" |
+ grep -F "ARCH_DESC_DEFINE" > /dev/null); then
+ gen_pers_line "${output}/${pers_file}" "${line}"
+ fi
+ if $(printf %s "${pline}" | grep -F "/*" > /dev/null); then
+ gen_includes_block "${output}/${includes_file}"\
+ "${pline}"
+ fi
+ pline="${line}"
+ done < "${input}/${defs_file}"
+ #Makemodule.am
+ (
+ printf \
+"ARCH_AUX_FILES = ${includes_file} ${pers_file}\n\
+\$(top_srcdir)/tools/asinfo/${includes_file}: \
+\$(top_srcdir)/tools/asinfo/${defs_file} \
+\$(top_srcdir)/tools/asinfo/gen_asinfo_files.sh\n\
+ \$(AM_V_GEN)\$(top_srcdir)/tools/asinfo/gen_asinfo_files.sh\n\
+\$(top_srcdir)/tools/asinfo/${pers_file}: \
+\$(top_srcdir)/tools/asinfo/${defs_file} \
+\$(top_srcdir)/tools/asinfo/gen_asinfo_files.sh\n\
+ \$(AM_V_GEN)\$(top_srcdir)/tools/asinfo/gen_asinfo_files.sh"
+ ) > "${output}/Makemodule.am"
+ #.gitignore
+ (
+ printf \
+"/${includes_file}\n\
+/${pers_file}\n\
+/Makemodule.am\n\
+/.gitignore\n\
+/asinfo.1"
+ ) > "${output}/.gitignore"
+}
+
+main "$@"
diff --git a/tools/asinfo/request_msgs.h b/tools/asinfo/request_msgs.h
new file mode 100644
index 000000000..c6dbd7efc
--- /dev/null
+++ b/tools/asinfo/request_msgs.h
@@ -0,0 +1,74 @@
+/*
+ * The request_msgs are purposed to set the general mode of work, in
+ * particular, the work of main dispatchers.
+ *
+ * Copyright (c) 2017 Edgar A. Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
+ * Copyright (c) 2017-2018 The strace developers.
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef ASINFO_REQUEST_MSGS_H
+#define ASINFO_REQUEST_MSGS_H
+
+/* Request types for syscall_dispatcher,
+ * arch_dispatcher, which, in turn, could be combined
+ */
+enum syscall_req_bit {
+ SD_REQ_GET_SNAME_BIT,
+ SD_REQ_GET_SNUM_BIT,
+ SD_REQ_NARGS_BIT,
+
+ SYSCALL_REQ_BIT_LAST
+};
+
+enum arch_req_bit {
+ AD_REQ_SET_ARCH_BIT = SYSCALL_REQ_BIT_LAST,
+ AD_REQ_GET_ARCH_BIT,
+ AD_REQ_LIST_ARCH_BIT,
+
+ ARCH_REQ_BIT_LAST
+};
+
+enum abi_req_bit {
+ ABD_REQ_SET_ABI_BIT = ARCH_REQ_BIT_LAST,
+ ABD_REQ_LIST_ABI_BIT,
+
+ ABD_REQ_BIT_LAST
+};
+
+enum serv_req_bit {
+ SERV_REQ_HELP_BIT = ABD_REQ_BIT_LAST,
+ SERV_REQ_VERSION_BIT,
+ SERV_REQ_ERROR_BIT,
+ SERV_REQ_RAW_BIT,
+
+ SERV_REQ_BIT_LAST
+};
+
+#define ENUM_FLAG(name) name = 1 << name##_BIT
+enum req_type {
+ ENUM_FLAG(SD_REQ_GET_SNAME),
+ ENUM_FLAG(SD_REQ_GET_SNUM),
+ ENUM_FLAG(SD_REQ_NARGS),
+ ENUM_FLAG(AD_REQ_SET_ARCH),
+ ENUM_FLAG(AD_REQ_GET_ARCH),
+ ENUM_FLAG(AD_REQ_LIST_ARCH),
+ ENUM_FLAG(ABD_REQ_SET_ABI),
+ ENUM_FLAG(ABD_REQ_LIST_ABI),
+ ENUM_FLAG(SERV_REQ_HELP),
+ ENUM_FLAG(SERV_REQ_VERSION),
+ ENUM_FLAG(SERV_REQ_ERROR),
+ ENUM_FLAG(SERV_REQ_RAW)
+};
+#undef ENUM_FLAG
+
+#define BITMASK(hi, lo) ((1 << (hi)) - (1 << (lo)))
+#define REQ_LAST_BIT SERV_REQ_BIT_LAST
+#define SD_REQ_MASK BITMASK(SYSCALL_REQ_BIT_LAST, 0)
+#define AD_REQ_MASK BITMASK(ARCH_REQ_BIT_LAST, SYSCALL_REQ_BIT_LAST)
+#define ABD_REQ_MASK BITMASK(ABD_REQ_BIT_LAST, ARCH_REQ_BIT_LAST)
+#define SERV_REQ_MASK BITMASK(SERV_REQ_BIT_LAST, ABD_REQ_BIT_LAST)
+
+#endif /* !ASINFO_REQUEST_MSGS_H */
diff --git a/tools/asinfo/syscall_interface.c b/tools/asinfo/syscall_interface.c
new file mode 100644
index 000000000..fe0fecb35
--- /dev/null
+++ b/tools/asinfo/syscall_interface.c
@@ -0,0 +1,672 @@
+/*
+ * Copyright (c) 2017 Edgar A. Kaziakhmedov <edgar.kaziakhmedv@virtuozzo.com>
+ * Copyright (c) 2017-2018 The strace developers.
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "macros.h"
+#include "arch_interface.h"
+#include "error_interface.h"
+#include "filter.h"
+#include "number_set.h"
+#include "arch_defs.h"
+#include "syscall_interface.h"
+#include "sysent.h"
+#include "request_msgs.h"
+#include "xmalloc.h"
+
+/* We shouldn't include defs.h here, because the following definitions
+ cannot be with const qualifier */
+const struct_sysent *sysent_vec[SUPPORTED_PERSONALITIES] = {NULL};
+unsigned int nsyscall_vec[SUPPORTED_PERSONALITIES] = {0};
+
+const char *const personality_designators[] =
+# if defined X86_64
+ { "64", "32", "x32" }
+# elif defined X32
+ { "x32", "32" }
+# elif SUPPORTED_PERSONALITIES == 2
+ { "64", "32" }
+# else
+ { STRINGIFY_VAL(__WORDSIZE) }
+# endif
+ ;
+
+struct syscall_service *
+ss_create(struct arch_service *m, int request_type)
+{
+ int i;
+ int ss_count = 0;
+ int ssize = al_psize(m);
+ int asize = al_size(m);
+ struct syscall_service *ss = NULL;
+ int scn = 0;
+
+ ss = xcalloc(sizeof(*ss), 1);
+ ss->err = al_err(m);
+ /* If we are in arch/abi mode, but we need syscall_service to pass
+ check for errors */
+ if (!(request_type & SD_REQ_MASK) || ssize == 0)
+ return ss;
+ ss->aws = xcalloc(sizeof(*(ss->aws)), ssize);
+ ss->narch = ssize;
+ for (i = 0; i < asize; i++)
+ if (al_flag(m, i) & AD_FLAG_PRINT) {
+ ss->aws[ss_count].arch = al_get(m, i);
+ scn = ss->aws[ss_count].arch->max_scn;
+ ss->aws[ss_count].flag = xcalloc(sizeof(int), scn);
+ ss->aws[ss_count].real_snum = xcalloc(sizeof(int), scn);
+ ss->aws[ss_count].a_name = al_in_aname(m, i);
+ al_set_in_aname(m, i, NULL);
+ ss_count++;
+ }
+ ss->request_type = request_type;
+ return ss;
+}
+
+int
+ssa_is_ok(struct syscall_service *s, int arch, int num)
+{
+ if (s == NULL || arch > s->narch || arch < 0 || num < 0 ||
+ (unsigned int) num >= s->aws[arch].arch->max_scn)
+ return 0;
+ return 1;
+}
+
+struct error_service *
+ss_err(struct syscall_service *s)
+{
+ return s->err;
+}
+
+int
+ss_size(struct syscall_service *s)
+{
+ return s->narch;
+}
+
+int
+ssa_max_scn(struct syscall_service *s, int arch)
+{
+ if (!ssa_is_ok(s, arch, 0))
+ return -1;
+ return s->aws[arch].arch->max_scn;
+}
+
+const struct_sysent *
+ssa_sysc_list(struct syscall_service *s, int arch)
+{
+ if (!ssa_is_ok(s, arch, 0))
+ return NULL;
+ return s->aws[arch].arch->syscall_list;
+}
+
+int
+ssa_flag(struct syscall_service *s, int arch, int num)
+{
+ if (!ssa_is_ok(s, arch, num))
+ return -1;
+ return s->aws[arch].flag[num];
+}
+
+int
+ssa_set_flag(struct syscall_service *s, int arch, int num, int flag)
+{
+ if (!ssa_is_ok(s, arch, num))
+ return -1;
+ s->aws[arch].flag[num] = flag;
+ return 0;
+}
+
+int
+ssa_real_num(struct syscall_service *s, int arch, int num)
+{
+ if (!ssa_is_ok(s, arch, num))
+ return -1;
+ return s->aws[arch].real_snum[num];
+}
+
+int
+ssa_set_real_num(struct syscall_service *s, int arch, int num, int real_num)
+{
+ if (!ssa_is_ok(s, arch, num))
+ return -1;
+ s->aws[arch].real_snum[num] = real_num;
+ return 0;
+}
+
+const char *
+ssa_syscall_name(struct syscall_service *s, int arch, int num)
+{
+ if (!ssa_is_ok(s, arch, num))
+ return NULL;
+ return s->aws[arch].arch->syscall_list[num].sys_name;
+}
+
+int
+ssa_syscall_flag(struct syscall_service *s, int arch, int num)
+{
+ if (!ssa_is_ok(s, arch, num))
+ return -1;
+ return s->aws[arch].arch->syscall_list[num].sys_flags;
+}
+
+int
+ssa_syscall_nargs(struct syscall_service *s, int arch, int num)
+{
+ if (!ssa_is_ok(s, arch, num))
+ return -1;
+ return (int)s->aws[arch].arch->syscall_list[num].nargs;
+}
+
+int
+ssa_user_num1(struct syscall_service *s, int arch)
+{
+ if (!ssa_is_ok(s, arch, 0))
+ return -1;
+ return *(s->aws[arch].arch->user_num1);
+}
+
+int
+ssa_user_num2(struct syscall_service *s, int arch)
+{
+ if (!ssa_is_ok(s, arch, 0))
+ return -1;
+ return *(s->aws[arch].arch->user_num2);
+}
+
+void
+ss_free(struct syscall_service *s)
+{
+ int i;
+
+ es_free(ss_err(s));
+ for (i = 0; i < s->narch; i++ ) {
+ free(s->aws[i].flag);
+ free(s->aws[i].real_snum);
+ if (s->aws[i].a_name)
+ free(s->aws[i].a_name);
+ }
+ free(s);
+}
+
+int
+ssa_print_size(struct syscall_service *s, int arch)
+{
+ int i;
+ int max_scn = ssa_max_scn(s, arch);
+ int psize = 0;
+
+ for (i = 0; i < max_scn; i++)
+ if (ssa_flag(s, arch, i) & SS_FLAG_PRINT)
+ psize++;
+ return psize;
+}
+
+int
+ssa_is_syscall_valid(struct syscall_service *s, int arch, int num)
+{
+ if (!ssa_is_ok(s, arch, num))
+ return 0;
+ return ssa_syscall_name(s, arch, num) &&
+ !(ssa_syscall_flag(s, arch, num) & TRACE_INDIRECT_SUBCALL);
+}
+
+int
+ss_mark_matches(struct syscall_service *s, int arch, char *arg)
+{
+ int i = 0;
+ int scount = 0;
+ int max_scn = ssa_max_scn(s, arch);
+ struct number_set *trace_set;
+
+ /* Init global variables */
+ nsyscall_vec[0] = ssa_max_scn(s, arch);
+ sysent_vec[0] = ssa_sysc_list(s, arch);
+
+ trace_set = alloc_number_set_array(SUPPORTED_PERSONALITIES);
+ qualify_syscall_tokens(arg, trace_set);
+ for (i = 0; i < max_scn; i++)
+ if (ssa_is_syscall_valid(s, arch, i) &&
+ ssa_real_num(s, arch, i) != HIDDEN_SYSC &&
+ is_number_in_set_array(i, trace_set, 0)) {
+ ssa_set_flag(s, arch, i, SS_FLAG_PRINT);
+ scount++;
+ }
+ if (scount == 0) {
+ es_set_error(ss_err(s), SD_NO_MATCHES_FND);
+ return SD_NO_MATCHES_FND;
+ }
+ return 0;
+}
+
+int
+ss_update_sc_num(struct syscall_service *s, int arch)
+{
+ int i = 0;
+ int max_scn = ssa_max_scn(s, arch);
+ int usr1n = ssa_user_num1(s, arch);
+ int usr2n = ssa_user_num2(s, arch);
+
+ for (i = 0; i < max_scn; i++) {
+ if (!ssa_is_syscall_valid(s, arch, i)) {
+ ssa_set_real_num(s, arch, i, HIDDEN_SYSC);
+ continue;
+ }
+ switch (s->aws[arch].arch->pers) {
+ case ARCH_x86_64_x32:
+ /* Pure x32 specific syscalls without X32_SYSCALL_BIT */
+ if (strstr(ssa_syscall_name(s, arch, i), "64:"))
+ ssa_set_real_num(s, arch, i, HIDDEN_SYSC);
+ else
+ ssa_set_real_num(s, arch, i, i);
+ break;
+ case ARCH_arm_oabi:
+ case ARCH_arm_eabi:
+ /* Do not deal with private ARM syscalls */
+ if (i == usr1n)
+ ssa_set_real_num(s, arch, i, HIDDEN_SYSC);
+ if ((i >= usr1n + 1) && (i <= usr1n + usr2n + 1))
+ ssa_set_real_num(s, arch, i, HIDDEN_SYSC);
+ if (i < usr1n)
+ ssa_set_real_num(s, arch, i, i);
+ break;
+ case ARCH_sh64_64bit:
+ ssa_set_real_num(s, arch, i, i & 0xffff);
+ break;
+ default:
+ ssa_set_real_num(s, arch, i, i);
+ }
+ }
+ return 0;
+}
+
+static int
+sysccmp(const void *arg1, const void *arg2)
+{
+ const char *str1 = ((struct in_sysc *)arg1)->sys_name;
+ const char *str2 = ((struct in_sysc *)arg2)->sys_name;
+
+ return strcmp(str1, str2);
+}
+
+static struct sysc_meta *
+ss_make_union(struct syscall_service *s,
+ void* (save)(struct syscall_service *, int, int))
+{
+ struct in_sysc **sysc;
+ struct out_sysc *sysc_l, *sysc_r, *sysc_to, *sysc_fr;
+ struct sysc_meta *sm = xcalloc(sizeof(*sm), 1);
+ int size = ss_size(s);
+ int max_scn;
+ int psize;
+ int out_size = 0;
+ int i, j, k, l;
+ int c = 0;
+ int res = 0;
+ int eff_size = 1;
+ /* Preparation */
+ sysc = xcalloc(sizeof(*sysc), size);
+ for (i = 0; i < size; i++) {
+ max_scn = ssa_max_scn(s, i);
+ psize = ssa_print_size(s, i);
+ sysc[i] = xcalloc(sizeof(**sysc), psize + 1);
+ c = 0;
+ for (j = 0; j < max_scn; j++) {
+ if (!(ssa_flag(s, i, j) & SS_FLAG_PRINT))
+ continue;
+ sysc[i][c].sys_name = ssa_syscall_name(s, i, j);
+ sysc[i][c].data = save(s, i, j);
+ c++;
+ }
+ qsort(sysc[i], psize, sizeof(struct in_sysc), &sysccmp);
+ out_size += psize;
+ }
+ /* Allocation */
+ sysc_l = xcalloc(sizeof(*sysc_l), out_size);
+ sysc_r = xcalloc(sizeof(*sysc_r), out_size);
+ for (i = 0; i < out_size; i++) {
+ sysc_r[i].data = xcalloc(sizeof(void *), size);
+ sysc_l[i].data = xcalloc(sizeof(void *), size);
+ }
+ /* Set with first arch in sysc */
+ for (i = 0; sysc[0][i].sys_name != NULL; i++) {
+ sysc_r[i].sys_name = sysc[0][i].sys_name;
+ sysc_r[i].data[0] = sysc[0][i].data;
+ }
+ /* Union
+ Main idea is simple:
+ [sysc_to]<->[sysc_fr]<------[sysc1][sysc2][sysc3]...
+ 1) [sysc_fr] = [sysc1]
+ 2) [sysc_to] = [sysc_fr] | [sysc2]
+ 3) [sysc_to] <-> [sysc_fr]
+ 4) [sysc_to] = [sysc_fr] | [sysc3]
+ etc. */
+ sysc_to = sysc_r;
+ sysc_fr = sysc_l;
+ for (i = 1; i < size; i++) {
+ l = 0; j = 0; k = 0;
+ if (sysc[i][j].sys_name != NULL || i == 1) {
+ sysc_fr = (eff_size % 2) ? sysc_r : sysc_l;
+ sysc_to = (eff_size % 2) ? sysc_l : sysc_r;
+ eff_size++;
+ }
+ while (sysc[i][j].sys_name != NULL && k < out_size) {
+ memset(sysc_to[l].data, 0, sizeof(void *) * size);
+ if (!sysc_fr[k].sys_name)
+ res = -1;
+ else
+ res = strcmp(sysc[i][j].sys_name,
+ sysc_fr[k].sys_name);
+ if (res < 0) {
+ sysc_to[l].sys_name = sysc[i][j].sys_name;
+ sysc_to[l].data[i] = sysc[i][j].data;
+ j++;
+ } else if (res > 0) {
+ sysc_to[l].sys_name = sysc_fr[k].sys_name;
+ memcpy(sysc_to[l].data, sysc_fr[k].data,
+ sizeof(void *) * size);
+ k++;
+ } else {
+ sysc_to[l].sys_name = sysc[i][j].sys_name;
+ memcpy(sysc_to[l].data, sysc_fr[k].data,
+ sizeof(void *) * size);
+ sysc_to[l].data[i] = sysc[i][j].data;
+ k++;
+ j++;
+ }
+ l++;
+ }
+ while (k < out_size && l < out_size) {
+ sysc_to[l].sys_name = sysc_fr[k].sys_name;
+ memcpy(sysc_to[l].data, sysc_fr[k].data,
+ sizeof(void *) * size);
+ k++;
+ l++;
+ }
+ }
+ /* Free */
+ for (i = 0; i < out_size; i++)
+ free(sysc_fr[i].data);
+ free(sysc_fr);
+ for (i = 0; i < size; i++)
+ free(sysc[i]);
+ free(sysc);
+
+ sm->sysc_list = sysc_to;
+ sm->size = out_size;
+ return sm;
+}
+
+static struct sysc_meta *
+ss_make_enumeration(struct syscall_service *s,
+ void* (save)(struct syscall_service *, int, int))
+{
+ struct out_sysc *sysc_out;
+ struct sysc_meta *sm = xcalloc(sizeof(*sm), 1);
+ int size = ss_size(s);
+ int max_scn = 0;
+ int i, j, k;
+ int flag;
+ bool clear = true;
+
+ for (i = 0; i < size; i++)
+ if (max_scn < ssa_max_scn(s, i))
+ max_scn = ssa_max_scn(s, i);
+
+ sysc_out = xcalloc(sizeof(*sysc_out), max_scn);
+ for (i = 0; i < max_scn; i++)
+ sysc_out[i].data = xcalloc(sizeof(void *), size);
+ for (i = 0; i < size; i++)
+ for (j = 0; j < max_scn; j++) {
+ clear = true;
+ flag = ssa_flag(s, i, j);
+ if ((flag != -1) && (flag & SS_FLAG_PRINT)) {
+ sysc_out[j].sys_num = j;
+ sysc_out[j].data[i] = save(s, i, j);
+ }
+ for (k = 0; k <= i; k++)
+ if (sysc_out[j].data[k])
+ clear = 0;
+ if (clear)
+ sysc_out[j].sys_num = -1;
+ }
+ sm->sysc_list = sysc_out;
+ sm->size = max_scn;
+ return sm;
+}
+
+static void *
+ssa_save_snum(struct syscall_service *s, int arch, int snum)
+{
+ return (void *)&(s->aws[arch].real_snum[snum]);
+}
+
+static void *
+ssa_save_nargs(struct syscall_service *s, int arch, int snum)
+{
+ return (void *)&(s->aws[arch].arch->syscall_list[snum].nargs);
+}
+
+static void *
+ssa_save_sname(struct syscall_service *s, int arch, int snum)
+{
+ return (void *)(s->aws[arch].arch->syscall_list[snum].sys_name);
+}
+
+static unsigned
+fast_len(int num)
+{
+ int i;
+ unsigned count = 0;
+
+ for (i = 1; num/i != 0; i *= 10)
+ count++;
+ return count;
+}
+
+static unsigned *
+ss_get_width_sname(struct syscall_service *s, struct sysc_meta *sm, int narch)
+{
+ /* '2' hereinafter takes into account first two default columns */
+ unsigned *width = xcalloc(sizeof(*width), narch + 2);
+ int i, j;
+ unsigned len;
+ unsigned max;
+ int N = 1;
+ struct out_sysc *psysc = sm->sysc_list;
+
+ /* Calculate length of 'N' and sname columns */
+ for (i = 0; i < sm->size; i++) {
+ if (!psysc[i].sys_name)
+ continue;
+ len = strlen(psysc[i].sys_name);
+ if (len > width[1])
+ width[1] = len;
+ N++;
+ }
+ width[0] = fast_len(N);
+ for (i = 0; i < narch; i++) {
+ for (j = 0; j < sm->size; j++) {
+ if (!psysc[j].data[i])
+ continue;
+ max = *((int *)(psysc[j].data[i]));
+ if (max > width[i + 2])
+ width[i + 2] = max;
+ }
+ width[i + 2] = fast_len(width[i + 2]);
+ max = 0;
+ }
+ return width;
+}
+
+static unsigned *
+ss_get_width_snum(struct syscall_service *s, struct sysc_meta *sm, int narch)
+{
+ unsigned *width = xcalloc(sizeof(*width), narch + 2);
+ int i, j;
+ unsigned len;
+ unsigned max;
+ int N = 1;
+ struct out_sysc *psysc = sm->sysc_list;
+
+
+ /* Calculate length of 'N' and snum columns */
+ for (i = 0; i < sm->size; i++) {
+ if (psysc[i].sys_num == -1)
+ continue;
+ max = fast_len(psysc[i].sys_num);
+ if (max > width[1])
+ width[1] = max;
+ N++;
+ }
+ width[1] = fast_len(width[1]);
+ width[0] = fast_len(N);
+ for (i = 0; i < narch; i++) {
+ for (j = 0; j < sm->size; j++) {
+ if (!psysc[j].data[i])
+ continue;
+ if (s->request_type & SD_REQ_NARGS)
+ len = *((int *)(psysc[j].data[i]));
+ else
+ len = strlen((char *)(psysc[j].data[i]));
+ if (len > width[i + 2])
+ width[i + 2] = len;
+ }
+ if (s->request_type & SD_REQ_NARGS)
+ width[i + 2] = fast_len(width[i + 2]);
+ len = 0;
+ }
+ return width;
+}
+
+void
+ss_dump(struct syscall_service *s, int is_raw)
+{
+ static const char *title[] = {
+ "N",
+ "Syscall name",
+ "Snum",
+ };
+ struct sysc_meta *sm;
+ struct out_sysc *psysc;
+ int ncolumn = ss_size(s);
+ unsigned *title_len;
+ int N = 1;
+ int i, j;
+
+ /* Main work */
+ if (s->request_type & SD_REQ_GET_SNAME) {
+ if (s->request_type & SD_REQ_NARGS)
+ sm = ss_make_union(s, ssa_save_nargs);
+ else
+ sm = ss_make_union(s, ssa_save_snum);
+ title_len = ss_get_width_sname(s, sm, ncolumn);
+ if (strlen(title[1]) > title_len[1])
+ title_len[1] = strlen(title[1]);
+ } else {
+ if (s->request_type & SD_REQ_NARGS)
+ sm = ss_make_enumeration(s, ssa_save_nargs);
+ else
+ sm = ss_make_enumeration(s, ssa_save_sname);
+ title_len = ss_get_width_snum(s, sm, ncolumn);
+ if (strlen(title[2]) > title_len[1])
+ title_len[1] = strlen(title[2]);
+ }
+ psysc = sm->sysc_list;
+ if (is_raw)
+ goto skip_format;
+ /* Adjust width */
+ for (i = 0; i < ncolumn; i++) {
+ if (strlen(s->aws[i].a_name) > title_len[i + 2])
+ title_len[i + 2] = strlen(s->aws[i].a_name);
+ if (strlen(s->aws[i].arch->abi_mode) > title_len[i + 2])
+ title_len[i + 2] = strlen(s->aws[i].arch->abi_mode);
+ }
+ /* Print out title */
+ printf("| %*s | %*s |", title_len[0], "", title_len[1], "");
+ for (i = 0; i < ncolumn; i++)
+ printf(" %*s |", title_len[i + 2], s->aws[i].a_name);
+ puts("");
+ printf("| %*s |", title_len[0], title[0]);
+ if (s->request_type & SD_REQ_GET_SNAME)
+ printf(" %*s |", title_len[1], title[1]);
+ else
+ printf(" %*s |", title_len[1], title[2]);
+ for (i = 0; i < ncolumn; i++)
+ printf(" %*s |", title_len[i + 2], s->aws[i].arch->abi_mode);
+ puts("");
+ /* Syscalls */
+ for (i = 0; i < sm->size; i++) {
+ if (s->request_type & SD_REQ_GET_SNAME) {
+ if (psysc[i].sys_name == NULL)
+ continue;
+ printf("| %*d |", title_len[0], N);
+ printf(" %*s |", title_len[1], psysc[i].sys_name);
+ } else {
+ if (psysc[i].sys_num == -1)
+ continue;
+ printf("| %*d |", title_len[0], N);
+ printf(" %*d |", title_len[1], psysc[i].sys_num);
+ }
+ for (j = 0; j < ncolumn; j++) {
+ if (!psysc[i].data[j]) {
+ printf(" %*s |", title_len[j + 2], "-");
+ continue;
+ }
+ if (s->request_type & SD_REQ_GET_SNAME ||
+ s->request_type & SD_REQ_NARGS)
+ printf(" %*d |", title_len[j + 2],
+ *((int *)(psysc[i].data[j])));
+ else
+ printf(" %*s |", title_len[j + 2],
+ (char *)(psysc[i].data[j]));
+ }
+ puts("");
+ N++;
+ }
+ goto out;
+skip_format:
+ for (i = 0; i < sm->size; i++) {
+ if (s->request_type & SD_REQ_GET_SNAME) {
+ if (psysc[i].sys_name == NULL)
+ continue;
+ printf("%d;%s;", N, psysc[i].sys_name);
+ } else {
+ if (psysc[i].sys_num == -1)
+ continue;
+ printf("%d;%d;", N, psysc[i].sys_num);
+ }
+ for (j = 0; j < ncolumn; j++) {
+ if (!psysc[i].data[j]) {
+ printf("%c;", '-');
+ continue;
+ }
+ if (s->request_type & SD_REQ_GET_SNAME ||
+ s->request_type & SD_REQ_NARGS)
+ printf("%d;", *((int *)(psysc[i].data[j])));
+ else
+ printf("%s;", (char *)(psysc[i].data[j]));
+ }
+ puts("");
+ N++;
+ }
+out:
+ /* free, exit */
+ for (i = 0; i < sm->size; i++) {
+ free(psysc[i].data);
+ }
+ free(psysc);
+ free(sm);
+ free(title_len);
+}
diff --git a/tools/asinfo/syscall_interface.h b/tools/asinfo/syscall_interface.h
new file mode 100644
index 000000000..edf815063
--- /dev/null
+++ b/tools/asinfo/syscall_interface.h
@@ -0,0 +1,123 @@
+/*
+ * The syscall_interface.h is purposed to interact with the basic data
+ * structure based on arch_descriptor struct. Mainly this set of methods are
+ * used by syscall_dispatcher.
+ *
+ * Copyright (c) 2017 Edgar A. Kaziakhmedov <edgar.kaziakhmedv@virtuozzo.com>
+ * Copyright (c) 2017-2018 The strace developers.
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef ASINFO_SYSCALL_INTERFACE_H
+#define ASINFO_SYSCALL_INTERFACE_H
+
+#include <limits.h>
+#include <stdbool.h>
+
+#include "arch_interface.h"
+#include "error_interface.h"
+#include "sysent.h"
+
+#define SS_FLAG_EMPTY 0
+#define SS_FLAG_PRINT 1
+
+#define HIDDEN_SYSC INT_MIN
+
+/* Complete element type ‘struct number_set’ */
+typedef unsigned int number_slot_t;
+
+struct number_set {
+ number_slot_t *vec;
+ unsigned int nslots;
+ bool not;
+};
+
+/* To avoid include defs.h */
+extern bool is_number_in_set(unsigned int number, const struct number_set *);
+
+struct arch_wrapper {
+ const struct arch_descriptor *arch;
+ /* Mutable user flags for each syscall */
+ int *flag;
+ int *real_snum;
+ char *a_name;
+};
+
+struct syscall_service {
+ struct arch_wrapper *aws;
+ int narch;
+ /* To choose the format while dumping */
+ int request_type;
+ struct error_service *err;
+};
+
+/* These structures are purposed to make union and enumeration with
+ syscall list */
+struct out_sysc {
+ const char *sys_name;
+ int sys_num;
+ void **data;
+};
+
+struct sysc_meta {
+ struct out_sysc *sysc_list;
+ int size;
+};
+
+struct in_sysc {
+ const char *sys_name;
+ void *data;
+};
+
+#define SYSCALL_LIST_DEFINE(name) \
+ struct syscall_service *(name)
+
+/* base methods
+ ss is related to syscall_service
+ ssa is related to arch_wrapper */
+struct syscall_service *ss_create(struct arch_service *m, int request_type);
+
+int ssa_is_ok(struct syscall_service *s, int arch, int num);
+
+struct error_service *ss_err(struct syscall_service *s);
+
+int ss_size(struct syscall_service *m);
+
+int ssa_max_scn(struct syscall_service *s, int arch);
+
+const struct_sysent *ssa_sysc_list(struct syscall_service *s, int arch);
+
+int ssa_flag(struct syscall_service *s, int arch, int num);
+
+int ssa_set_flag(struct syscall_service *s, int arch, int num, int flag);
+
+int ssa_real_num(struct syscall_service *s, int arch, int num);
+
+int ssa_set_real_num(struct syscall_service *s, int arch, int num,
+ int real_num);
+
+const char *ssa_syscall_name(struct syscall_service *s, int arch, int num);
+
+int ssa_syscall_flag(struct syscall_service *s, int arch, int num);
+
+int ssa_syscall_nargs(struct syscall_service *s, int arch, int num);
+
+int ssa_user_num1(struct syscall_service *s, int arch);
+
+int ssa_user_num2(struct syscall_service *s, int arch);
+
+void ss_free(struct syscall_service *s);
+
+/* calculating methods */
+int ssa_print_size(struct syscall_service *s, int arch);
+
+int ssa_find_snum(struct syscall_service *s, int arch, int real_num);
+
+int ss_mark_matches(struct syscall_service *s, int arch, char *arg);
+
+int ss_update_sc_num(struct syscall_service *s, int arch);
+
+void ss_dump(struct syscall_service *s, int is_raw);
+
+#endif /* !ASINFO_SYSCALL_INTERFACE_H */
diff --git a/tools/asinfo/tests/Makefile.am b/tools/asinfo/tests/Makefile.am
new file mode 100644
index 000000000..8e4c7558f
--- /dev/null
+++ b/tools/asinfo/tests/Makefile.am
@@ -0,0 +1,45 @@
+# Automake input for asinfo tests.
+#
+# Copyright (c) 2017 Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
+# Copyright (c) 2017-2018 The strace developers.
+# All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+OS = linux
+AM_COLOR_TESTS = always
+AM_CFLAGS = $(WARN_CFLAGS)
+AM_CPPFLAGS = $(ARCH_MFLAGS) \
+ -I$(builddir) \
+ -I$(builddir)/.. \
+ -I$(top_builddir) \
+ -I$(top_srcdir)
+AM_LDFLAGS = $(ARCH_MFLAGS)
+
+check_PROGRAMS = set_arch set_mult_arch list_arch set_abi set_mult_abi \
+ get_arch_abi get_sname get_snum com_disp_wrong_keys \
+ multiarch_get_sname multiarch_get_snum format_output
+TESTS = set_arch.test set_mult_arch.test list_arch.test set_abi.test \
+ set_mult_abi.test get_arch_abi.test get_sname.test get_snum.test \
+ com_disp_wrong_keys.test multiarch_get_sname.test \
+ multiarch_get_snum.test format_output.test
+
+set_arch_SOURCES = set_arch.c
+set_mult_arch_SOURCES = set_mult_arch.c
+list_arch_SOURCES = list_arch.c
+set_abi_SOURCES = set_abi.c
+set_mult_abi_SOURCES = set_mult_abi.c
+get_arch_abi_SOURCES = get_arch_abi.c
+get_sname_SOURCES = get_sname.c
+get_snum_SOURCES = get_snum.c
+com_disp_wrong_keys_SOURCES = com_disp_wrong_keys.c
+multiarch_get_sname_SOURCES = multiarch_get_sname.c
+multiarch_get_snum_SOURCES = multiarch_get_snum.c
+format_output_SOURCES = format_output.c
+
+EXTRA_DIST = $(TESTS) init.sh ref_asinfo_output.h
+
+clean-local: clean-local-check
+.PHONY: clean-local-check
+clean-local-check:
+ -rm -rf -- $(TESTS:.test=.dir)
diff --git a/tools/asinfo/tests/com_disp_wrong_keys.c b/tools/asinfo/tests/com_disp_wrong_keys.c
new file mode 100644
index 000000000..9dcb3ee25
--- /dev/null
+++ b/tools/asinfo/tests/com_disp_wrong_keys.c
@@ -0,0 +1,42 @@
+#include <stdio.h>
+#include "ref_asinfo_output.h"
+
+#define TRY_HELP "Try \'../../asinfo -h\' for more information."
+
+int
+main(int argc, char *argv[])
+{
+ puts("../../asinfo: unrecognized option \'--set-ar\'\n"
+ TRY_HELP "\n"
+ "../../asinfo: parameter \'--get-arch\' has been used more than "
+ "once\n"
+ TRY_HELP "\n"
+ "../../asinfo: parameter \'--set-arch\' requires argument\n"
+ TRY_HELP "\n"
+ "../../asinfo: argument \'aarch64,\' of \'--set-arch\' parameter "
+ "has a wrong format\n"
+ TRY_HELP "\n"
+ "../../asinfo: exclusive parameters\n"
+ TRY_HELP "\n"
+ "../../asinfo: wrong parameters\n"
+ TRY_HELP "\n"
+ "../../asinfo: \'--list-arch\' cannot be used with any ABI "
+ "parameters\n"
+ TRY_HELP "\n"
+ "../../asinfo: ABI parameters could be used only with "
+ "architecture parameter\n"
+ TRY_HELP "\n"
+ "../../asinfo: ABI modes cannot be automatically detected for "
+ "multiple architectures\n"
+ TRY_HELP "\n"
+ "../../asinfo: each architecture needs respective ABI mode, "
+ "and vice versa\n"
+ TRY_HELP "\n"
+ "../../asinfo: first set main output syscall characteristics\n"
+ TRY_HELP "\n"
+ "../../asinfo: raw data implies existing data\n"
+ TRY_HELP "\n"
+ "../../asinfo: must have OPTIONS\n"
+ TRY_HELP);
+ return 0;
+}
diff --git a/tools/asinfo/tests/com_disp_wrong_keys.test b/tools/asinfo/tests/com_disp_wrong_keys.test
new file mode 100755
index 000000000..ba725ab5c
--- /dev/null
+++ b/tools/asinfo/tests/com_disp_wrong_keys.test
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+. "${srcdir=.}/init.sh"
+
+run_prog > $EXP
+#Unrecognized option
+run_asinfo --set-ar > $LOG
+#More than once param
+run_asinfo --get-arch --get-arch >> $LOG
+#Requiring argument
+run_asinfo --set-arch >> $LOG
+#Wrong format
+run_asinfo --set-arch aarch64, >> $LOG
+#More than one option in one group
+run_asinfo --get-arch --set-arch arm >> $LOG
+#syscall and list-arch
+run_asinfo --list-arch --get-sname all >> $LOG
+#list-arch and abi params
+run_asinfo --list-arch --list-abi >> $LOG
+#abi params without arch params
+run_asinfo --list-abi >> $LOG
+#auto abi for multiple archs
+run_asinfo --set-arch aarch64,arm >> $LOG
+#auto abi for multiple archs
+run_asinfo --set-arch aarch64,arm --set-abi all,all,all >> $LOG
+#nargs should be used together with other options from syscall group
+run_asinfo --set-arch x86_64 --set-abi x32 --nargs >> $LOG
+#raw check
+run_asinfo --raw >> $LOG
+#empty input
+run_asinfo >> $LOG
+match_diff "$LOG" "$EXP"
diff --git a/tools/asinfo/tests/format_output.c b/tools/asinfo/tests/format_output.c
new file mode 100644
index 000000000..870b8445a
--- /dev/null
+++ b/tools/asinfo/tests/format_output.c
@@ -0,0 +1,17 @@
+#include <stdio.h>
+#include "ref_asinfo_output.h"
+
+int
+main(int argc, char *argv[])
+{
+ puts(
+"| N | Architecture name | ABI mode | IMPL syscalls | IPC IMPL | SOCKET IMPL |\n"
+"| 1 | avr32 | 32bit | 329 | external | external |"
+ );
+ puts(
+"| | | x86_64 | x86_64 | x86_64 |\n"
+"| N | Snum | 64bit | x32 | 32bit |\n"
+"| 1 | 1 | write | write | - |\n"
+"| 2 | 4 | - | - | write |");
+ return 0;
+}
diff --git a/tools/asinfo/tests/format_output.test b/tools/asinfo/tests/format_output.test
new file mode 100755
index 000000000..6addc6e95
--- /dev/null
+++ b/tools/asinfo/tests/format_output.test
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+. "${srcdir=.}/init.sh"
+
+run_prog > $EXP
+run_asinfo --set-arch avr32 --list-abi > $LOG
+run_asinfo --set-arch x86_64 --list-abi --get-snum write >> $LOG
+match_diff "$LOG" "$EXP"
diff --git a/tools/asinfo/tests/get_arch_abi.c b/tools/asinfo/tests/get_arch_abi.c
new file mode 100644
index 000000000..fd6ed675f
--- /dev/null
+++ b/tools/asinfo/tests/get_arch_abi.c
@@ -0,0 +1,174 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/utsname.h>
+
+#include "ref_asinfo_output.h"
+
+static inline void
+print_cannot_detect(char *arch_name)
+{
+ printf("../../asinfo: ABI mode cannot be automatically detected for "
+ "non-target architecture \'%s\'\n", arch_name);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct utsname buf;
+ uname(&buf);
+#if defined(bfin)
+ puts("1" BFIN_32bit_STR);
+ return 0;
+#endif
+#if defined(IA64)
+ puts("1" IA64_64bit_STR);
+ return 0;
+#endif
+#if defined(M68K)
+ puts("1" M68K_32bit_STR);
+#endif
+#if defined(SPARC64)
+ print_cannot_detect(buf.machine);
+ return 0;
+#endif
+#if defined(SPARC)
+ puts("1" SPARC_32bit_STR);
+ return 0;
+#endif
+#if defined(METAG)
+ puts("1" METAG_32bit_STR);
+ return 0;
+#endif
+#if defined(MIPS)
+ if (strstr(buf.machine, "mips64")) {
+ puts(
+#if defined(LINUX_MIPSO32)
+ "1" MIPS64_O32_STR
+#elif defined(LINUX_MIPSN32)
+ "1" MIPS64_N32_STR
+#elif defined(LINUX_MIPSN64)
+ "1" MIPS64_N64_STR
+#endif
+ );
+ return 0;
+ }
+ if (strstr(buf.machine, "mips")) {
+ puts("1" MIPS_O32_STR);
+ return 0;
+ }
+#endif
+#if defined(ALPHA)
+ puts("1" ALPHA_64bit_STR);
+ return 0;
+#endif
+#if defined(POWERPC64LE)
+ puts("1" PPC64LE_64bit_STR);
+ return 0;
+#elif defined(POWERPC64)
+ print_cannot_detect(buf.machine);
+ return 0;
+#elif defined(POWERPC)
+ puts("1" PPC_32bit_STR);
+ return 0;
+#endif
+#if defined(ARM)
+ if (strstr(buf.machine, "arm")) {
+ puts(
+#if defined(__ARM_EABI__) || !defined(ENABLE_ARM_OABI)
+ "1" ARM_eabi_STR
+#else
+ "1" ARM_oabi_STR
+#endif
+ );
+ return 0;
+ }
+#endif
+#if defined(AARCH64)
+ puts(
+#if defined(__ARM_EABI__)
+ "1" AARCH64_eabi_STR
+#else
+ "1" AARCH64_64bit_STR
+#endif
+ );
+ return 0;
+#endif
+#if defined(AVR32)
+ puts("1" AVR32_32bit_STR);
+ return 0;
+#endif
+#if defined(ARC)
+ puts("1" ARC_32bit_STR);
+ return 0;
+#endif
+#if defined(S390)
+ puts("1" S390_32bit_STR);
+ return 0;
+#endif
+#if defined(S390X)
+ puts("1" S390X_64bit_STR);
+ return 0;
+#endif
+#if defined(HPPA)
+ puts("1" PARISC_32bit_STR);
+ return 0;
+#endif
+#if defined(SH64)
+ puts("1" SH64_64bit_STR);
+ return 0;
+#endif
+#if defined(SH)
+ puts("1" SH_32bit_STR);
+ return 0;
+#endif
+#if defined(X86_64) || defined(X32)
+ puts(
+#if defined(X86_64)
+ "1" X86_64_64bit_STR
+#elif defined(X32)
+ "1" X86_64_X32_STR
+#endif
+ );
+ return 0;
+#endif
+#if defined(I386)
+ if (strstr(buf.machine, "64"))
+ puts("1" X86_64_32bit_STR);
+ else
+ puts("1" X86_32bit_STR);
+ return 0;
+#endif
+#if defined(TILE)
+ puts(
+#if defined(__tilepro__)
+ "1" TILE_64bit_STR
+#else
+ "1" TILE_32bit_STR
+#endif
+ );
+#endif
+#if defined(MICROBLAZE)
+ puts("1" MICROBLAZE_32bit_STR);
+ return 0;
+#endif
+#if defined(NIOS2)
+ puts("1" NIOS2_32bit_STR);
+ return 0;
+#endif
+#if defined(OR1K)
+ puts("1" OR1K_32bit_STR);
+ return 0;
+#endif
+#if defined(XTENSA)
+ puts("1" XTENSA_32bit_STR);
+ return 0;
+#endif
+#if defined(RISCV64)
+ print_cannot_detect(buf.machine);
+ return 0;
+#endif
+ printf("../../asinfo: architecture \'%s\' is unsupported\n",
+ buf.machine);
+ return 0;
+}
diff --git a/tools/asinfo/tests/get_arch_abi.test b/tools/asinfo/tests/get_arch_abi.test
new file mode 100755
index 000000000..d320c067d
--- /dev/null
+++ b/tools/asinfo/tests/get_arch_abi.test
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+. "${srcdir=.}/init.sh"
+
+run_prog > $EXP
+run_asinfo --get-arch --raw > "$LOG"
+match_diff "$LOG" "$EXP"
diff --git a/tools/asinfo/tests/get_sname.c b/tools/asinfo/tests/get_sname.c
new file mode 100644
index 000000000..79aae64fa
--- /dev/null
+++ b/tools/asinfo/tests/get_sname.c
@@ -0,0 +1,26 @@
+#include <stdio.h>
+#include "ref_asinfo_output.h"
+
+int
+main(int argc, char *argv[])
+{
+ //--get-sname write
+ puts("1;write;1;\n"
+ //--get-sname /write
+ "1;process_vm_writev;311;\n"
+ "2;pwrite64;18;\n"
+ "3;pwritev;296;\n"
+ "4;pwritev2;328;\n"
+ "5;write;1;\n"
+ "6;writev;20;\n"
+ //--get-sname write,read
+ "1;read;0;\n"
+ "2;write;1;\n"
+ //--get-sname 1
+ "1;write;1;\n"
+ //--get-sname 1000
+ "../../asinfo: invalid system call \'1000\'\n"
+ //--get-sname helloworld
+ "../../asinfo: invalid system call \'helloworld\'");
+ return 0;
+}
diff --git a/tools/asinfo/tests/get_sname.test b/tools/asinfo/tests/get_sname.test
new file mode 100755
index 000000000..2bff806ee
--- /dev/null
+++ b/tools/asinfo/tests/get_sname.test
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+. "${srcdir=.}/init.sh"
+
+run_prog > $EXP
+run_asinfo --set-arch x86_64 --set-abi 64bit --get-sname write --raw > $LOG
+run_asinfo --set-arch x86_64 --set-abi 64bit --get-sname /write --raw >> $LOG
+run_asinfo --set-arch x86_64 --set-abi 64bit --get-sname write,read --raw >> $LOG
+run_asinfo --set-arch x86_64 --set-abi 64bit --get-sname 1 --raw >> $LOG
+run_asinfo --set-arch x86_64 --set-abi 64bit --get-sname 1000 --raw >> $LOG
+run_asinfo --set-arch x86_64 --set-abi 64bit --get-sname helloworld --raw >> $LOG
+match_diff "$LOG" "$EXP"
diff --git a/tools/asinfo/tests/get_snum.c b/tools/asinfo/tests/get_snum.c
new file mode 100644
index 000000000..fded7aa0e
--- /dev/null
+++ b/tools/asinfo/tests/get_snum.c
@@ -0,0 +1,26 @@
+#include <stdio.h>
+#include "ref_asinfo_output.h"
+
+int
+main(int argc, char *argv[])
+{
+ //--get-snum write
+ puts("1;1;write;\n"
+ //--get-snum /write
+ "1;1;write;\n"
+ "2;18;pwrite64;\n"
+ "3;20;writev;\n"
+ "4;296;pwritev;\n"
+ "5;311;process_vm_writev;\n"
+ "6;328;pwritev2;\n"
+ //--get-snum write,read
+ "1;0;read;\n"
+ "2;1;write;\n"
+ //--get-snum 1
+ "1;1;write;\n"
+ //--get-snum 1000
+ "../../asinfo: invalid system call \'1000\'\n"
+ //--get-snum helloworld
+ "../../asinfo: invalid system call \'helloworld\'");
+ return 0;
+}
diff --git a/tools/asinfo/tests/get_snum.test b/tools/asinfo/tests/get_snum.test
new file mode 100755
index 000000000..02dceca4e
--- /dev/null
+++ b/tools/asinfo/tests/get_snum.test
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+. "${srcdir=.}/init.sh"
+
+run_prog > $EXP
+run_asinfo --set-arch x86_64 --set-abi 64bit --get-snum write --raw > $LOG
+run_asinfo --set-arch x86_64 --set-abi 64bit --get-snum /write --raw >> $LOG
+run_asinfo --set-arch x86_64 --set-abi 64bit --get-snum write,read --raw >> $LOG
+run_asinfo --set-arch x86_64 --set-abi 64bit --get-snum 1 --raw >> $LOG
+run_asinfo --set-arch x86_64 --set-abi 64bit --get-snum 1000 --raw >> $LOG
+run_asinfo --set-arch x86_64 --set-abi 64bit --get-snum helloworld --raw >> $LOG
+match_diff "$LOG" "$EXP"
diff --git a/tools/asinfo/tests/init.sh b/tools/asinfo/tests/init.sh
new file mode 100644
index 000000000..02c7d7242
--- /dev/null
+++ b/tools/asinfo/tests/init.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+#
+# Copyright (c) 2011-2018 The strace developers.
+# All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+ME_="${0##*/}"
+LOG="log"
+OUT="out"
+EXP="exp"
+ASINFO="../../asinfo"
+
+fail_() { warn_ "$ME_: failed test: $*"; exit 1; }
+warn_() { printf >&2 '%s\n' "$*"; }
+
+run_prog()
+{
+ if [ $# -eq 0 ]; then
+ set -- "../$NAME"
+ fi
+ args="$*"
+ "$@" || {
+ rc=$?
+ if [ $rc != 0 ]; then
+ fail_ "$args failed with code $rc"
+ fi
+ }
+}
+
+
+dump_log_and_fail_with()
+{
+ cat < "$LOG" >&2
+ fail_ "$*"
+}
+
+run_asinfo()
+{
+ args="$*"
+ $ASINFO "$@" 2>&1
+}
+
+match_diff()
+{
+ local output expected error
+ if [ $# -eq 0 ]; then
+ output="$LOG"
+ else
+ output="$1"; shift
+ fi
+ if [ $# -eq 0 ]; then
+ expected="$srcdir/$NAME.expected"
+ else
+ expected="$1"; shift
+ fi
+ if [ $# -eq 0 ]; then
+ error="$STRACE $args output mismatch"
+ else
+ error="$1"; shift
+ fi
+
+ diff -u -- "$expected" "$output" ||
+ fail_ "$error"
+}
+
+NAME="${ME_%.test}"
+TESTDIR="$NAME.dir"
+rm -rf -- "$TESTDIR"
+mkdir -- "$TESTDIR"
+cd "$TESTDIR"
+case "$srcdir" in
+ /*) ;;
+ *) srcdir="../$srcdir" ;;
+esac
diff --git a/tools/asinfo/tests/list_arch.c b/tools/asinfo/tests/list_arch.c
new file mode 100644
index 000000000..6f703e813
--- /dev/null
+++ b/tools/asinfo/tests/list_arch.c
@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include "ref_asinfo_output.h"
+
+int
+main(int argc, char *argv[])
+{
+ puts("1" AARCH64_64bit_STR"\n"
+ "2" AARCH64_eabi_STR"\n"
+ "3" ALPHA_64bit_STR"\n"
+ "4" ARC_32bit_STR"\n"
+ "5" ARM_oabi_STR"\n"
+ "6" ARM_eabi_STR"\n"
+ "7" AVR32_32bit_STR"\n"
+ "8" BFIN_32bit_STR"\n"
+ "9" IA64_64bit_STR"\n"
+ "10" M68K_32bit_STR"\n"
+ "11" METAG_32bit_STR"\n"
+ "12" MICROBLAZE_32bit_STR"\n"
+ "13" MIPS64_N64_STR"\n"
+ "14" MIPS64_N32_STR"\n"
+ "15" MIPS64_O32_STR"\n"
+ "16" MIPS_O32_STR"\n"
+ "17" NIOS2_32bit_STR"\n"
+ "18" OR1K_32bit_STR"\n"
+ "19" PARISC_32bit_STR"\n"
+ "20" PPC_32bit_STR"\n"
+ "21" PPC64_64bit_STR"\n"
+ "22" PPC64_32bit_STR"\n"
+ "23" PPC64LE_64bit_STR"\n"
+ "24" RISCV64_64bit_STR"\n"
+ "25" S390_32bit_STR"\n"
+ "26" S390X_64bit_STR"\n"
+ "27" SH_32bit_STR"\n"
+ "28" SH64_64bit_STR"\n"
+ "29" SPARC_32bit_STR"\n"
+ "30" SPARC64_64bit_STR"\n"
+ "31" SPARC64_32bit_STR"\n"
+ "32" TILE_64bit_STR"\n"
+ "33" TILE_32bit_STR"\n"
+ "34" TILEPRO_32bit_STR"\n"
+ "35" X86_64_64bit_STR"\n"
+ "36" X86_64_X32_STR"\n"
+ "37" X86_64_32bit_STR"\n"
+ "38" X86_32bit_STR"\n"
+ "39" XTENSA_32bit_STR);
+ return 0;
+}
diff --git a/tools/asinfo/tests/list_arch.test b/tools/asinfo/tests/list_arch.test
new file mode 100755
index 000000000..263d69df3
--- /dev/null
+++ b/tools/asinfo/tests/list_arch.test
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+. "${srcdir=.}/init.sh"
+
+run_prog > $EXP
+run_asinfo --list-arch --raw > $LOG
+match_diff "$LOG" "$EXP"
diff --git a/tools/asinfo/tests/multiarch_get_sname.c b/tools/asinfo/tests/multiarch_get_sname.c
new file mode 100644
index 000000000..703e964c3
--- /dev/null
+++ b/tools/asinfo/tests/multiarch_get_sname.c
@@ -0,0 +1,31 @@
+#include <stdio.h>
+#include "ref_asinfo_output.h"
+
+int
+main(int argc, char *argv[])
+{
+ //--get-sname write
+ puts("1;write;1;1;4;4;\n"
+ //--get-sname /write
+ "1;process_vm_writev;311;540;348;348;\n"
+ "2;process_vm_writev#64;-;311;-;-;\n"
+ "3;pwrite64;18;18;181;181;\n"
+ "4;pwritev;296;535;334;334;\n"
+ "5;pwritev#64;-;296;-;-;\n"
+ "6;pwritev2;328;547;379;379;\n"
+ "7;pwritev2#64;-;328;-;-;\n"
+ "8;write;1;1;4;4;\n"
+ "9;writev;20;516;146;146;\n"
+ "10;writev#64;-;20;-;-;\n"
+ //--get-sname write,read
+ "1;read;0;0;3;3;\n"
+ "2;write;1;1;4;4;\n"
+ //--get-sname 1
+ "1;exit;-;-;1;1;\n"
+ "2;write;1;1;-;-;\n"
+ //--get-sname 1000
+ "../../asinfo: invalid system call \'1000\'\n"
+ //--get-sname helloworld
+ "../../asinfo: invalid system call \'helloworld\'");
+ return 0;
+}
diff --git a/tools/asinfo/tests/multiarch_get_sname.test b/tools/asinfo/tests/multiarch_get_sname.test
new file mode 100755
index 000000000..0ff7bb92a
--- /dev/null
+++ b/tools/asinfo/tests/multiarch_get_sname.test
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+. "${srcdir=.}/init.sh"
+
+run_prog > $EXP
+run_asinfo --set-arch x86_64,x86 --list-abi --get-sname write --raw > $LOG
+run_asinfo --set-arch x86_64,x86 --list-abi --get-sname /write --raw >> $LOG
+run_asinfo --set-arch x86_64,x86 --list-abi --get-sname write,read --raw >> $LOG
+run_asinfo --set-arch x86_64,x86 --list-abi --get-sname 1 --raw >> $LOG
+run_asinfo --set-arch x86_64,x86 --list-abi --get-sname 1000 --raw >> $LOG
+run_asinfo --set-arch x86_64,x86 --list-abi --get-sname helloworld --raw >> $LOG
+match_diff "$LOG" "$EXP"
diff --git a/tools/asinfo/tests/multiarch_get_snum.c b/tools/asinfo/tests/multiarch_get_snum.c
new file mode 100644
index 000000000..d9a53ebb2
--- /dev/null
+++ b/tools/asinfo/tests/multiarch_get_snum.c
@@ -0,0 +1,39 @@
+#include <stdio.h>
+#include "ref_asinfo_output.h"
+
+int
+main(int argc, char *argv[])
+{
+ //--get-snum write
+ puts("1;1;write;write;-;-;\n"
+ "2;4;-;-;write;write;\n"
+ //--get-snum /write
+ "1;1;write;write;-;-;\n"
+ "2;4;-;-;write;write;\n"
+ "3;18;pwrite64;pwrite64;-;-;\n"
+ "4;20;writev;writev#64;-;-;\n"
+ "5;146;-;-;writev;writev;\n"
+ "6;181;-;-;pwrite64;pwrite64;\n"
+ "7;296;pwritev;pwritev#64;-;-;\n"
+ "8;311;process_vm_writev;process_vm_writev#64;-;-;\n"
+ "9;328;pwritev2;pwritev2#64;-;-;\n"
+ "10;334;-;-;pwritev;pwritev;\n"
+ "11;348;-;-;process_vm_writev;process_vm_writev;\n"
+ "12;379;-;-;pwritev2;pwritev2;\n"
+ "13;516;-;writev;-;-;\n"
+ "14;535;-;pwritev;-;-;\n"
+ "15;540;-;process_vm_writev;-;-;\n"
+ "16;547;-;pwritev2;-;-;\n"
+ //--get-snum write,read
+ "1;0;read;read;-;-;\n"
+ "2;1;write;write;-;-;\n"
+ "3;3;-;-;read;read;\n"
+ "4;4;-;-;write;write;\n"
+ //--get-snum 1
+ "1;1;write;write;exit;exit;\n"
+ //--get-snum 1000
+ "../../asinfo: invalid system call \'1000\'\n"
+ //--get-snum helloworld
+ "../../asinfo: invalid system call \'helloworld\'");
+ return 0;
+}
diff --git a/tools/asinfo/tests/multiarch_get_snum.test b/tools/asinfo/tests/multiarch_get_snum.test
new file mode 100755
index 000000000..364903a69
--- /dev/null
+++ b/tools/asinfo/tests/multiarch_get_snum.test
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+. "${srcdir=.}/init.sh"
+
+run_prog > $EXP
+run_asinfo --set-arch x86_64,x86 --list-abi --get-snum write --raw > $LOG
+run_asinfo --set-arch x86_64,x86 --list-abi --get-snum /write --raw >> $LOG
+run_asinfo --set-arch x86_64,x86 --list-abi --get-snum write,read --raw >> $LOG
+run_asinfo --set-arch x86_64,x86 --list-abi --get-snum 1 --raw >> $LOG
+run_asinfo --set-arch x86_64,x86 --list-abi --get-snum 1000 --raw >> $LOG
+run_asinfo --set-arch x86_64,x86 --list-abi --get-snum helloworld --raw >> $LOG
+match_diff "$LOG" "$EXP"
diff --git a/tools/asinfo/tests/ref_asinfo_output.h b/tools/asinfo/tests/ref_asinfo_output.h
new file mode 100644
index 000000000..f9d8c41e8
--- /dev/null
+++ b/tools/asinfo/tests/ref_asinfo_output.h
@@ -0,0 +1,40 @@
+/* Reference output strings for asinfo tool which are necessary for tests */
+#define AARCH64_64bit_STR ";aarch64/arm64;64bit;293;external;external;"
+#define AARCH64_eabi_STR ";aarch64/arm64;eabi;439;external;external;"
+#define ALPHA_64bit_STR ";alpha;64bit;466;external;external;"
+#define ARC_32bit_STR ";arc;32bit;318;external;external;"
+#define ARM_eabi_STR ";arm;eabi;439;external;external;"
+#define ARM_oabi_STR ";arm;oabi;439;int/ext;int/ext;"
+#define AVR32_32bit_STR ";avr32;32bit;329;external;external;"
+#define BFIN_32bit_STR ";blackfin/bfin;32bit;391;external;external;"
+#define IA64_64bit_STR ";ia64;64bit;348;external;external;"
+#define M68K_32bit_STR ";m68k;32bit;427;int/ext;int/ext;"
+#define METAG_32bit_STR ";metag;32bit;317;external;external;"
+#define MICROBLAZE_32bit_STR ";microblaze;32bit;431;external;external;"
+#define MIPS64_N32_STR ";mips64/mips64le;n32;1040;int/ext;int/ext;"
+#define MIPS64_N64_STR ";mips64/mips64le;n64;1016;int/ext;int/ext;"
+#define MIPS64_O32_STR ";mips64/mips64le;o32;1086;int/ext;int/ext;"
+#define MIPS_O32_STR ";mips/mipsle;o32;1086;int/ext;int/ext;"
+#define NIOS2_32bit_STR ";nios2;32bit;314;external;external;"
+#define OR1K_32bit_STR ";openrisc/or1k;32bit;314;external;external;"
+#define PARISC_32bit_STR ";parisc/hppa;32bit;389;external;external;"
+#define PPC64LE_64bit_STR ";ppc64le/powerpc64le;64bit;392;int/ext;int/ext;"
+#define PPC64_32bit_STR ";ppc64/powerpc64;32bit;420;int/ext;int/ext;"
+#define PPC64_64bit_STR ";ppc64/powerpc64;64bit;392;int/ext;int/ext;"
+#define PPC_32bit_STR ";ppc/ppcle/powerpc;32bit;420;int/ext;int/ext;"
+#define RISCV64_64bit_STR ";riscv64;64bit;294;external;external;"
+#define S390X_64bit_STR ";s390x;64bit;358;int/ext;int/ext;"
+#define S390_32bit_STR ";s390;32bit;410;int/ext;int/ext;"
+#define SH64_64bit_STR ";sh64;64bit;396;int/ext;int/ext;"
+#define SH_32bit_STR ";sh;32bit;423;int/ext;int/ext;"
+#define SPARC64_32bit_STR ";sparc64;32bit;408;int/ext;int/ext;"
+#define SPARC64_64bit_STR ";sparc64;64bit;371;int/ext;int/ext;"
+#define SPARC_32bit_STR ";sparc;32bit;408;int/ext;int/ext;"
+#define TILEPRO_32bit_STR ";tilepro;32bit;315;external;external;"
+#define TILE_32bit_STR ";tile/tilegx;32bit;315;external;external;"
+#define TILE_64bit_STR ";tile/tilegx;64bit;295;external;external;"
+#define X86_32bit_STR ";x86/i386/i486/i586/i686;32bit;427;int/ext;int/ext;"
+#define X86_64_32bit_STR ";x86_64/amd64/EM64T;32bit;427;int/ext;int/ext;"
+#define X86_64_64bit_STR ";x86_64/amd64/EM64T;64bit;349;external;external;"
+#define X86_64_X32_STR ";x86_64/amd64/EM64T;x32;385;external;external;"
+#define XTENSA_32bit_STR ";xtensa;32bit;361;external;external;"
diff --git a/tools/asinfo/tests/set_abi.c b/tools/asinfo/tests/set_abi.c
new file mode 100644
index 000000000..b74e0ccb7
--- /dev/null
+++ b/tools/asinfo/tests/set_abi.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+#include "ref_asinfo_output.h"
+
+int
+main(int argc, char *argv[])
+{
+ puts("1" X86_64_64bit_STR);
+ return 0;
+}
diff --git a/tools/asinfo/tests/set_abi.test b/tools/asinfo/tests/set_abi.test
new file mode 100755
index 000000000..70b1f4290
--- /dev/null
+++ b/tools/asinfo/tests/set_abi.test
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+. "${srcdir=.}/init.sh"
+
+run_prog > $EXP
+run_asinfo --set-arch x86_64 --set-abi 64bit --raw > $LOG
+match_diff "$LOG" "$EXP"
diff --git a/tools/asinfo/tests/set_arch.c b/tools/asinfo/tests/set_arch.c
new file mode 100644
index 000000000..05f44b79a
--- /dev/null
+++ b/tools/asinfo/tests/set_arch.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include "ref_asinfo_output.h"
+
+int
+main(int argc, char *argv[])
+{
+ puts("1" X86_64_64bit_STR "\n"
+ "2" X86_64_X32_STR "\n"
+ "3" X86_64_32bit_STR);
+ return 0;
+}
diff --git a/tools/asinfo/tests/set_arch.test b/tools/asinfo/tests/set_arch.test
new file mode 100755
index 000000000..e9a102c6f
--- /dev/null
+++ b/tools/asinfo/tests/set_arch.test
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+. "${srcdir=.}/init.sh"
+
+run_prog > $EXP
+run_asinfo --set-arch x86_64 --list-abi --raw > $LOG
+match_diff "$LOG" "$EXP"
diff --git a/tools/asinfo/tests/set_mult_abi.c b/tools/asinfo/tests/set_mult_abi.c
new file mode 100644
index 000000000..4107fabb4
--- /dev/null
+++ b/tools/asinfo/tests/set_mult_abi.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include "ref_asinfo_output.h"
+
+int
+main(int argc, char *argv[])
+{
+ puts("1" X86_64_64bit_STR "\n"
+ "2" AARCH64_64bit_STR "\n"
+ "3" AARCH64_eabi_STR );
+ return 0;
+}
diff --git a/tools/asinfo/tests/set_mult_abi.test b/tools/asinfo/tests/set_mult_abi.test
new file mode 100755
index 000000000..555c27ca0
--- /dev/null
+++ b/tools/asinfo/tests/set_mult_abi.test
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+. "${srcdir=.}/init.sh"
+
+run_prog > $EXP
+run_asinfo --set-arch x86_64,aarch64 --set-abi 64bit,all --raw >> $LOG
+match_diff "$LOG" "$EXP"
diff --git a/tools/asinfo/tests/set_mult_arch.c b/tools/asinfo/tests/set_mult_arch.c
new file mode 100644
index 000000000..ed8d66df3
--- /dev/null
+++ b/tools/asinfo/tests/set_mult_arch.c
@@ -0,0 +1,12 @@
+#include <stdio.h>
+#include "ref_asinfo_output.h"
+
+int
+main(int argc, char *argv[])
+{
+ puts("1" AARCH64_64bit_STR "\n"
+ "2" AARCH64_eabi_STR "\n"
+ "3" ARM_oabi_STR "\n"
+ "4" ARM_eabi_STR );
+ return 0;
+}
diff --git a/tools/asinfo/tests/set_mult_arch.test b/tools/asinfo/tests/set_mult_arch.test
new file mode 100755
index 000000000..d0152d606
--- /dev/null
+++ b/tools/asinfo/tests/set_mult_arch.test
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+. "${srcdir=.}/init.sh"
+
+run_prog > $EXP
+run_asinfo --set-arch aarch64,arm --list-abi --raw >> $LOG
+match_diff "$LOG" "$EXP"