diff --git a/syscall/.gitignore b/syscall/.gitignore index eaefd7c784..70bb8f7f04 100644 --- a/syscall/.gitignore +++ b/syscall/.gitignore @@ -1 +1,2 @@ /.context +/*.ldcmd diff --git a/syscall/Makefile b/syscall/Makefile index a99761c679..c8ed9fab3e 100644 --- a/syscall/Makefile +++ b/syscall/Makefile @@ -37,18 +37,27 @@ include $(TOPDIR)/Make.defs include proxies/Make.defs include stubs/Make.defs +include wraps/Make.defs MKSYSCALL = "$(TOPDIR)$(DELIM)tools$(DELIM)mksyscall$(HOSTEXEEXT)" CSVFILE = "$(TOPDIR)$(DELIM)syscall$(DELIM)syscall.csv" +ifeq ($(CONFIG_SCHED_INSTRUMENTATION_SYSCALL),y) +ifeq ($(CONFIG_LIB_SYSCALL),y) +PROXY_SRCS += syscall_names.c +else +WRAP_SRCS += syscall_names.c +endif +endif STUB_SRCS += syscall_stublookup.c AOBJS = $(ASRCS:.S=$(OBJEXT)) PROXY_OBJS = $(PROXY_SRCS:.c=$(OBJEXT)) STUB_OBJS = $(STUB_SRCS:.c=$(OBJEXT)) +WRAP_OBJS = $(WRAP_SRCS:.c=$(OBJEXT)) -CSRCS = $(PROXY_SRCS) $(STUB_SRCS) +CSRCS = $(PROXY_SRCS) $(STUB_SRCS) $(WRAP_SRCS) COBJS = $(CSRCS:.c=$(OBJEXT)) SRCS = $(ASRCS) $(CSRCS) @@ -56,12 +65,15 @@ OBJS = $(AOBJS) $(COBJS) PROXYDEPPATH = --dep-path proxies STUBDEPPATH = --dep-path stubs -VPATH = proxies:stubs +WRAPDEPPATH = --dep-path wraps +VPATH = proxies:stubs:wraps BIN1 = libproxies$(LIBEXT) BIN2 = libstubs$(LIBEXT) +BIN3 = libwraps$(LIBEXT) +SYSCALLWRAPS = syscall_wraps.ldcmd -all: $(BIN1) $(BIN2) +all: $(BIN1) $(BIN2) $(BIN3) $(SYSCALLWRAPS) .PHONY: context depend clean distclean $(AOBJS): %$(OBJEXT): %.S @@ -76,8 +88,13 @@ $(BIN1): $(PROXY_OBJS) $(BIN2): $(STUB_OBJS) $(call ARCHIVE, $@, $(STUB_OBJS)) +$(BIN3): $(WRAP_OBJS) + $(call ARCHIVE, $@, $(WRAP_OBJS)) + +$(SYSCALLWRAPS): .context + .depend: Makefile $(SRCS) - $(Q) $(MKDEP) $(PROXYDEPPATH) $(STUBDEPPATH) \ + $(Q) $(MKDEP) $(PROXYDEPPATH) $(STUBDEPPATH) $(WRAPDEPPATH) \ "$(CC)" -- $(CFLAGS) -- $(SRCS) >Make.dep $(Q) touch $@ @@ -85,8 +102,15 @@ depend: .depend .context: syscall.csv $(Q) $(MAKE) -C $(TOPDIR)$(DELIM)tools -f Makefile.host mksyscall +ifeq ($(CONFIG_LIB_SYSCALL),y) $(Q) (cd proxies; $(MKSYSCALL) -p $(CSVFILE);) $(Q) (cd stubs; $(MKSYSCALL) -s $(CSVFILE);) +endif +ifeq ($(CONFIG_SCHED_INSTRUMENTATION_SYSCALL),y) + $(Q) (cd wraps; $(MKSYSCALL) -w $(CSVFILE);) + $(Q) $(CPP) $(CPPFLAGS) $(SYSCALLWRAPS:.ldcmd=.h) | \ + sed -e '1,/WRAPOPTSTARTS/d' -e '/^#/d' > $(SYSCALLWRAPS) +endif $(Q) touch $@ context: .context @@ -94,9 +118,11 @@ context: .context clean: $(call DELFILE, $(BIN1)) $(call DELFILE, $(BIN2)) + $(call DELFILE, $(BIN3)) ifneq ($(OBJEXT),) $(call DELFILE, proxies$(DELIM)*$(OBJEXT)) $(call DELFILE, stubs$(DELIM)*$(OBJEXT)) + $(call DELFILE, wraps$(DELIM)*$(OBJEXT)) endif $(call CLEAN) @@ -106,5 +132,7 @@ distclean: clean $(call DELFILE, .depend) $(call DELFILE, proxies$(DELIM)*.c) $(call DELFILE, stubs$(DELIM)*.c) + $(call DELFILE, wraps$(DELIM)*.c) + $(call DELFILE, $(SYSCALLWRAPS)) -include Make.dep diff --git a/syscall/syscall_names.c b/syscall/syscall_names.c new file mode 100644 index 0000000000..b4005fe638 --- /dev/null +++ b/syscall/syscall_names.c @@ -0,0 +1,48 @@ +/**************************************************************************** + * syscall/syscall_names.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#ifndef CONFIG_LIB_SYSCALL +#define CONFIG_LIB_SYSCALL +#endif +#include + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* System call name tables. This table is indexed by the system call numbers + * defined above. Given the system call number, this table provides the + * name of the corresponding system function. + */ + +const char *g_funcnames[SYS_nsyscalls] = +{ +# define SYSCALL_LOOKUP1(f,n) #f +# define SYSCALL_LOOKUP(f,n) , #f +# include +# undef SYSCALL_LOOKUP1 +# undef SYSCALL_LOOKUP +}; diff --git a/syscall/syscall_wraps.h b/syscall/syscall_wraps.h new file mode 100644 index 0000000000..0bc130ee6c --- /dev/null +++ b/syscall/syscall_wraps.h @@ -0,0 +1,37 @@ +/**************************************************************************** + * syscall/syscall_wraps.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#ifndef UP_WRAPOPT +#define UP_WRAPOPT(f) --wrap f +#endif + +#define SYSCALL_LOOKUP1(f,n) UP_WRAPOPT(f) +#define SYSCALL_LOOKUP(f,n) UP_WRAPOPT(f) + +WRAPOPTSTARTS + +#include diff --git a/syscall/wraps/.gitignore b/syscall/wraps/.gitignore new file mode 100644 index 0000000000..b5a455aa98 --- /dev/null +++ b/syscall/wraps/.gitignore @@ -0,0 +1 @@ +/*.c diff --git a/syscall/wraps/Make.defs b/syscall/wraps/Make.defs new file mode 100644 index 0000000000..9643b58779 --- /dev/null +++ b/syscall/wraps/Make.defs @@ -0,0 +1,21 @@ +############################################################################ +# syscall/wraps/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +WRAP_SRCS := ${shell cd wraps; ls *.c 2>/dev/null } diff --git a/tools/Directories.mk b/tools/Directories.mk index f63fb03013..f6c0602947 100644 --- a/tools/Directories.mk +++ b/tools/Directories.mk @@ -99,8 +99,13 @@ ifeq ($(CONFIG_LIB_SYSCALL),y) CONTEXTDIRS += syscall USERDEPDIRS += syscall else +ifeq ($(CONFIG_SCHED_INSTRUMENTATION_SYSCALL),y) +CONTEXTDIRS += syscall +USERDEPDIRS += syscall +else CLEANDIRS += syscall endif +endif ifeq ($(CONFIG_LIB_ZONEINFO_ROMFS),y) CONTEXTDIRS += libs$(DELIM)libc diff --git a/tools/FlatLibs.mk b/tools/FlatLibs.mk index 5e115ae253..657481fe2a 100644 --- a/tools/FlatLibs.mk +++ b/tools/FlatLibs.mk @@ -62,6 +62,12 @@ NUTTXLIBS += staging$(DELIM)libstubs$(LIBEXT) USERLIBS += staging$(DELIM)libproxies$(LIBEXT) endif +# Add library for system call instrumentation if needed + +ifeq ($(CONFIG_SCHED_INSTRUMENTATION_SYSCALL),y) +NUTTXLIBS += staging$(DELIM)libwraps$(LIBEXT) +endif + # Add libraries for two pass build support. The special directory pass1 # may be populated so that application generated logic can be included into # the kernel build diff --git a/tools/KernelLibs.mk b/tools/KernelLibs.mk index 93c4b127a7..e50da99f08 100644 --- a/tools/KernelLibs.mk +++ b/tools/KernelLibs.mk @@ -59,6 +59,12 @@ NUTTXLIBS += staging$(DELIM)libkmm$(LIBEXT) staging$(DELIM)libkarch$(LIBEXT) USERLIBS += staging$(DELIM)libproxies$(LIBEXT) staging$(DELIM)libc$(LIBEXT) USERLIBS += staging$(DELIM)libmm$(LIBEXT) staging$(DELIM)libarch$(LIBEXT) +# Add library for system call instrumentation if needed + +ifeq ($(CONFIG_SCHED_INSTRUMENTATION_SYSCALL),y) +NUTTXLIBS += staging$(DELIM)libwraps$(LIBEXT) +endif + # Add libraries for C++ support. CXX, CXXFLAGS, and COMPILEXX must # be defined in Make.defs for this to work! diff --git a/tools/LibTargets.mk b/tools/LibTargets.mk index 39c83996ab..dc15531c1f 100644 --- a/tools/LibTargets.mk +++ b/tools/LibTargets.mk @@ -148,6 +148,12 @@ syscall$(DELIM)libstubs$(LIBEXT): pass2dep staging$(DELIM)libstubs$(LIBEXT): syscall$(DELIM)libstubs$(LIBEXT) $(Q) $(call INSTALL_LIB,$<,$@) +syscall$(DELIM)libwraps$(LIBEXT): pass2dep + $(Q) $(MAKE) -C syscall TOPDIR="$(TOPDIR)" libwraps$(LIBEXT) EXTRAFLAGS="$(KDEFINE) $(EXTRAFLAGS)" + +staging$(DELIM)libwraps$(LIBEXT): syscall$(DELIM)libwraps$(LIBEXT) + $(Q) $(call INSTALL_LIB,$<,$@) + # Special case ifeq ($(CONFIG_BUILD_FLAT),y) diff --git a/tools/ProtectedLibs.mk b/tools/ProtectedLibs.mk index 816bd7d041..306b53efe7 100644 --- a/tools/ProtectedLibs.mk +++ b/tools/ProtectedLibs.mk @@ -60,6 +60,12 @@ NUTTXLIBS += staging$(DELIM)libkmm$(LIBEXT) staging$(DELIM)libkarch$(LIBEXT) USERLIBS += staging$(DELIM)libproxies$(LIBEXT) staging$(DELIM)libc$(LIBEXT) USERLIBS += staging$(DELIM)libmm$(LIBEXT) staging$(DELIM)libarch$(LIBEXT) +# Add library for system call instrumentation if needed + +ifeq ($(CONFIG_SCHED_INSTRUMENTATION_SYSCALL),y) +NUTTXLIBS += staging$(DELIM)libwraps$(LIBEXT) +endif + # Add libraries for two pass build support. The special directory pass1 # may be populated so that application generated logic can be included into # the kernel build diff --git a/tools/mksyscall.c b/tools/mksyscall.c index 4ad0b874f1..21fe29d768 100644 --- a/tools/mksyscall.c +++ b/tools/mksyscall.c @@ -520,6 +520,304 @@ static void generate_stub(int nfixed, int nparms) stub_close(stream); } +static FILE *open_wrapper(void) +{ + char filename[MAX_PARMSIZE + 8]; + FILE *stream; + + snprintf(filename, MAX_PARMSIZE + 7, "WRAP_%s.c", g_parm[NAME_INDEX]); + filename[MAX_PARMSIZE + 7] = '\0'; + + stream = fopen(filename, "w"); + + if (stream == NULL) + { + fprintf(stderr, "Failed to open %s: %s\n", filename, strerror(errno)); + exit(10); + } + + return stream; +} + +static void generate_wrapper(int nfixed, int nparms) +{ + FILE *stream = open_wrapper(); + char formal[MAX_PARMSIZE]; + char actual[MAX_PARMSIZE]; + char fieldname[MAX_PARMSIZE]; + int i = 0; + + /* Generate "up-front" information, include correct header files */ + + fprintf(stream, "/* Auto-generated %s wrap file -- do not edit */\n\n", + g_parm[NAME_INDEX]); + fprintf(stream, "#include \n"); + fprintf(stream, "#include \n"); + fprintf(stream, "#include \n"); + + /* Suppress "'noreturn' function does return" warnings. */ + + fprintf(stream, "#include \n"); + fprintf(stream, "#undef noreturn_function\n"); + fprintf(stream, "#define noreturn_function\n"); + + /* CONFIG_LIB_SYSCALL must be defined to get syscall number */ + + fprintf(stream, "#undef CONFIG_LIB_SYSCALL\n"); + fprintf(stream, "#define CONFIG_LIB_SYSCALL\n"); + fprintf(stream, "#include \n"); + + /* Does this function have a variable number of parameters? If so then the + * final parameter type will be encoded as "..." + */ + + if (nfixed != nparms) + { + fprintf(stream, "#include \n"); + } + + if (g_parm[HEADER_INDEX] && strlen(g_parm[HEADER_INDEX]) > 0) + { + fprintf(stream, "#include <%s>\n", g_parm[HEADER_INDEX]); + } + + /* Define macros to get wrapper symbol */ + + fprintf(stream, "#include \n"); + fprintf(stream, "#ifndef UP_WRAPSYM\n"); + fprintf(stream, "#define UP_WRAPSYM(s) __wrap_##s\n"); + fprintf(stream, "#endif\n"); + fprintf(stream, "#ifndef UP_REALSYM\n"); + fprintf(stream, "#define UP_REALSYM(s) __real_##s\n"); + fprintf(stream, "#endif\n\n"); + + if (g_parm[COND_INDEX][0] != '\0') + { + fprintf(stream, "#if %s\n\n", g_parm[COND_INDEX]); + } + + /* Generate the wrapper function definition that matches standard function + * prototype + */ + + fprintf(stream, "%s UP_WRAPSYM(%s)(", g_parm[RETTYPE_INDEX], + g_parm[NAME_INDEX]); + + /* Generate the formal parameter list */ + + if (nparms <= 0) + { + fprintf(stream, "void"); + } + else + { + for (i = 0; i < nfixed; i++) + { + /* The formal and actual parameter types may be encoded.. extra the + * formal parameter type. + */ + + get_formalparmtype(g_parm[PARM1_INDEX + i], formal); + + /* Arguments after the first must be separated from the preceding + * parameter with a comma. + */ + + if (i > 0) + { + fprintf(stream, ", "); + } + + print_formalparm(stream, formal, i + 1); + } + } + + if (i < nparms) + { + fprintf(stream, ", ...)\n{\n"); + } + else + { + fprintf(stream, ")\n{\n"); + } + + /* Generate the result variable definition for non-void function */ + + if (strcmp(g_parm[RETTYPE_INDEX], "void") != 0) + { + fprintf(stream, " %s result;\n", g_parm[RETTYPE_INDEX]); + } + + /* Generate the wrapped (real) function prototype definition */ + + fprintf(stream, " %s UP_REALSYM(%s)(", g_parm[RETTYPE_INDEX], + g_parm[NAME_INDEX]); + + /* Generate the formal parameter list */ + + if (nparms <= 0) + { + fprintf(stream, "void"); + } + else + { + for (i = 0; i < nfixed; i++) + { + /* The formal and actual parameter types may be encoded.. extra the + * formal parameter type. + */ + + get_formalparmtype(g_parm[PARM1_INDEX + i], formal); + + /* Arguments after the first must be separated from the preceding + * parameter with a comma. + */ + + if (i > 0) + { + fprintf(stream, ", "); + } + + fprintf(stream, "%s", formal); + } + } + + /* Handle the end of the formal parameter list */ + + if (i < nparms) + { + fprintf(stream, ", ...);\n"); + + /* Get parm variables .. some from the parameter list and others from + * the varargs. + */ + + fprintf(stream, " va_list ap;\n"); + for (; i < nparms; i++) + { + get_formalparmtype(g_parm[PARM1_INDEX + i], formal); + fprintf(stream, " %s parm%d;\n", formal, i + 1); + } + + fprintf(stream, "\n va_start(ap, parm%d);\n", nfixed); + + for (i = nfixed; i < nparms; i++) + { + get_formalparmtype(g_parm[PARM1_INDEX + i], formal); + get_actualparmtype(g_parm[PARM1_INDEX + i], actual); + + if (is_union(formal)) + { + fprintf(stream, " parm%d = (%s)va_arg(ap, %s);\n", + i + 1, formal, actual); + } + else + { + fprintf(stream, " parm%d = va_arg(ap, %s);\n", i + 1, actual); + } + } + + fprintf(stream, " va_end(ap);\n"); + } + else + { + fprintf(stream, ");\n"); + } + + /* Call system call enter hook function */ + + fprintf(stream, "\n sched_note_syscall_enter(SYS_%s, %d", + g_parm[NAME_INDEX], nparms); + + for (i = 0; i < nparms; i++) + { + /* Is the parameter a union member */ + + if (is_union(g_parm[PARM1_INDEX + i])) + { + /* Then we will have to pick a field name that can be cast to a + * uintptr_t. There probably should be some error handling here< + * to catch the case where the fieldname was not supplied. + */ + + get_fieldname(g_parm[PARM1_INDEX + i], fieldname); + fprintf(stream, ", (uintptr_t)parm%d.%s", i + 1, fieldname); + } + else + { + fprintf(stream, ", (uintptr_t)parm%d", i + 1); + } + } + + fprintf(stream, ");\n\n"); + + /* Then call the wrapped (real) function. Functions that have no return + * value are a special case. + */ + + if (strcmp(g_parm[RETTYPE_INDEX], "void") == 0) + { + fprintf(stream, " UP_REALSYM(%s)(", g_parm[NAME_INDEX]); + } + else + { + fprintf(stream, " result = UP_REALSYM(%s)(", g_parm[NAME_INDEX]); + } + + /* The pass all of the system call parameters */ + + for (i = 0; i < nparms; i++) + { + /* Treat the first argument in the list differently from the others.. + * It does not need a comma before it. + */ + + if (i > 0) + { + fprintf(stream, ", "); + } + + fprintf(stream, "parm%d", i + 1); + } + + fprintf(stream, ");\n\n"); + + /* Call system call leave hook function */ + + fprintf(stream, " sched_note_syscall_leave(SYS_%s, ", g_parm[NAME_INDEX]); + + if (strcmp(g_parm[RETTYPE_INDEX], "void") == 0) + { + fprintf(stream, "0"); + } + else + { + fprintf(stream, "(uintptr_t)result"); + } + + fprintf(stream, ");\n\n"); + + /* Tail end of the function. If the wrapped (real) function has no return + * value, do nothing. + */ + + if (strcmp(g_parm[RETTYPE_INDEX], "void") == 0) + { + fprintf(stream, "}\n"); + } + else + { + fprintf(stream, " return result;\n}\n"); + } + + if (g_parm[COND_INDEX][0] != '\0') + { + fprintf(stream, "\n#endif /* %s */\n", g_parm[COND_INDEX]); + } + + fclose(stream); +} + static void show_usage(const char *progname) { fprintf(stderr, "USAGE: %s [-p|s|i] \n\n", progname); @@ -527,6 +825,7 @@ static void show_usage(const char *progname) fprintf(stderr, "\t-p : Generate proxies\n"); fprintf(stderr, "\t-s : Generate stubs\n"); fprintf(stderr, "\t-i : Generate proxies as static inline functions\n"); + fprintf(stderr, "\t-w : Generate wrappers\n"); fprintf(stderr, "\t-d : Enable debug output\n"); exit(1); } @@ -539,6 +838,7 @@ int main(int argc, char **argv, char **envp) { char *csvpath; bool proxies = false; + bool wrappers = false; FILE *stream; char *ptr; int ch; @@ -549,7 +849,7 @@ int main(int argc, char **argv, char **envp) g_debug = false; g_inline = false; - while ((ch = getopt(argc, argv, ":dps")) > 0) + while ((ch = getopt(argc, argv, ":dpsw")) > 0) { switch (ch) { @@ -569,6 +869,10 @@ int main(int argc, char **argv, char **envp) g_inline = true; break; + case 'w' : + wrappers = true; + break; + case '?' : fprintf(stderr, "Unrecognized option: %c\n", optopt); show_usage(argv[0]); @@ -661,6 +965,10 @@ int main(int argc, char **argv, char **envp) { generate_proxy(nfixed, nargs - PARM1_INDEX); } + else if (wrappers) + { + generate_wrapper(nfixed, nargs - PARM1_INDEX); + } else { g_stubstream = NULL;