diff --git a/Makefile.am b/Makefile.am index 20cbd285..7274cfa2 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 b81507fe..e8d72086 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. + * Improvements * Updated lists of BPF_*, BTRFS_*, FAN_*, IFLA_*, KERN_*, KVM_CAP_*, NDA_*, NETNSA_*, NT_*, PR_*, REL_*, SECCOMP_*, SCTP_*, UDP_*, V4L2_*, and *_MAGIC diff --git a/bootstrap b/bootstrap index 290d819a..c71c3358 100755 --- a/bootstrap +++ b/bootstrap @@ -10,6 +10,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 tests=tests-$m diff --git a/configure.ac b/configure.ac index 9bf64c33..a6b10e1d 100644 --- a/configure.ac +++ b/configure.ac @@ -18,6 +18,7 @@ AC_INIT([strace], [https://strace.io]) m4_define([copyright_year], m4_esyscmd([./copyright-year-gen .year])) m4_define([manpage_date], m4_esyscmd([./file-date-gen strace.1.in])) +m4_define([asinfo_manpage_date], m4_esyscmd([./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([.]) @@ -48,6 +49,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_MSG_CHECKING([for supported architecture]) arch_m32= arch_mx32= @@ -922,6 +926,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 38e13a13..3fe15ec4 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 30b0a6b0..1e2401bb 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 9fb376b3..706650b8 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 5a973f34..c025c066 100644 --- a/strace.spec.in +++ b/strace.spec.in @@ -33,7 +33,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. @@ -86,6 +88,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 00000000..16643e12 --- /dev/null +++ b/tools/Makefile.am @@ -0,0 +1,9 @@ +# Automake input for strace tools. +# +# Copyright (c) 2017 Edgar A. Kaziakhmedov +# 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 00000000..544c3781 --- /dev/null +++ b/tools/asinfo/Makefile.am @@ -0,0 +1,56 @@ +# Automake input for asinfo. +# +# Copyright (c) 2017 Edgar Kaziakhmedov +# 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 diff --git a/tools/asinfo/README-arch b/tools/asinfo/README-arch new file mode 100644 index 00000000..4ef760ef --- /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 00000000..f931ea09 --- /dev/null +++ b/tools/asinfo/arch_definitions.h @@ -0,0 +1,66 @@ +/* [],[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"}) ), +/* [],[sparc64/syscallent.h],[0,0] */ +ARCH_DESC_DEFINE(sparc64, 64bit, PASS({ARCH_sparc_32bit}), PASS({"sparc64"}) ), +/* [],[sparc/syscallent.h],[0,0] */ +ARCH_DESC_DEFINE(sparc, 32bit, PASS({}), PASS({"sparc"}) ), +/* [],[metag/syscallent.h],[0,0] */ +ARCH_DESC_DEFINE(metag, 32bit, PASS({}), PASS({"metag"}) ), +/* [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"}) ), +/* [],[alpha/syscallent.h],[0,0] */ +ARCH_DESC_DEFINE(alpha, 64bit, PASS({}), PASS({"alpha"}) ), +/* [],[powerpc64/syscallent.h],[0,0] */ +ARCH_DESC_DEFINE(ppc64, 64bit, PASS({ARCH_ppc_32bit}), PASS({"ppc64", "ppc64le", "powerpc64"}) ), +/* [],[powerpc/syscallent.h],[0,0] */ +ARCH_DESC_DEFINE(ppc, 32bit, PASS({}), PASS({"ppc", "ppcle", "powerpc"}) ), +/* [],[aarch64/syscallent.h],[0,0] */ +ARCH_DESC_DEFINE(aarch64, 64bit, PASS({ARCH_arm_eabi}), PASS({"aarch64", "arm64"}) ), +/* [!__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"}) ), +/* [],[arc/syscallent.h],[0,0] */ +ARCH_DESC_DEFINE(arc, 32bit, PASS({}), PASS({"arc"}) ), +/* [],[s390x/syscallent.h],[0,0] */ +ARCH_DESC_DEFINE(s390x, 64bit, PASS({}), PASS({"s390x"}) ), +/* [],[s390/syscallent.h],[0,0] */ +ARCH_DESC_DEFINE(s390, 32bit, PASS({}), PASS({"s390"}) ), +/* [],[hppa/syscallent.h],[0,0] */ +ARCH_DESC_DEFINE(hppa, 32bit, PASS({}), PASS({"parisc", "hppa"}) ), +/* [],[sh64/syscallent.h],[0,0] */ +ARCH_DESC_DEFINE(sh64, 64bit, PASS({}), PASS({"sh64"}) ), +/* [],[sh/syscallent.h],[0,0] */ +ARCH_DESC_DEFINE(sh, 32bit, PASS({}), PASS({"sh"}) ), +/* [],[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"}) ), +/* [],[tile/syscallent.h],[0,0] */ +ARCH_DESC_DEFINE(tile, 64bit, PASS({ARCH_tile_32bit}), PASS({"tile", "tilegx"}) ), +/* [],[tile/syscallent1.h],[0,0] */ +ARCH_DESC_DEFINE(tile, 32bit, PASS({}), PASS({"tilepro"}) ), +/* [],[microblaze/syscallent.h],[0,0] */ +ARCH_DESC_DEFINE(microblaze, 32bit, PASS({}), PASS({"microblaze"}) ), +/* [],[nios2/syscallent.h],[0,0] */ +ARCH_DESC_DEFINE(nios2, 32bit, PASS({}), PASS({"nios2"}) ), +/* [],[or1k/syscallent.h],[0,0] */ +ARCH_DESC_DEFINE(openrisc, 32bit, PASS({}), PASS({"openrisc", "or1k"}) ), +/* [],[xtensa/syscallent.h],[0,0] */ +ARCH_DESC_DEFINE(xtensa, 32bit, PASS({}), PASS({"xtensa"}) ), +/* [],[riscv/syscallent.h],[0,0] */ +ARCH_DESC_DEFINE(riscv, 64bit, PASS({ARCH_riscv_32bit}), PASS({"riscv"}) ), +/* [],[riscv/syscallent1.h],[0,0] */ +ARCH_DESC_DEFINE(riscv, 32bit, PASS({}), PASS({}) ) diff --git a/tools/asinfo/arch_interface.c b/tools/asinfo/arch_interface.c new file mode 100644 index 00000000..85727de5 --- /dev/null +++ b/tools/asinfo/arch_interface.c @@ -0,0 +1,635 @@ +/* + * Copyright (c) 2017 Edgar A. Kaziakhmedov + * 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 +#include +#include + +#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 i = 0; + int count = 0; + + if (elem == NULL) + return -1; + for (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]; +} + +enum impl_type +al_ipc_syscall(struct arch_service *m, unsigned index) +{ + const struct arch_descriptor *elem = al_get(m, index); + int i; + enum impl_type impl_buf = IMPL_int; + + for (i = 0; i < elem->max_scn; i++) { + if (elem->syscall_list[i].sys_name == NULL) + continue; + /* It is enough to find just semop sybcall */ + if (!strcmp(elem->syscall_list[i].sys_name, "semop")) { + if (!(elem->syscall_list[i].sys_flags & + TRACE_INDIRECT_SUBCALL)) + impl_buf = IMPL_ext; + else if (impl_buf == IMPL_ext) + impl_buf = IMPL_int_ext; + else + impl_buf = IMPL_int; + } + } + return impl_buf; +} + +enum impl_type +al_sck_syscall(struct arch_service *m, unsigned index) +{ + const struct arch_descriptor *elem = al_get(m, index); + int i; + enum impl_type impl_buf = IMPL_int; + + for (i = 0; i < elem->max_scn; i++) { + if (elem->syscall_list[i].sys_name == NULL) + continue; + /* It is enough to find just socket sybcall */ + if (!strcmp(elem->syscall_list[i].sys_name, "socket")) { + if (!(elem->syscall_list[i].sys_flags & + TRACE_INDIRECT_SUBCALL)) + impl_buf = IMPL_ext; + else if (impl_buf == IMPL_ext) + impl_buf = IMPL_int_ext; + else + impl_buf = IMPL_int; + } + } + return impl_buf; +} + +/* 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[] = { + "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_ipc_syscall(m, i)], + impl_st[al_ipc_syscall(m, i)]); + 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_ipc_syscall(m, i)]); + printf("%*s |\n", title_len[5], impl_st[al_sck_syscall(m, i)]); + } + free(whole_arch_name); +} diff --git a/tools/asinfo/arch_interface.h b/tools/asinfo/arch_interface.h new file mode 100644 index 00000000..b2b0b9aa --- /dev/null +++ b/tools/asinfo/arch_interface.h @@ -0,0 +1,143 @@ +/* + * 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 + * 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_ext, + IMPL_int, + IMPL_int_ext +}; + +/* 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 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 00000000..63175710 --- /dev/null +++ b/tools/asinfo/asinfo.1.in @@ -0,0 +1,264 @@ +.\" Copyright (c) 2017 Edgar Kaziakhmedov +.\" 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 . + +.SH "SEE ALSO" +.BR strace (1) diff --git a/tools/asinfo/asinfo.c b/tools/asinfo/asinfo.c new file mode 100644 index 00000000..44f11a3b --- /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 + * 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 +#include +#include +#include +#include +#include +#include +#include + +#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 00000000..a599b654 --- /dev/null +++ b/tools/asinfo/dispatchers.c @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2017 Edgar A. Kaziakhmedov + * 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 +#include +#include +#include + +#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(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 00000000..ec406c0c --- /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 + * 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 00000000..b896c4d1 --- /dev/null +++ b/tools/asinfo/error_interface.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017 Edgar A. Kaziakhmedov + * 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 +#include +#include + +#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 00000000..be0a47f4 --- /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 + * 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 00000000..634cf570 --- /dev/null +++ b/tools/asinfo/gen_asinfo_files.sh @@ -0,0 +1,152 @@ +#!/bin/sh +# +# Code generator simplifies addition of new architecture to asinfo tool +# +# Copyright (c) 2017 Edgar A. Kaziakhmedov +# 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" + ) >> "${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 00000000..c6dbd7ef --- /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 + * 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 00000000..b3efaa44 --- /dev/null +++ b/tools/asinfo/syscall_interface.c @@ -0,0 +1,672 @@ +/* + * Copyright (c) 2017 Edgar A. Kaziakhmedov + * 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 +#include +#include +#include + +#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 || + 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 00000000..edf81506 --- /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 + * 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 +#include + +#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 00000000..8e4c7558 --- /dev/null +++ b/tools/asinfo/tests/Makefile.am @@ -0,0 +1,45 @@ +# Automake input for asinfo tests. +# +# Copyright (c) 2017 Edgar Kaziakhmedov +# 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 00000000..9dcb3ee2 --- /dev/null +++ b/tools/asinfo/tests/com_disp_wrong_keys.c @@ -0,0 +1,42 @@ +#include +#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 00000000..ba725ab5 --- /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 00000000..870b8445 --- /dev/null +++ b/tools/asinfo/tests/format_output.c @@ -0,0 +1,17 @@ +#include +#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 00000000..6addc6e9 --- /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 00000000..6ce16e26 --- /dev/null +++ b/tools/asinfo/tests/get_arch_abi.c @@ -0,0 +1,172 @@ +#include "config.h" +#include +#include +#include + +#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(POWERPC64) + print_cannot_detect(buf.machine); + return 0; +#endif +#if 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(RISCV) + 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 00000000..d320c067 --- /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 00000000..79aae64f --- /dev/null +++ b/tools/asinfo/tests/get_sname.c @@ -0,0 +1,26 @@ +#include +#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 00000000..2bff806e --- /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 00000000..fded7aa0 --- /dev/null +++ b/tools/asinfo/tests/get_snum.c @@ -0,0 +1,26 @@ +#include +#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 00000000..02dceca4 --- /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 00000000..02c7d724 --- /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 00000000..c1865bc6 --- /dev/null +++ b/tools/asinfo/tests/list_arch.c @@ -0,0 +1,47 @@ +#include +#include "ref_asinfo_output.h" + +int +main(int argc, char *argv[]) +{ + puts("1" BFIN_32bit_STR"\n" + "2" IA64_64bit_STR"\n" + "3" M68K_32bit_STR"\n" + "4" SPARC64_64bit_STR"\n" + "5" SPARC64_32bit_STR"\n" + "6" SPARC_32bit_STR"\n" + "7" METAG_32bit_STR"\n" + "8" MIPS64_N64_STR"\n" + "9" MIPS64_N32_STR"\n" + "10" MIPS64_O32_STR"\n" + "11" MIPS_O32_STR"\n" + "12" ALPHA_64bit_STR"\n" + "13" PPC64_64bit_STR"\n" + "14" PPC64_32bit_STR"\n" + "15" PPC_32bit_STR"\n" + "16" AARCH64_64bit_STR"\n" + "17" AARCH64_eabi_STR"\n" + "18" ARM_oabi_STR"\n" + "19" ARM_eabi_STR"\n" + "20" AVR32_32bit_STR"\n" + "21" ARC_32bit_STR"\n" + "22" S390X_64bit_STR"\n" + "23" S390_32bit_STR"\n" + "24" PARISC_32bit_STR"\n" + "25" SH64_64bit_STR"\n" + "26" SH_32bit_STR"\n" + "27" X86_64_64bit_STR"\n" + "28" X86_64_X32_STR"\n" + "29" X86_64_32bit_STR"\n" + "30" X86_32bit_STR"\n" + "31" TILE_64bit_STR"\n" + "32" TILE_32bit_STR"\n" + "33" TILEPRO_32bit_STR"\n" + "34" MICROBLAZE_32bit_STR"\n" + "35" NIOS2_32bit_STR"\n" + "36" OR1K_32bit_STR"\n" + "37" XTENSA_32bit_STR"\n" + "38" RISCV_64bit_STR"\n" + "39" RISCV_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 00000000..263d69df --- /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 00000000..703e964c --- /dev/null +++ b/tools/asinfo/tests/multiarch_get_sname.c @@ -0,0 +1,31 @@ +#include +#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 00000000..0ff7bb92 --- /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 00000000..d9a53ebb --- /dev/null +++ b/tools/asinfo/tests/multiarch_get_snum.c @@ -0,0 +1,39 @@ +#include +#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 00000000..364903a6 --- /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 00000000..e4680794 --- /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 BFIN_32bit_STR ";blackfin/bfin;32bit;391;external;external;" +#define IA64_64bit_STR ";ia64;64bit;326;external;external;" +#define M68K_32bit_STR ";m68k;32bit;378;internal;internal;" +#define SPARC64_64bit_STR ";sparc64;64bit;341;internal;internal;" +#define SPARC64_32bit_STR ";sparc64;32bit;359;internal;internal;" +#define SPARC_32bit_STR ";sparc;32bit;359;internal;internal;" +#define METAG_32bit_STR ";metag;32bit;281;external;external;" +#define MIPS64_N64_STR ";mips64/mips64le;n64;1002;int/ext;int/ext;" +#define MIPS64_N32_STR ";mips64/mips64le;n32;1006;int/ext;int/ext;" +#define MIPS64_O32_STR ";mips64/mips64le;o32;1042;internal;internal;" +#define MIPS_O32_STR ";mips/mipsle;o32;1042;internal;internal;" +#define ALPHA_64bit_STR ";alpha;64bit;442;external;external;" +#define PPC64_64bit_STR ";ppc64/ppc64le/powerpc64;64bit;379;int/ext;int/ext;" +#define PPC64_32bit_STR ";ppc64/ppc64le/powerpc64;32bit;388;int/ext;int/ext;" +#define PPC_32bit_STR ";ppc/ppcle/powerpc;32bit;388;int/ext;int/ext;" +#define AARCH64_64bit_STR ";aarch64/arm64;64bit;333;external;external;" +#define AARCH64_eabi_STR ";aarch64/arm64;eabi;403;external;external;" +#define ARM_oabi_STR ";arm;oabi;403;int/ext;int/ext;" +#define ARM_eabi_STR ";arm;eabi;403;external;external;" +#define AVR32_32bit_STR ";avr32;32bit;329;external;external;" +#define ARC_32bit_STR ";arc;32bit;282;external;external;" +#define S390X_64bit_STR ";s390x;64bit;330;internal;internal;" +#define S390_32bit_STR ";s390;32bit;363;internal;internal;" +#define PARISC_32bit_STR ";parisc/hppa;32bit;350;external;external;" +#define SH64_64bit_STR ";sh64;64bit;382;int/ext;int/ext;" +#define SH_32bit_STR ";sh;32bit;374;internal;internal;" +#define X86_64_64bit_STR ";x86_64/amd64/EM64T;64bit;335;external;external;" +#define X86_64_X32_STR ";x86_64/amd64/EM64T;x32;371;external;external;" +#define X86_64_32bit_STR ";x86_64/amd64/EM64T;32bit;383;internal;internal;" +#define X86_32bit_STR ";x86/i386/i486/i586/i686;32bit;383;internal;internal;" +#define TILE_64bit_STR ";tile/tilegx;64bit;279;external;external;" +#define TILE_32bit_STR ";tile/tilegx;32bit;279;external;external;" +#define TILEPRO_32bit_STR ";tilepro;32bit;279;external;external;" +#define MICROBLAZE_32bit_STR ";microblaze;32bit;397;external;external;" +#define NIOS2_32bit_STR ";nios2;32bit;278;external;external;" +#define OR1K_32bit_STR ";openrisc/or1k;32bit;278;external;external;" +#define XTENSA_32bit_STR ";xtensa;32bit;326;external;external;" +#define RISCV_64bit_STR ";riscv;64bit;278;external;external;" +#define RISCV_32bit_STR ";riscv;32bit;277;external;external;" diff --git a/tools/asinfo/tests/set_abi.c b/tools/asinfo/tests/set_abi.c new file mode 100644 index 00000000..b74e0ccb --- /dev/null +++ b/tools/asinfo/tests/set_abi.c @@ -0,0 +1,9 @@ +#include +#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 00000000..70b1f429 --- /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 00000000..05f44b79 --- /dev/null +++ b/tools/asinfo/tests/set_arch.c @@ -0,0 +1,11 @@ +#include +#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 00000000..e9a102c6 --- /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 00000000..4107fabb --- /dev/null +++ b/tools/asinfo/tests/set_mult_abi.c @@ -0,0 +1,11 @@ +#include +#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 00000000..555c27ca --- /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 00000000..ed8d66df --- /dev/null +++ b/tools/asinfo/tests/set_mult_arch.c @@ -0,0 +1,12 @@ +#include +#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 00000000..d0152d60 --- /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"