diff --git a/Makefile.am b/Makefile.am index 57ab3e20f..010620922 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,6 +15,22 @@ TOOLS = memcheck \ EXP_TOOLS = exp-omega \ exp-ptrcheck +# DDD: once all tools work on Darwin, TEST_TOOLS and TEST_EXP_TOOLS can be +# replaced with TOOLS and EXP_TOOLS. +if !VGCONF_OS_IS_DARWIN + TEST_TOOLS = $(TOOLS) + TEST_EXP_TOOLS = $(EXP_TOOLS) +else + TEST_TOOLS = memcheck \ + cachegrind \ + callgrind \ + massif \ + lackey \ + none + + TEST_EXP_TOOLS = exp-omega +endif + # Put docs last because building the HTML is slow and we want to get # everything else working before we try it. SUBDIRS = include coregrind . tests perf auxprogs $(TOOLS) $(EXP_TOOLS) docs @@ -27,7 +43,8 @@ SUPP_FILES = \ glibc-2.34567-NPTL-helgrind.supp \ glibc-2.2-LinuxThreads-helgrind.supp \ glibc-2.X-drd.supp \ - exp-ptrcheck.supp + exp-ptrcheck.supp \ + darwin9.supp DEFAULT_SUPP_FILES = @DEFAULT_SUPP@ # We include all the base .supp files in the distribution, but not @@ -78,6 +95,9 @@ endif if VGCONF_PLATFORMS_INCLUDE_PPC64_AIX5 # Ditto endif +if VGCONF_OS_IS_DARWIN +# GrP untested, possibly hopeless +endif default.supp: $(DEFAULT_SUPP_FILES) echo "# This is a generated file, composed of the following suppression rules:" > default.supp @@ -86,11 +106,11 @@ default.supp: $(DEFAULT_SUPP_FILES) ## Preprend @PERL@ because tests/vg_regtest isn't executable regtest: check - @PERL@ tests/vg_regtest $(TOOLS) $(EXP_TOOLS) + @PERL@ tests/vg_regtest $(TEST_TOOLS) $(TEST_EXP_TOOLS) nonexp-regtest: check - @PERL@ tests/vg_regtest $(TOOLS) + @PERL@ tests/vg_regtest $(TEST_TOOLS) exp-regtest: check - @PERL@ tests/vg_regtest $(EXP_TOOLS) + @PERL@ tests/vg_regtest $(TEST_EXP_TOOLS) ## Preprend @PERL@ because tests/vg_perf isn't executable perf: check diff --git a/Makefile.core-tool.am b/Makefile.core-tool.am index e5ba11809..493e8c369 100644 --- a/Makefile.core-tool.am +++ b/Makefile.core-tool.am @@ -1,6 +1,15 @@ # This file contains things shared by coregrind/Makefile.am and tool # Makefile.am files. +# See Makefile.tool-tests.am for an explanation of dSYMs. +build-noinst_DSYMS: + for f in $(noinst_DSYMS); do \ + if [ ! -e $$f.dSYM -o $$f -nt $$f.dSYM ] ; then \ + echo "dsymutil $$f"; \ + dsymutil $$f; \ + fi; \ + done + # This is used by coregrind/Makefile.am and Makefile.tool.am for doing # "in-place" installs. It copies $(noinst_PROGRAMS) into $inplacedir. # It needs to be depended on by an 'all-local' rule. @@ -13,6 +22,16 @@ inplace-noinst_PROGRAMS: done ; \ fi +# Similar to inplace-noinst_PROGRAMS +inplace-noinst_DSYMS: build-noinst_DSYMS + if [ -n "$(noinst_DSYMS)" ] ; then \ + mkdir -p $(inplacedir); \ + for f in $(noinst_DSYMS); do \ + rm -f $(inplacedir)/$$f.dSYM; \ + ln -f -s ../$(subdir)/$$f.dSYM $(inplacedir); \ + done ; \ + fi + # This is used by coregrind/Makefile.am and by /Makefile.am for doing # "make install". It copies $(noinst_PROGRAMS) into $prefix/lib/valgrind/. # It needs to be depended on by an 'install-exec-local' rule. @@ -24,3 +43,23 @@ install-noinst_PROGRAMS: done ; \ fi +# Similar to install-noinst_PROGRAMS. +# Nb: we don't use $(INSTALL_PROGRAM) here because it doesn't work with +# directories. XXX: not sure whether the resulting permissions will be +# correct when using 'cp -R'... +install-noinst_DSYMS: build-noinst_DSYMS + if [ -n "$(noinst_DSYMS)" ] ; then \ + $(mkinstalldirs) $(DESTDIR)$(valdir); \ + for f in $(noinst_DSYMS); do \ + cp -R $$f.dSYM $(DESTDIR)$(valdir); \ + done ; \ + fi + +# This needs to be depended on by a 'clean-local' rule. +clean-noinst_DSYMS: + for f in $(noinst_DSYMS); do \ + rm -rf $$f.dSYM; \ + done + + + diff --git a/Makefile.flags.am b/Makefile.flags.am index dc09cab42..322c8fb24 100644 --- a/Makefile.flags.am +++ b/Makefile.flags.am @@ -10,7 +10,12 @@ AM_CFLAGS_BASE = -O2 -g -Wmissing-prototypes -Wall -Wshadow \ # The aim is to give reasonable performance but also to have good # stack traces, since users often see stack traces extending # into (and through) the preloads. -AM_CFLAGS_PIC = -O -g -fpic -fno-omit-frame-pointer -fno-strict-aliasing +if VGCONF_OS_IS_DARWIN +AM_CFLAGS_PIC = -dynamic -O -g -fno-omit-frame-pointer -fno-strict-aliasing -mno-dynamic-no-pic +else +AM_CFLAGS_PIC = -fpic -O -g -fno-omit-frame-pointer -fno-strict-aliasing +endif + # Flags for specific targets. # @@ -82,6 +87,25 @@ AM_CFLAGS_PPC64_AIX5 = @FLAG_MAIX64@ -mcpu=powerpc64 $(AM_CFLAGS_BASE) AM_CCASFLAGS_PPC64_AIX5 = $(AM_CPPFLAGS_PPC64_AIX5) \ @FLAG_MAIX64@ -mcpu=powerpc64 -g +AM_FLAG_M3264_X86_DARWIN = -arch i386 +AM_CPPFLAGS_X86_DARWIN = $(AM_CPPFLAGS_COMMON) \ + -DVGA_x86=1 \ + -DVGO_darwin=1 \ + -DVGP_x86_darwin=1 +AM_CFLAGS_X86_DARWIN = $(WERROR) -arch i386 $(AM_CFLAGS_BASE) \ + -mmacosx-version-min=10.5 -fno-stack-protector \ + -mdynamic-no-pic +AM_CCASFLAGS_X86_DARWIN = $(AM_CPPFLAGS_X86_DARWIN) -arch i386 -g + +AM_FLAG_M3264_AMD64_DARWIN = -arch x86_64 +AM_CPPFLAGS_AMD64_DARWIN = $(AM_CPPFLAGS_COMMON) \ + -DVGA_amd64=1 \ + -DVGO_darwin=1 \ + -DVGP_amd64_darwin=1 +AM_CFLAGS_AMD64_DARWIN = $(WERROR) -arch x86_64 $(AM_CFLAGS_BASE) \ + -mmacosx-version-min=10.5 -fno-stack-protector +AM_CCASFLAGS_AMD64_DARWIN = $(AM_CPPFLAGS_AMD64_DARWIN) -arch x86_64 -g + # Flags for the primary target. These must be used to build the # regtests and performance tests. In fact, these must be used to # build anything which is built only once on a dual-arch build. @@ -102,9 +126,12 @@ endif # PRELOAD_LDFLAGS_COMMON_LINUX = -nodefaultlibs -shared -Wl,-z,interpose,-z,initfirst PRELOAD_LDFLAGS_COMMON_AIX5 = -nodefaultlibs -shared -Wl,-G -Wl,-bnogc +PRELOAD_LDFLAGS_COMMON_DARWIN = -dynamic -dynamiclib -all_load PRELOAD_LDFLAGS_X86_LINUX = $(PRELOAD_LDFLAGS_COMMON_LINUX) @FLAG_M32@ PRELOAD_LDFLAGS_AMD64_LINUX = $(PRELOAD_LDFLAGS_COMMON_LINUX) @FLAG_M64@ PRELOAD_LDFLAGS_PPC32_LINUX = $(PRELOAD_LDFLAGS_COMMON_LINUX) @FLAG_M32@ PRELOAD_LDFLAGS_PPC64_LINUX = $(PRELOAD_LDFLAGS_COMMON_LINUX) @FLAG_M64@ PRELOAD_LDFLAGS_PPC32_AIX5 = $(PRELOAD_LDFLAGS_COMMON_AIX5) @FLAG_MAIX32@ PRELOAD_LDFLAGS_PPC64_AIX5 = $(PRELOAD_LDFLAGS_COMMON_AIX5) @FLAG_MAIX64@ +PRELOAD_LDFLAGS_X86_DARWIN = $(PRELOAD_LDFLAGS_COMMON_DARWIN) -arch i386 +PRELOAD_LDFLAGS_AMD64_DARWIN = $(PRELOAD_LDFLAGS_COMMON_DARWIN) -arch x86_64 diff --git a/Makefile.tool-tests.am b/Makefile.tool-tests.am index b81d3e583..9694f064a 100644 --- a/Makefile.tool-tests.am +++ b/Makefile.tool-tests.am @@ -16,3 +16,25 @@ AM_CXXFLAGS = -Winline -Wall -Wshadow -g # automake; see comments in Makefile.flags.am for more detail. AM_CCASFLAGS = $(AM_CPPFLAGS) + +# On Darwin, for a program 'p', the DWARF debug info is stored in the +# directory 'p.dSYM'. This must be generated after the executable is +# created, with 'dsymutil p'. We could redefine LINK with a script that +# executes 'dsymutil' after linking, but that's a pain. Instead we use this +# hook so that every time "make check" is run, we subsequently invoke +# 'dsymutil' on all the executables that lack a .dSYM directory, or that are +# newer than their corresponding .dSYM directory. +if VGCONF_OS_IS_DARWIN +check-local: + for f in $(check_PROGRAMS) ; do \ + if [ ! -e $$f.dSYM -o $$f -nt $$f.dSYM ] ; then \ + echo "dsymutil $$f"; \ + dsymutil $$f; \ + fi \ + done + +clean-local: + for f in $(check_PROGRAMS) ; do \ + rm -rf $$f.dSYM; \ + done +endif diff --git a/Makefile.tool.am b/Makefile.tool.am index 08127286a..21925fca4 100644 --- a/Makefile.tool.am +++ b/Makefile.tool.am @@ -24,6 +24,12 @@ LIBREPLACEMALLOC_PPC32_AIX5 = \ LIBREPLACEMALLOC_PPC64_AIX5 = \ $(top_builddir)/coregrind/libreplacemalloc_toolpreload-ppc64-aix5.a +LIBREPLACEMALLOC_X86_DARWIN = \ + $(top_builddir)/coregrind/libreplacemalloc_toolpreload-x86-darwin.a + +LIBREPLACEMALLOC_AMD64_DARWIN = \ + $(top_builddir)/coregrind/libreplacemalloc_toolpreload-amd64-darwin.a + COREGRIND_LIBS_X86_LINUX = \ $(top_builddir)/coregrind/libcoregrind-x86-linux.a \ @@ -49,6 +55,14 @@ COREGRIND_LIBS_PPC64_AIX5 = \ $(top_builddir)/coregrind/libcoregrind-ppc64-aix5.a \ @VEX_DIR@/libvex-ppc64-aix5.a +COREGRIND_LIBS_X86_DARWIN = \ + $(top_builddir)/coregrind/libcoregrind-x86-darwin.a \ + @VEX_DIR@/libvex-x86-darwin.a + +COREGRIND_LIBS_AMD64_DARWIN = \ + $(top_builddir)/coregrind/libcoregrind-amd64-darwin.a \ + @VEX_DIR@/libvex-amd64-darwin.a + ##.PHONY: @VEX_DIR@/libvex.a @@ -88,6 +102,18 @@ COREGRIND_LIBS_PPC64_AIX5 = \ EXTRA_CFLAGS="$(AM_CFLAGS_PPC64_AIX5) @FLAG_WDECL_AFTER_STMT@ \ @FLAG_FNO_STACK_PROTECTOR@" +@VEX_DIR@/libvex-x86-darwin.a: @VEX_DIR@/priv/main/vex_svnversion.h + $(MAKE) -C @VEX_DIR@ CC="$(CC)" AR="$(AR)" \ + libvex-x86-darwin.a \ + EXTRA_CFLAGS="$(AM_CFLAGS_X86_DARWIN) @FLAG_WDECL_AFTER_STMT@ \ + @FLAG_FNO_STACK_PROTECTOR@" + +@VEX_DIR@/libvex-amd64-darwin.a: @VEX_DIR@/priv/main/vex_svnversion.h + $(MAKE) -C @VEX_DIR@ CC="$(CC)" AR="$(AR)" \ + libvex-amd64-darwin.a \ + EXTRA_CFLAGS="$(AM_CFLAGS_AMD64_DARWIN) @FLAG_WDECL_AFTER_STMT@ \ + @FLAG_FNO_STACK_PROTECTOR@" + @VEX_DIR@/priv/main/vex_svnversion.h: $(MAKE) -C @VEX_DIR@ CC="$(CC)" version @@ -97,7 +123,8 @@ TOOL_LDFLAGS_COMMON_LINUX = -static \ -Wl,-defsym,valt_load_address=@VALT_LOAD_ADDRESS@ \ -nodefaultlibs -nostartfiles -u _start TOOL_LDFLAGS_COMMON_AIX5 = -static -Wl,-e_start_valgrind - +TOOL_LDFLAGS_COMMON_DARWIN = -nodefaultlibs -nostartfiles \ + -Wl,-u,__start -Wl,-e,__start -Wl,-bind_at_load /usr/lib/dyld TOOL_LDADD_X86_LINUX = $(COREGRIND_LIBS_X86_LINUX) $(TOOL_LDADD_COMMON) TOOL_LDFLAGS_X86_LINUX = \ @@ -127,6 +154,23 @@ TOOL_LDADD_PPC64_AIX5 = $(COREGRIND_LIBS_PPC64_AIX5) $(TOOL_LDADD_COMMON) TOOL_LDFLAGS_PPC64_AIX5 = \ $(TOOL_LDFLAGS_COMMON_AIX5) @FLAG_MAIX64@ -Wl,-bbigtoc +TOOL_LDADD_X86_DARWIN = $(COREGRIND_LIBS_X86_DARWIN) $(TOOL_LDADD_COMMON) +TOOL_LDFLAGS_X86_DARWIN = \ + $(TOOL_LDFLAGS_COMMON_DARWIN) -arch i386 \ + -Wl,-seg1addr,0xf0080000 \ + -Wl,-stack_addr,0xf0080000 -Wl,-stack_size,0x80000 \ + -Wl,-pagezero_size,0xf0000000 + +# pagezero can't be unmapped and remapped. Use stack instead. +# GrP fixme no stack guard +TOOL_LDADD_AMD64_DARWIN = $(COREGRIND_LIBS_AMD64_DARWIN) $(TOOL_LDADD_COMMON) +TOOL_LDFLAGS_AMD64_DARWIN = \ + $(TOOL_LDFLAGS_COMMON_DARWIN) -arch x86_64 \ + -Wl,-seg1addr,0x7fff55000000 \ + -Wl,-stack_addr,0x7fff50080000 -Wl,-stack_size,0x7ffe50080000 \ + -Wl,-pagezero_size,0x100000000 + + LIBREPLACEMALLOC_LDFLAGS_X86_LINUX = \ -Wl,--whole-archive \ $(LIBREPLACEMALLOC_X86_LINUX) \ @@ -153,8 +197,17 @@ LIBREPLACEMALLOC_LDFLAGS_PPC32_AIX5 = \ LIBREPLACEMALLOC_LDFLAGS_PPC64_AIX5 = \ $(LIBREPLACEMALLOC_PPC64_AIX5) +LIBREPLACEMALLOC_LDFLAGS_X86_DARWIN = \ + $(LIBREPLACEMALLOC_X86_DARWIN) -all-local: inplace-noinst_PROGRAMS +LIBREPLACEMALLOC_LDFLAGS_AMD64_DARWIN = \ + $(LIBREPLACEMALLOC_AMD64_DARWIN) -install-exec-local: install-noinst_PROGRAMS + + +all-local: inplace-noinst_PROGRAMS inplace-noinst_DSYMS + +clean-local: clean-noinst_DSYMS + +install-exec-local: install-noinst_PROGRAMS install-noinst_DSYMS diff --git a/NEWS b/NEWS index 757b22043..332abab84 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,22 @@ Release 3.5.0 (???) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* XXX: Mac OS X support + - x86/Darwin. + - probably amd64/Darwin. + - Requires Mac OS X 10.5 Leopard or later. + - No support for Mac OS X on PowerPC machines. + - Many thanks to Greg Parker for developing this port over several years. + + Things that don't work + - Objective-C garbage collection + - --db-attach=yes + - Messages like the following indicate a mismatch between Valgrind's + memory map and the kernel. Occasional failures are expected in + multithreaded programs. If the failure repeats for the same address + range, then there may be a problem causing false errors or crashes. + sync check at ...: FAILED + * A new Memcheck client request VALGRIND_COUNT_LEAK_BLOCKS has been added. It is similar to VALGRIND_COUNT_LEAKS but counts blocks instead of bytes. [XXX: consider adding VALGRIND_COUNT_LEAK_BYTES as a synonym and diff --git a/auxprogs/Makefile.am b/auxprogs/Makefile.am index 7145df752..df59b7429 100644 --- a/auxprogs/Makefile.am +++ b/auxprogs/Makefile.am @@ -27,7 +27,6 @@ valgrind_listener_LDFLAGS = $(AM_CFLAGS_PRI) # #---------------------------------------------------------- - #------------------------- mpi wrappers ----------------------- # Build libmpiwrap.so for the primary target, and for the secondary # target if relevant. @@ -50,12 +49,19 @@ if VGCONF_OS_IS_AIX5 HACKY_FLAGS_SEC = -g -O -bE:libmpiwrap_aix5.exp -bM:SRE -bnoentry \ -qflag=w:w -qlanglvl=extended \ `echo $(AM_FLAG_M3264_SEC) | sed s/maix/q/g` +else +if VGCONF_OS_IS_DARWIN + HACKY_FLAGS_PRI = -g -O -fno-omit-frame-pointer -Wall -dynamic \ + -dynamiclib -all_load $(AM_FLAG_M3264_PRI) + HACKY_FLAGS_SEC = -g -O -fno-omit-frame-pointer -Wall -dynamic \ + -dynamiclib -all_load $(AM_FLAG_M3264_SEC) else HACKY_FLAGS_PRI = -g -O -fno-omit-frame-pointer -Wall -fpic -shared \ $(AM_FLAG_M3264_PRI) HACKY_FLAGS_SEC = -g -O -fno-omit-frame-pointer -Wall -fpic -shared \ $(AM_FLAG_M3264_SEC) endif +endif ## First, we have to say how to build the .so's .. @@ -120,6 +126,9 @@ clean-local: rm -f libmpiwrap-.c \ libmpiwrap-@VGCONF_ARCH_PRI@-@VGCONF_OS@.c \ libmpiwrap-@VGCONF_ARCH_SEC@-@VGCONF_OS@.c +if VGCONF_OS_IS_DARWIN + rm -rf libmpiwrap-*.dSYM +endif # #---------------------------------------------------------- diff --git a/cachegrind/Makefile.am b/cachegrind/Makefile.am index eac282558..f165e8dc8 100644 --- a/cachegrind/Makefile.am +++ b/cachegrind/Makefile.am @@ -23,6 +23,12 @@ endif if VGCONF_PLATFORMS_INCLUDE_PPC64_AIX5 noinst_PROGRAMS += cachegrind-ppc64-aix5 endif +if VGCONF_PLATFORMS_INCLUDE_X86_DARWIN +noinst_PROGRAMS += cachegrind-x86-darwin +endif +if VGCONF_PLATFORMS_INCLUDE_AMD64_DARWIN +noinst_PROGRAMS += cachegrind-amd64-darwin +endif # Build cg_merge for the primary target only. bin_PROGRAMS = cg_merge @@ -80,3 +86,17 @@ cachegrind_ppc64_aix5_CFLAGS = $(AM_CFLAGS_PPC64_AIX5) cachegrind_ppc64_aix5_DEPENDENCIES = $(COREGRIND_LIBS_PPC64_AIX5) cachegrind_ppc64_aix5_LDADD = $(TOOL_LDADD_PPC64_AIX5) cachegrind_ppc64_aix5_LDFLAGS = $(TOOL_LDFLAGS_PPC64_AIX5) + +cachegrind_x86_darwin_SOURCES = $(CACHEGRIND_SOURCES_COMMON) $(CACHEGRIND_SOURCES_X86) +cachegrind_x86_darwin_CPPFLAGS = $(AM_CPPFLAGS_X86_DARWIN) +cachegrind_x86_darwin_CFLAGS = $(AM_CFLAGS_X86_DARWIN) +cachegrind_x86_darwin_DEPENDENCIES = $(COREGRIND_LIBS_X86_DARWIN) +cachegrind_x86_darwin_LDADD = $(TOOL_LDADD_X86_DARWIN) +cachegrind_x86_darwin_LDFLAGS = $(TOOL_LDFLAGS_X86_DARWIN) + +cachegrind_amd64_darwin_SOURCES = $(CACHEGRIND_SOURCES_COMMON) $(CACHEGRIND_SOURCES_AMD64) +cachegrind_amd64_darwin_CPPFLAGS = $(AM_CPPFLAGS_AMD64_DARWIN) +cachegrind_amd64_darwin_CFLAGS = $(AM_CFLAGS_AMD64_DARWIN) +cachegrind_amd64_darwin_DEPENDENCIES = $(COREGRIND_LIBS_AMD64_DARWIN) +cachegrind_amd64_darwin_LDADD = $(TOOL_LDADD_AMD64_DARWIN) +cachegrind_amd64_darwin_LDFLAGS = $(TOOL_LDFLAGS_AMD64_DARWIN) diff --git a/cachegrind/cg_main.c b/cachegrind/cg_main.c index 279356bd5..6c4e1e471 100644 --- a/cachegrind/cg_main.c +++ b/cachegrind/cg_main.c @@ -1464,13 +1464,14 @@ static void cg_fini(Int exitcode) if (VG_(clo_verbosity) == 0) return; - #define MAX(a, b) ((a) >= (b) ? (a) : (b)) + // Nb: this isn't called "MAX" because that overshadows a global on Darwin. + #define CG_MAX(a, b) ((a) >= (b) ? (a) : (b)) /* I cache results. Use the I_refs value to determine the first column * width. */ l1 = ULong_width(Ir_total.a); - l2 = ULong_width(MAX(Dr_total.a, Bc_total.b)); - l3 = ULong_width(MAX(Dw_total.a, Bi_total.b)); + l2 = ULong_width(CG_MAX(Dr_total.a, Bc_total.b)); + l3 = ULong_width(CG_MAX(Dw_total.a, Bi_total.b)); /* Make format string, getting width right for numbers */ VG_(sprintf)(fmt, "%%s %%,%dllu", l1); diff --git a/cachegrind/tests/Makefile.am b/cachegrind/tests/Makefile.am index 497ee1977..f7f12a71c 100644 --- a/cachegrind/tests/Makefile.am +++ b/cachegrind/tests/Makefile.am @@ -25,5 +25,9 @@ AM_CXXFLAGS += $(AM_FLAG_M3264_PRI) # C ones dlclose_LDADD = -ldl -myprint_so_LDFLAGS = $(AM_FLAG_M3264_PRI) -shared -fPIC +if VGCONF_OS_IS_DARWIN +myprint_so_LDFLAGS = $(AM_CFLAGS) -dynamic -dynamiclib -all_load -fpic +else +myprint_so_LDFLAGS = $(AM_CFLAGS) -shared -fPIC +endif myprint_so_CFLAGS = $(AM_CFLAGS) -fPIC diff --git a/callgrind/Makefile.am b/callgrind/Makefile.am index ac1720c49..bc983b6ed 100644 --- a/callgrind/Makefile.am +++ b/callgrind/Makefile.am @@ -23,6 +23,12 @@ endif if VGCONF_PLATFORMS_INCLUDE_PPC64_AIX5 noinst_PROGRAMS += callgrind-ppc64-aix5 endif +if VGCONF_PLATFORMS_INCLUDE_X86_DARWIN +noinst_PROGRAMS += callgrind-x86-darwin +endif +if VGCONF_PLATFORMS_INCLUDE_AMD64_DARWIN +noinst_PROGRAMS += callgrind-amd64-darwin +endif CALLGRIND_SOURCES_COMMON = main.c events.c bb.c clo.c \ costs.c bbcc.c command.c debug.c fn.c \ @@ -81,3 +87,17 @@ callgrind_ppc64_aix5_CFLAGS = $(CALLGRIND_CFLAGS_COMMON) $(AM_CFLAGS_PPC64 callgrind_ppc64_aix5_DEPENDENCIES = $(COREGRIND_LIBS_PPC64_AIX5) callgrind_ppc64_aix5_LDADD = $(TOOL_LDADD_PPC64_AIX5) callgrind_ppc64_aix5_LDFLAGS = $(TOOL_LDFLAGS_PPC64_AIX5) + +callgrind_x86_darwin_SOURCES = $(CALLGRIND_SOURCES_COMMON) $(CALLGRIND_SOURCES_X86) +callgrind_x86_darwin_CPPFLAGS = $(AM_CPPFLAGS_X86_DARWIN) +callgrind_x86_darwin_CFLAGS = $(CALLGRIND_CFLAGS_COMMON) $(AM_CFLAGS_X86_DARWIN) +callgrind_x86_darwin_DEPENDENCIES = $(COREGRIND_LIBS_X86_DARWIN) +callgrind_x86_darwin_LDADD = $(TOOL_LDADD_X86_DARWIN) +callgrind_x86_darwin_LDFLAGS = $(TOOL_LDFLAGS_X86_DARWIN) + +callgrind_amd64_darwin_SOURCES = $(CALLGRIND_SOURCES_COMMON) $(CALLGRIND_SOURCES_AMD64) +callgrind_amd64_darwin_CPPFLAGS = $(AM_CPPFLAGS_AMD64_DARWIN) +callgrind_amd64_darwin_CFLAGS = $(CALLGRIND_CFLAGS_COMMON) $(AM_CFLAGS_AMD64_DARWIN) +callgrind_amd64_darwin_DEPENDENCIES = $(COREGRIND_LIBS_AMD64_DARWIN) +callgrind_amd64_darwin_LDADD = $(TOOL_LDADD_AMD64_DARWIN) +callgrind_amd64_darwin_LDFLAGS = $(TOOL_LDFLAGS_AMD64_DARWIN) diff --git a/configure.in b/configure.in index 1e3892fc5..e6657df6c 100644 --- a/configure.in +++ b/configure.in @@ -62,6 +62,7 @@ AC_PROG_CC AM_PROG_CC_C_O AC_PROG_CPP AC_PROG_CXX +AC_PROG_OBJC AC_PROG_RANLIB # If no AR variable was specified, look up the name of the archiver. Otherwise @@ -219,6 +220,8 @@ fi AC_MSG_CHECKING([for a supported OS]) AC_SUBST(VGCONF_OS) +DEFAULT_SUPP="" + case "${host_os}" in *linux*) AC_MSG_RESULT([ok (${host_os})]) @@ -266,6 +269,34 @@ case "${host_os}" in VGCONF_OS="freebsd" ;; + *darwin*) + AC_MSG_RESULT([ok (${host_os})]) + VGCONF_OS="darwin" + + AC_MSG_CHECKING([for the kernel version]) + kernel=`uname -r` + + # Nb: for Darwin we set DEFAULT_SUPP here. That's because Darwin + # has only one relevant version, the OS version. The `uname` check + # is a good way to get that version (i.e. "Darwin 9.6.0" is Mac OS + # X 10.5.6, and "Darwin 10.x" would presumably be Mac OS X 10.6.x + # Snow Leopard and darwin10.supp), and we don't know of an macros + # similar to __GLIBC__ to get that info. + # + # XXX: `uname -r` won't do the right thing for cross-compiles, but + # that's not a problem yet. + case "${kernel}" in + 9.*) + AC_MSG_RESULT([Darwin 9.x (${kernel}) / Mac OS X 10.5 Leopard]) + DEFAULT_SUPP="darwin9.supp ${DEFAULT_SUPP}" + ;; + *) + AC_MSG_RESULT([unsupported (${kernel})]) + AC_MSG_ERROR([Valgrind works on Darwin 9.x (Mac OS X 10.5)]) + ;; + esac + ;; + *) AC_MSG_RESULT([no (${host_os})]) AC_MSG_ERROR([Valgrind is operating system specific. Sorry. Please consider doing a port.]) @@ -405,6 +436,33 @@ case "$ARCH_MAX-$VGCONF_OS" in valt_load_address_inner="0x28000000" AC_MSG_RESULT([ok (${ARCH_MAX}-${VGCONF_OS})]) ;; + x86-darwin) + VGCONF_ARCH_PRI="x86" + VGCONF_PLATFORM_PRI_CAPS="X86_DARWIN" + VGCONF_PLATFORM_SEC_CAPS="" + valt_load_address_normal="0x0" + valt_load_address_inner="0x0" + AC_MSG_RESULT([ok (${ARCH_MAX}-${VGCONF_OS})]) + ;; + amd64-darwin) + if test x$vg_cv_only64bit = xyes; then + VGCONF_ARCH_PRI="amd64" + VGCONF_PLATFORM_PRI_CAPS="AMD64_DARWIN" + VGCONF_PLATFORM_SEC_CAPS="" + elif test x$vg_cv_only32bit = xyes; then + VGCONF_ARCH_PRI="x86" + VGCONF_PLATFORM_PRI_CAPS="X86_DARWIN" + VGCONF_PLATFORM_SEC_CAPS="" + VGCONF_ARCH_PRI_CAPS="x86" + else + VGCONF_ARCH_PRI="amd64" + VGCONF_PLATFORM_PRI_CAPS="AMD64_DARWIN" + VGCONF_PLATFORM_SEC_CAPS="X86_DARWIN" + fi + valt_load_address_normal="0x0" + valt_load_address_inner="0x0" + AC_MSG_RESULT([ok (${ARCH_MAX}-${VGCONF_OS})]) + ;; *) VGCONF_ARCH_PRI="unknown" VGCONF_PLATFORM_PRI_CAPS="UNKNOWN" @@ -420,9 +478,12 @@ esac # defined. AM_CONDITIONAL(VGCONF_ARCHS_INCLUDE_X86, test x$VGCONF_PLATFORM_PRI_CAPS = xX86_LINUX \ - -o x$VGCONF_PLATFORM_SEC_CAPS = xX86_LINUX ) + -o x$VGCONF_PLATFORM_SEC_CAPS = xX86_LINUX \ + -o x$VGCONF_PLATFORM_PRI_CAPS = xX86_DARWIN \ + -o x$VGCONF_PLATFORM_SEC_CAPS = xX86_DARWIN ) AM_CONDITIONAL(VGCONF_ARCHS_INCLUDE_AMD64, - test x$VGCONF_PLATFORM_PRI_CAPS = xAMD64_LINUX ) + test x$VGCONF_PLATFORM_PRI_CAPS = xAMD64_LINUX \ + -o x$VGCONF_PLATFORM_PRI_CAPS = xAMD64_DARWIN ) AM_CONDITIONAL(VGCONF_ARCHS_INCLUDE_PPC32, test x$VGCONF_PLATFORM_PRI_CAPS = xPPC32_LINUX \ -o x$VGCONF_PLATFORM_SEC_CAPS = xPPC32_LINUX \ @@ -444,12 +505,20 @@ AM_CONDITIONAL(VGCONF_PLATFORMS_INCLUDE_PPC32_LINUX, -o x$VGCONF_PLATFORM_SEC_CAPS = xPPC32_LINUX) AM_CONDITIONAL(VGCONF_PLATFORMS_INCLUDE_PPC64_LINUX, test x$VGCONF_PLATFORM_PRI_CAPS = xPPC64_LINUX) + AM_CONDITIONAL(VGCONF_PLATFORMS_INCLUDE_PPC32_AIX5, test x$VGCONF_PLATFORM_PRI_CAPS = xPPC32_AIX5 \ -o x$VGCONF_PLATFORM_SEC_CAPS = xPPC32_AIX5) AM_CONDITIONAL(VGCONF_PLATFORMS_INCLUDE_PPC64_AIX5, test x$VGCONF_PLATFORM_PRI_CAPS = xPPC64_AIX5) +AM_CONDITIONAL(VGCONF_PLATFORMS_INCLUDE_X86_DARWIN, + test x$VGCONF_PLATFORM_PRI_CAPS = xX86_DARWIN \ + -o x$VGCONF_PLATFORM_SEC_CAPS = xX86_DARWIN) +AM_CONDITIONAL(VGCONF_PLATFORMS_INCLUDE_AMD64_DARWIN, + test x$VGCONF_PLATFORM_PRI_CAPS = xAMD64_DARWIN) + + # Similarly, set up VGCONF_OF_IS_. Exactly one of these becomes defined. # Relies on the assumption that the primary and secondary targets are # for the same OS, so therefore only necessary to test the primary. @@ -461,6 +530,9 @@ AM_CONDITIONAL(VGCONF_OS_IS_LINUX, AM_CONDITIONAL(VGCONF_OS_IS_AIX5, test x$VGCONF_PLATFORM_PRI_CAPS = xPPC32_AIX5 \ -o x$VGCONF_PLATFORM_PRI_CAPS = xPPC64_AIX5) +AM_CONDITIONAL(VGCONF_OS_IS_DARWIN, + test x$VGCONF_PLATFORM_PRI_CAPS = xX86_DARWIN \ + -o x$VGCONF_PLATFORM_PRI_CAPS = xAMD64_DARWIN) # Sometimes, in the Makefile.am files, it's useful to know whether or not @@ -493,7 +565,6 @@ fi # Libc and suppressions #---------------------------------------------------------------------------- # This variable will collect the suppression files to be used. -DEFAULT_SUPP="" AC_SUBST(DEFAULT_SUPP) GLIBC_VERSION="" @@ -596,6 +667,15 @@ AC_EGREP_CPP([AIX5_LIBC], [ ], GLIBC_VERSION="aix5") +# not really a version check +AC_EGREP_CPP([DARWIN_LIBC], [ +#include +#if defined(__DARWIN_VERS_1050) + DARWIN_LIBC +#endif +], +GLIBC_VERSION="darwin") + AC_MSG_CHECKING([the GLIBC_VERSION version]) case "${GLIBC_VERSION}" in @@ -670,11 +750,17 @@ case "${GLIBC_VERSION}" in AC_DEFINE([AIX5_LIBC], 1, [Define to 1 if you're using AIX 5.1 or 5.2 or 5.3]) DEFAULT_SUPP="aix5libc.supp ${DEFAULT_SUPP}" ;; + darwin) + AC_MSG_RESULT(Darwin) + AC_DEFINE([DARWIN_LIBC], 1, [Define to 1 if you're using Darwin]) + # DEFAULT_SUPP set by kernel version check above. + ;; *) AC_MSG_RESULT(unsupported version) AC_MSG_ERROR([Valgrind requires glibc version 2.2 - 2.10]) AC_MSG_ERROR([or AIX 5.1 or 5.2 or 5.3 GLIBC_VERSION]) + AC_MSG_ERROR([or Darwin libc]) ;; esac @@ -1340,6 +1426,14 @@ AC_DEFINE([HAVE_TLS], 1, [can use __thread to define thread-local variables]) fi +#---------------------------------------------------------------------------- +# Check for /proc filesystem +#---------------------------------------------------------------------------- +AC_CHECK_FILES(/proc/self/fd /proc/self/exe /proc/self/maps, + [ AC_DEFINE([HAVE_PROC], 1, [can use /proc filesystem]) ], + []) + + #---------------------------------------------------------------------------- # Checks for C header files. #---------------------------------------------------------------------------- @@ -1389,6 +1483,7 @@ AC_CHECK_FUNCS([ \ memchr \ memset \ mkdir \ + mremap \ ppoll \ pthread_barrier_init \ pthread_condattr_setclock \ @@ -1738,6 +1833,7 @@ AC_OUTPUT( memcheck/tests/amd64/Makefile memcheck/tests/x86/Makefile memcheck/tests/linux/Makefile + memcheck/tests/darwin/Makefile memcheck/tests/x86-linux/Makefile memcheck/perf/Makefile memcheck/docs/Makefile @@ -1769,6 +1865,7 @@ AC_OUTPUT( none/tests/ppc64/Makefile none/tests/x86/Makefile none/tests/linux/Makefile + none/tests/darwin/Makefile none/tests/x86-linux/Makefile none/docs/Makefile exp-omega/Makefile diff --git a/coregrind/Makefile.am b/coregrind/Makefile.am index 0839b856d..1bcfec7ff 100644 --- a/coregrind/Makefile.am +++ b/coregrind/Makefile.am @@ -7,29 +7,40 @@ include $(top_srcdir)/Makefile.all.am include $(top_srcdir)/Makefile.flags.am include $(top_srcdir)/Makefile.core-tool.am - AM_CPPFLAGS_CORE_COMMON = \ -I$(top_srcdir)/coregrind \ -DVG_LIBDIR="\"$(valdir)"\" AM_CPPFLAGS_X86_LINUX += \ $(AM_CPPFLAGS_CORE_COMMON) -DVG_PLATFORM="\"x86-linux\"" + AM_CPPFLAGS_AMD64_LINUX += \ $(AM_CPPFLAGS_CORE_COMMON) -DVG_PLATFORM="\"amd64-linux\"" + AM_CPPFLAGS_PPC32_LINUX += \ $(AM_CPPFLAGS_CORE_COMMON) -DVG_PLATFORM="\"ppc32-linux\"" + AM_CPPFLAGS_PPC64_LINUX += \ $(AM_CPPFLAGS_CORE_COMMON) -DVG_PLATFORM="\"ppc64-linux\"" + AM_CPPFLAGS_PPC32_AIX5 += \ $(AM_CPPFLAGS_CORE_COMMON) -DVG_PLATFORM="\"ppc32-aix5\"" + AM_CPPFLAGS_PPC64_AIX5 += \ $(AM_CPPFLAGS_CORE_COMMON) -DVG_PLATFORM="\"ppc64-aix5\"" +AM_CPPFLAGS_X86_DARWIN += \ + $(AM_CPPFLAGS_CORE_COMMON) -DVG_PLATFORM="\"x86-darwin\"" + +AM_CPPFLAGS_AMD64_DARWIN += \ + $(AM_CPPFLAGS_CORE_COMMON) -DVG_PLATFORM="\"amd64-darwin\"" + default.supp: $(SUPP_FILES) noinst_PROGRAMS = +noinst_DSYMS = pkglib_LIBRARIES = LIBVEX = @@ -69,6 +80,20 @@ pkglib_LIBRARIES += libcoregrind-ppc64-aix5.a libreplacemalloc_toolpreload-ppc64 LIBVEX += libvex-ppc64-aix5.a endif +if VGCONF_PLATFORMS_INCLUDE_X86_DARWIN +noinst_PROGRAMS += vgpreload_core-x86-darwin.so +noinst_DSYMS += vgpreload_core-x86-darwin.so +pkglib_LIBRARIES += libcoregrind-x86-darwin.a libreplacemalloc_toolpreload-x86-darwin.a +LIBVEX += libvex-x86-darwin.a +endif + +if VGCONF_PLATFORMS_INCLUDE_AMD64_DARWIN +noinst_PROGRAMS += vgpreload_core-amd64-darwin.so +noinst_DSYMS += vgpreload_core-amd64-darwin.so +pkglib_LIBRARIES += libcoregrind-amd64-darwin.a libreplacemalloc_toolpreload-amd64-darwin.a +LIBVEX += libvex-amd64-darwin.a +endif + #------------------------- launcher ----------------------- # Build the launcher (valgrind) for the primary target only. @@ -87,6 +112,46 @@ valgrind_SOURCES = \ m_debuglog.c \ m_vkiscnums.c endif +if VGCONF_OS_IS_DARWIN +valgrind_SOURCES = \ + launcher-darwin.c \ + m_debuglog.c +endif + +# Mach RPC interface definitions +# Here are some more .defs files that are not used, but could be in the +# future: +# clock.defs \ +# clock_priv.defs \ +# clock_reply.defs \ +# exc.defs \ +# host_priv.defs \ +# host_security.defs \ +# ledger.defs \ +# lock_set.defs \ +# mach_host.defs \ +# mach_port.defs \ +# notify.defs \ +# processor.defs \ +# processor_set.defs \ +# +if VGCONF_OS_IS_DARWIN +mach_defs = \ + mach_vm.defs \ + task.defs \ + thread_act.defs \ + vm_map.defs +else +mach_defs = +endif + +mach_srcs = $(addprefix m_mach/,$(mach_defs:.defs=User.c)) +mach_server_srcs = $(addprefix m_mach/,$(mach_defs:.defs=Server.c)) +mach_hdrs = $(addprefix m_mach/,$(mach_defs:.defs=.h)) +mach_files = $(addprefix /usr/include/mach/,$(mach_defs)) + +$(mach_srcs) $(mach_hdrs): $(mach_files) + (cd m_mach && mig $(mach_files)) valgrind_CPPFLAGS = $(AM_CPPFLAGS_PRI) valgrind_CFLAGS = $(AM_CFLAGS_PRI) @@ -103,6 +168,7 @@ no_op_client_for_valgrind_LDFLAGS = $(AM_CFLAGS_PRI) noinst_HEADERS = \ + $(mach_hdrs) \ launcher-aix5-bootblock.h \ pub_core_aspacehl.h \ pub_core_aspacemgr.h \ @@ -129,6 +195,7 @@ noinst_HEADERS = \ pub_core_libcprint.h \ pub_core_libcproc.h \ pub_core_libcsignal.h \ + pub_core_mach.h \ pub_core_machine.h \ pub_core_mallocfree.h \ pub_core_options.h \ @@ -166,6 +233,7 @@ noinst_HEADERS = \ m_debuginfo/priv_readdwarf.h \ m_debuginfo/priv_readdwarf3.h \ m_debuginfo/priv_readelf.h \ + m_debuginfo/priv_readmacho.h \ m_debuginfo/priv_readxcoff.h \ m_demangle/ansidecl.h \ m_demangle/cp-demangle.h \ @@ -181,17 +249,22 @@ noinst_HEADERS = \ m_syswrap/priv_syswrap-linux.h \ m_syswrap/priv_syswrap-linux-variants.h \ m_syswrap/priv_syswrap-aix5.h \ + m_syswrap/priv_syswrap-darwin.h \ m_syswrap/priv_syswrap-main.h \ m_ume/priv_ume.h BUILT_SOURCES = CLEANFILES = +if VGCONF_OS_IS_DARWIN +BUILT_SOURCES += $(COREGRIND_DARWIN_BUILT_SOURCES) +CLEANFILES += $(COREGRIND_DARWIN_BUILT_SOURCES) +endif COREGRIND_SOURCES_COMMON = \ m_commandline.c \ - m_cpuid.S \ m_clientstate.c \ + m_cpuid.S \ m_debugger.c \ m_debuglog.c \ m_errormgr.c \ @@ -241,6 +314,7 @@ COREGRIND_SOURCES_COMMON = \ m_scheduler/sema.c \ m_syswrap/syswrap-main.c \ m_ume/elf.c \ + m_ume/macho.c \ m_ume/main.c \ m_ume/script.c @@ -264,6 +338,28 @@ COREGRIND_AIX5_SOURCE = \ m_initimg/initimg-aix5.c \ m_syswrap/syswrap-aix5.c + # Note that the *User.c files are generated using 'mig' from $mach_defs + # above. +COREGRIND_DARWIN_SOURCE = \ + m_aspacemgr/aspacemgr-linux.c \ + m_debuginfo/readdwarf.c \ + m_debuginfo/readdwarf3.c \ + m_debuginfo/readstabs.c \ + m_debuginfo/readmacho.c \ + m_debuginfo/readpdb.c \ + m_mach/mach_basics.c \ + m_mach/mach_msg.c \ + m_initimg/initimg-darwin.c \ + m_initimg/initimg-pathscan.c \ + m_syswrap/syswrap-darwin.c \ + m_syswrap/syswrap-generic.c + +COREGRIND_DARWIN_BUILT_SOURCES = \ + m_mach/mach_vmUser.c \ + m_mach/taskUser.c \ + m_mach/thread_actUser.c \ + m_mach/vm_mapUser.c + libcoregrind_x86_linux_a_SOURCES = \ $(COREGRIND_SOURCES_COMMON) \ $(COREGRIND_LINUX_SOURCE) \ @@ -338,6 +434,37 @@ libcoregrind_ppc64_aix5_a_CFLAGS = $(AM_CFLAGS_PPC64_AIX5) libcoregrind_ppc64_aix5_a_CCASFLAGS = $(AM_CCASFLAGS_PPC64_AIX5) libcoregrind_ppc64_aix5_a_AR = $(AR) -X64 cru +libcoregrind_x86_darwin_a_SOURCES = \ + $(COREGRIND_SOURCES_COMMON) \ + $(COREGRIND_DARWIN_SOURCE) \ + m_coredump/coredump-x86-darwin.c \ + m_dispatch/dispatch-x86-darwin.S \ + m_mach/mach_traps-x86-darwin.S \ + m_sigframe/sigframe-x86-darwin.c \ + m_start-x86-darwin.S \ + m_syswrap/syscall-x86-darwin.S \ + m_syswrap/syswrap-x86-darwin.c +nodist_libcoregrind_x86_darwin_a_SOURCES = $(COREGRIND_DARWIN_BUILT_SOURCES) +libcoregrind_x86_darwin_a_CPPFLAGS = $(AM_CPPFLAGS_X86_DARWIN) +libcoregrind_x86_darwin_a_CFLAGS = $(AM_CFLAGS_X86_DARWIN) +libcoregrind_x86_darwin_a_CCASFLAGS = $(AM_CCASFLAGS_X86_DARWIN) + + +libcoregrind_amd64_darwin_a_SOURCES = \ + $(COREGRIND_SOURCES_COMMON) \ + $(COREGRIND_DARWIN_SOURCE) \ + m_coredump/coredump-amd64-darwin.c \ + m_dispatch/dispatch-amd64-darwin.S \ + m_mach/mach_traps-amd64-darwin.S \ + m_sigframe/sigframe-amd64-darwin.c \ + m_start-amd64-darwin.S \ + m_syswrap/syscall-amd64-darwin.S \ + m_syswrap/syswrap-amd64-darwin.c +nodist_libcoregrind_amd64_darwin_a_SOURCES = $(COREGRIND_DARWIN_BUILT_SOURCES) +libcoregrind_amd64_darwin_a_CPPFLAGS = $(AM_CPPFLAGS_AMD64_DARWIN) +libcoregrind_amd64_darwin_a_CFLAGS = $(AM_CFLAGS_AMD64_DARWIN) +libcoregrind_amd64_darwin_a_CCASFLAGS = $(AM_CCASFLAGS_AMD64_DARWIN) + libreplacemalloc_toolpreload_x86_linux_a_SOURCES = m_replacemalloc/vg_replace_malloc.c libreplacemalloc_toolpreload_x86_linux_a_CPPFLAGS = $(AM_CPPFLAGS_X86_LINUX) @@ -365,18 +492,30 @@ libreplacemalloc_toolpreload_ppc64_aix5_a_CPPFLAGS = $(AM_CPPFLAGS_PPC64_AIX5) libreplacemalloc_toolpreload_ppc64_aix5_a_CFLAGS = $(AM_CFLAGS_PPC64_AIX5) $(AM_CFLAGS_PIC) libreplacemalloc_toolpreload_ppc64_aix5_a_AR = $(AR) -X64 cru +libreplacemalloc_toolpreload_x86_darwin_a_SOURCES = m_replacemalloc/vg_replace_malloc.c +libreplacemalloc_toolpreload_x86_darwin_a_CPPFLAGS = $(AM_CPPFLAGS_X86_DARWIN) +libreplacemalloc_toolpreload_x86_darwin_a_CFLAGS = $(AM_CFLAGS_X86_DARWIN) $(AM_CFLAGS_PIC) + +libreplacemalloc_toolpreload_amd64_darwin_a_SOURCES = m_replacemalloc/vg_replace_malloc.c +libreplacemalloc_toolpreload_amd64_darwin_a_CPPFLAGS = $(AM_CPPFLAGS_AMD64_DARWIN) +libreplacemalloc_toolpreload_amd64_darwin_a_CFLAGS = $(AM_CFLAGS_AMD64_DARWIN) $(AM_CFLAGS_PIC) + m_dispatch/dispatch-x86-linux.S: libvex_guest_offsets.h m_dispatch/dispatch-amd64-linux.S: libvex_guest_offsets.h m_dispatch/dispatch-ppc32-linux.S: libvex_guest_offsets.h m_dispatch/dispatch-ppc64-linux.S: libvex_guest_offsets.h m_dispatch/dispatch-ppc32-aix5.S: libvex_guest_offsets.h m_dispatch/dispatch-ppc64-aix5.S: libvex_guest_offsets.h +m_dispatch/dispatch-x86-darwin.S: libvex_guest_offsets.h +m_dispatch/dispatch-amd64-darwin.S: libvex_guest_offsets.h m_syswrap/syscall-x86-linux.S: libvex_guest_offsets.h m_syswrap/syscall-amd64-linux.S: libvex_guest_offsets.h m_syswrap/syscall-ppc32-linux.S: libvex_guest_offsets.h m_syswrap/syscall-ppc64-linux.S: libvex_guest_offsets.h m_syswrap/syscall-ppc32-aix5.S: libvex_guest_offsets.h m_syswrap/syscall-ppc64-aix5.S: libvex_guest_offsets.h +m_syswrap/syscall-x86-darwin.S: libvex_guest_offsets.h +m_syswrap/syscall-amd64-darwin.S: libvex_guest_offsets.h m_syswrap/syswrap-main.c: libvex_guest_offsets.h libvex_guest_offsets.h: @@ -414,14 +553,25 @@ vgpreload_core_ppc64_aix5_so_CPPFLAGS = $(AM_CPPFLAGS_PPC64_AIX5) vgpreload_core_ppc64_aix5_so_CFLAGS = $(AM_CFLAGS_PPC64_AIX5) $(AM_CFLAGS_PIC) vgpreload_core_ppc64_aix5_so_LDFLAGS = $(PRELOAD_LDFLAGS_PPC64_AIX5) -all-local: inplace-noinst_PROGRAMS +vgpreload_core_x86_darwin_so_SOURCES = $(VGPRELOAD_CORE_SOURCES_COMMON) +vgpreload_core_x86_darwin_so_CPPFLAGS = $(AM_CPPFLAGS_X86_DARWIN) +vgpreload_core_x86_darwin_so_CFLAGS = $(AM_CFLAGS_X86_DARWIN) $(AM_CFLAGS_PIC) +vgpreload_core_x86_darwin_so_LDFLAGS = $(PRELOAD_LDFLAGS_X86_DARWIN) -clean-local: +vgpreload_core_amd64_darwin_so_SOURCES = $(VGPRELOAD_CORE_SOURCES_COMMON) +vgpreload_core_amd64_darwin_so_CPPFLAGS = $(AM_CPPFLAGS_AMD64_DARWIN) +vgpreload_core_amd64_darwin_so_CFLAGS = $(AM_CFLAGS_AMD64_DARWIN) $(AM_CFLAGS_PIC) +vgpreload_core_amd64_darwin_so_LDFLAGS = $(PRELOAD_LDFLAGS_AMD64_DARWIN) + +all-local: inplace-noinst_PROGRAMS inplace-noinst_DSYMS + +clean-local: clean-noinst_DSYMS $(MAKE) -C @VEX_DIR@ CC="$(CC)" AR="$(AR)" clean + rm -f $(mach_srcs) $(mach_server_srcs) $(mach_hdrs) # Nb: The loop installs the libvex library for possible use by standalone # tools. -install-exec-local: install-noinst_PROGRAMS +install-exec-local: install-noinst_PROGRAMS install-noinst_DSYMS for v in $(LIBVEX) ; do \ $(INSTALL_DATA) @VEX_DIR@/$$v $(DESTDIR)$(valdir) ; \ done diff --git a/coregrind/launcher-darwin.c b/coregrind/launcher-darwin.c new file mode 100644 index 000000000..a470cf109 --- /dev/null +++ b/coregrind/launcher-darwin.c @@ -0,0 +1,421 @@ + +/*--------------------------------------------------------------------*/ +/*--- Launching valgrind launcher-darwin.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2007 Julian Seward + jseward@acm.org + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +/* Note: this is a "normal" program and not part of Valgrind proper, + and so it doesn't have to conform to Valgrind's arcane rules on + no-glibc-usage etc. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pub_core_debuglog.h" +#include "pub_core_vki.h" // Avoids warnings from + // pub_core_libcfile.h +#include "pub_core_libcproc.h" // For VALGRIND_LIB, VALGRIND_LAUNCHER +#include "pub_core_ume.h" + +static struct { + cpu_type_t cputype; + const char *apple_name; // e.g. x86_64 + const char *valgrind_name; // e.g. amd64 +} valid_archs[] = { + { CPU_TYPE_X86, "i386", "x86" }, + { CPU_TYPE_X86_64, "x86_64", "amd64" }, + { CPU_TYPE_ARM, "arm", "arm" }, + { CPU_TYPE_POWERPC, "ppc", "ppc32" }, + { CPU_TYPE_POWERPC64, "ppc64", "ppc64" }, +}; +static int valid_archs_count = sizeof(valid_archs)/sizeof(valid_archs[0]); + +static const char *name_for_cputype(cpu_type_t cputype) +{ + int i; + for (i = 0; i < valid_archs_count; i++) { + if (valid_archs[i].cputype == cputype) { + return valid_archs[i].valgrind_name; + } + } + return NULL; +} + +/* Report fatal errors */ +__attribute__((noreturn)) +static void barf ( const char *format, ... ) +{ + va_list vargs; + + va_start(vargs, format); + fprintf(stderr, "valgrind: "); + vfprintf(stderr, format, vargs); + fprintf(stderr, "\n"); + va_end(vargs); + + exit(1); + /*NOTREACHED*/ + assert(0); +} + +/* Search the path for the client program */ +static const char *find_client(const char *clientname) +{ + static char fullname[PATH_MAX]; + const char *path = getenv("PATH"); + const char *colon; + + while (path) + { + if ((colon = strchr(path, ':')) == NULL) + { + strcpy(fullname, path); + path = NULL; + } + else + { + memcpy(fullname, path, colon - path); + fullname[colon - path] = '\0'; + path = colon + 1; + } + + strcat(fullname, "/"); + strcat(fullname, clientname); + + if (access(fullname, R_OK|X_OK) == 0) + return fullname; + } + + return clientname; +} + +static int fat_has_cputype(struct fat_header *fh, cpu_type_t cputype) +{ + struct fat_arch *fa = (struct fat_arch *)(fh+1); + uint32_t nfat_arch = ntohl(fh->nfat_arch); + uint32_t i; + for (i = 0; i < nfat_arch; i++) { + if (ntohl(fa[i].cputype) == cputype) return 1; + } + return 0; +} + +/* Examine the client and work out which arch it is for */ +static const char *select_arch(const char *clientname, cpu_type_t default_cputype, const char *default_arch) +{ + uint8_t buf[4096]; + ssize_t bytes; + int fd = open(find_client(clientname), O_RDONLY); + if (fd < 0) { + barf("%s: %s", clientname, strerror(errno)); + } + + bytes = pread(fd, buf, sizeof(buf), 0); + if (bytes != sizeof(buf)) { + close(fd); + return NULL; + } + + // If it's thin, return that arch. + { + struct mach_header *mh = (struct mach_header *)buf; + if (mh->magic == MH_MAGIC || mh->magic == MH_MAGIC_64) { + return name_for_cputype(mh->cputype); + } else if (mh->magic == MH_CIGAM || mh->magic == MH_CIGAM_64) { + return name_for_cputype(OSSwapInt32(mh->cputype)); + } + } + + // If it's fat, look for a good arch. + { + struct fat_header *fh = (struct fat_header *)buf; + if (ntohl(fh->magic) == FAT_MAGIC) { + uint32_t nfat_arch = ntohl(fh->nfat_arch); + int i; + // If only one fat arch, use it. + if (nfat_arch == 1) { + struct fat_arch *fa = (struct fat_arch *)(fh+1); + return name_for_cputype(ntohl(fa->cputype)); + } + // Scan fat headers for default arch. + if (fat_has_cputype(fh, default_cputype)) { + return default_arch; + } + + // Scan fat headers for any supported arch. + for (i = 0; i < valid_archs_count; i++) { + if (fat_has_cputype(fh, valid_archs[i].cputype)) { + return valid_archs[i].valgrind_name; + } + } + } + } + + return NULL; +} + + +/* Where we expect to find all our aux files */ +static const char *valgrind_lib; + +int main(int argc, char** argv, char** envp) +{ + int i, j, loglevel; + const char *toolname = NULL; + const char *clientname = NULL; + int clientname_arg = 0; + const char *archname = NULL; + const char *arch; + const char *default_arch; + cpu_type_t default_cputype; + char *toolfile; + char launcher_name[PATH_MAX+1]; + char* new_line; + char* set_cwd; + char* cwd; + char** new_env; + char **new_argv; + int new_argc; + + /* Start the debugging-log system ASAP. First find out how many + "-d"s were specified. This is a pre-scan of the command line. + At the same time, look for the tool name. */ + loglevel = 0; + for (i = 1; i < argc; i++) { + if (argv[i][0] != '-') { + clientname = argv[i]; + clientname_arg = i; + break; + } + if (0 == strcmp(argv[i], "--")) { + if (i+1 < argc) { + clientname = argv[i+1]; + clientname_arg = i; + } + break; + } + if (0 == strcmp(argv[i], "-d")) + loglevel++; + if (0 == strncmp(argv[i], "--tool=", 7)) + toolname = argv[i] + 7; + if (0 == strncmp(argv[i], "--arch=", 7)) + archname = argv[i] + 7; + } + + /* ... and start the debug logger. Now we can safely emit logging + messages all through startup. */ + VG_(debugLog_startup)(loglevel, "Stage 1"); + + /* Make sure we know which tool we're using */ + if (toolname) { + VG_(debugLog)(1, "launcher", "tool '%s' requested\n", toolname); + } else { + VG_(debugLog)(1, "launcher", + "no tool requested, defaulting to 'memcheck'\n"); + toolname = "memcheck"; + } + + /* Find the real executable if clientname is an app bundle. */ + if (clientname) { + struct stat st; + if (0 == stat(clientname, &st) && (st.st_mode & S_IFDIR)) { + char *copy = strdup(clientname); + char *appname = basename(copy); + char *dot = strrchr(appname, '.'); + if (dot) { + char *newclient; + *dot = '\0'; + asprintf(&newclient, "%s/Contents/MacOS/%s", clientname, appname); + VG_(debugLog)(1, "launcher", "Using executable in app bundle: %s\n", newclient); + clientname = newclient; + argv[clientname_arg] = newclient; + } + free(copy); + } + } + + /* Establish the correct VALGRIND_LIB. */ + { const char *cp; + cp = getenv(VALGRIND_LIB); + valgrind_lib = ( cp == NULL ? VG_LIBDIR : cp ); + } + + /* Find installed architectures. Use vgpreload_core-.so as the + * indicator of whether the platform is installed. */ + for (i = 0; i < valid_archs_count; i++) { + char *vgpreload_core; + asprintf(&vgpreload_core, "%s/vgpreload_core-%s-darwin.so", valgrind_lib, valid_archs[i].valgrind_name); + if (access(vgpreload_core, R_OK|X_OK) != 0) { + VG_(debugLog)(1, "launcher", "arch '%s' IS NOT installed\n", valid_archs[i].valgrind_name); + bzero(&valid_archs[i], sizeof(valid_archs[i])); + } else { + VG_(debugLog)(1, "launcher", "arch '%s' IS installed\n", valid_archs[i].valgrind_name); + } + free(vgpreload_core); + } + + /* Find the "default" arch (VGCONF_ARCH_PRI from configure). + This is the preferred arch from fat files and the fallback. */ + default_arch = NULL; + default_cputype = 0; + for (i = 0; i < valid_archs_count; i++) { + if (!valid_archs[i].cputype) continue; + if (0 == strncmp(VG_PLATFORM, valid_archs[i].valgrind_name, + strlen(valid_archs[i].valgrind_name))) + { + default_arch = valid_archs[i].valgrind_name; + default_cputype = valid_archs[i].cputype; + break; + } + } + if (i == valid_archs_count) barf("Unknown/uninstalled VG_PLATFORM '%s'", VG_PLATFORM); + assert(NULL != default_arch); + assert(0 != default_cputype); + + /* Work out what arch to use, or use the default arch if not possible. */ + if (archname != NULL) { + // --arch from command line + arch = NULL; + for (i = 0; i < valid_archs_count; i++) { + if (0 == strcmp(archname, valid_archs[i].apple_name) || + 0 == strcmp(archname, valid_archs[i].valgrind_name)) + { + arch = valid_archs[i].valgrind_name; + break; + } + } + if (i == valid_archs_count) barf("Unknown --arch '%s'", archname); + assert(NULL != arch); + VG_(debugLog)(1, "launcher", "using arch '%s' from --arch=%s\n", + arch, archname); + } + else if (clientname == NULL) { + // no client executable; use default as fallback + VG_(debugLog)(1, "launcher", + "no client specified, defaulting arch to '%s'\n", + default_arch); + arch = default_arch; + } + else if ((arch = select_arch(clientname, default_cputype,default_arch))) { + // arch from client executable + VG_(debugLog)(1, "launcher", "selected arch '%s'\n", arch); + } + else { + // nothing found in client executable; use default as fallback + VG_(debugLog)(1, "launcher", + "no arch detected, defaulting arch to '%s'\n", + default_arch); + arch = default_arch; + } + + cwd = getcwd(NULL, 0); + if (!cwd) barf("Current directory no longer exists."); + + /* Figure out the name of this executable (viz, the launcher), so + we can tell stage2. stage2 will use the name for recursive + invokations of valgrind on child processes. */ + memset(launcher_name, 0, PATH_MAX+1); + for (i = 0; envp[i]; i++) + ; /* executable path is after last envp item */ + /* envp[i] == NULL ; envp[i+1] == executable_path */ + if (envp[i+1][0] != '/') { + strcpy(launcher_name, cwd); + strcat(launcher_name, "/"); + } + if (strlen(launcher_name) + strlen(envp[i+1]) > PATH_MAX) + barf("launcher path is too long"); + strcat(launcher_name, envp[i+1]); + VG_(debugLog)(1, "launcher", "launcher_name = %s\n", launcher_name); + + /* tediously augment the env: VALGRIND_LAUNCHER=launcher_name */ + asprintf(&new_line, VALGRIND_LAUNCHER "=%s", launcher_name); + + /* tediously augment the env: VALGRIND_STARTUP_PWD_%PID_XYZZY=current_working_dir */ + asprintf(&set_cwd, "VALGRIND_STARTUP_PWD_%u_XYZZY=%s", getppid(), cwd); + + // Note that Apple binaries get a secret fourth arg, "char* apple", which + // contains the executable path. Don't forget about it. + for (j = 0; envp[j]; j++) + ; + new_env = malloc((j+4) * sizeof(char*)); + if (new_env == NULL) + barf("malloc of new_env failed."); + for (i = 0; i < j; i++) + new_env[i] = envp[i]; + new_env[i++] = new_line; + new_env[i++] = set_cwd; + new_env[i++] = NULL; + new_env[i ] = envp[i-2]; // the 'apple' arg == the executable_path + assert(i == j+3); + + /* tediously edit env: hide dyld options from valgrind's captive dyld */ + for (i = 0; envp[i]; i++) { + if (0 == strncmp(envp[i], "DYLD_", 5)) { + envp[i][0] = 'V'; /* VYLD_; changed back by initimg-darwin */ + } + } + + /* tediously edit argv: remove --arch= */ + new_argv = malloc((1+argc) * sizeof(char *)); + for (i = 0, new_argc = 0; i < argc; i++) { + if (0 == strncmp(argv[i], "--arch=", 7)) { + // skip + } else { + new_argv[new_argc++] = argv[i]; + } + } + new_argv[new_argc++] = NULL; + + /* Build the stage2 invokation, and execve it. Bye! */ + asprintf(&toolfile, "%s/%s-%s-darwin", valgrind_lib, toolname, arch); + if (access(toolfile, R_OK|X_OK) != 0) { + barf("tool '%s' not installed (%s) (%s)", toolname, toolfile, strerror(errno)); + } + + VG_(debugLog)(1, "launcher", "launching %s\n", toolfile); + + execve(toolfile, new_argv, new_env); + + fprintf(stderr, "valgrind: failed to start tool '%s' for platform '%s-darwin': %s\n", + toolname, arch, strerror(errno)); + + exit(1); +} diff --git a/coregrind/m_aspacemgr/aspacemgr-common.c b/coregrind/m_aspacemgr/aspacemgr-common.c index 62c21dab6..b8a7dd8b9 100644 --- a/coregrind/m_aspacemgr/aspacemgr-common.c +++ b/coregrind/m_aspacemgr/aspacemgr-common.c @@ -160,6 +160,18 @@ SysRes VG_(am_do_mmap_NO_NOTIFY)( Addr start, SizeT length, UInt prot, || defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) res = VG_(do_syscall6)(__NR_mmap, (UWord)start, length, prot, flags, fd, offset); +# elif defined(VGP_x86_darwin) + if (fd == 0 && (flags & VKI_MAP_ANONYMOUS)) { + fd = -1; // MAP_ANON with fd==0 is EINVAL + } + res = VG_(do_syscall7)(__NR_mmap, (UWord)start, length, + prot, flags, fd, offset & 0xffffffff, offset >> 32); +# elif defined(VGP_amd64_darwin) + if (fd == 0 && (flags & VKI_MAP_ANONYMOUS)) { + fd = -1; // MAP_ANON with fd==0 is EINVAL + } + res = VG_(do_syscall6)(__NR_mmap, (UWord)start, length, + prot, flags, (UInt)fd, offset); # else # error Unknown platform # endif @@ -177,6 +189,9 @@ SysRes ML_(am_do_munmap_NO_NOTIFY)(Addr start, SizeT length) return VG_(do_syscall2)(__NR_munmap, (UWord)start, length ); } +#if HAVE_MREMAP +/* The following are used only to implement mremap(). */ + SysRes ML_(am_do_extend_mapping_NO_NOTIFY)( Addr old_addr, SizeT old_len, @@ -227,6 +242,8 @@ SysRes ML_(am_do_relocate_nooverlap_mapping_NO_NOTIFY)( # endif } +#endif + /* --- Pertaining to files --- */ SysRes ML_(am_open) ( const Char* pathname, Int flags, Int mode ) @@ -257,6 +274,8 @@ Int ML_(am_fcntl) ( Int fd, Int cmd, Addr arg ) { # if defined(VGO_linux) || defined(VGO_aix5) SysRes res = VG_(do_syscall3)(__NR_fcntl, fd, cmd, arg); +# elif defined(VGO_darwin) + SysRes res = VG_(do_syscall3)(__NR_fcntl_nocancel, fd, cmd, arg); # else # error "Unknown OS" # endif @@ -310,6 +329,17 @@ Bool ML_(am_resolve_filename) ( Int fd, /*OUT*/HChar* buf, Int nbuf ) I_die_here; /* maybe just return False? */ return False; +#elif defined(VGO_darwin) + HChar tmp[VKI_MAXPATHLEN+1]; + if (0 == ML_(am_fcntl)(fd, VKI_F_GETPATH, (UWord)tmp)) { + if (nbuf > 0) { + VG_(strncpy)( buf, tmp, nbuf < sizeof(tmp) ? nbuf : sizeof(tmp) ); + buf[nbuf-1] = 0; + } + if (tmp[0] == '/') return True; + } + return False; + # else # error Unknown OS # endif diff --git a/coregrind/m_aspacemgr/aspacemgr-linux.c b/coregrind/m_aspacemgr/aspacemgr-linux.c index 4f09b6792..0af3bb5f2 100644 --- a/coregrind/m_aspacemgr/aspacemgr-linux.c +++ b/coregrind/m_aspacemgr/aspacemgr-linux.c @@ -3,7 +3,7 @@ /*--- The address space manager: segment initialisation and ---*/ /*--- tracking, stack operations ---*/ /*--- ---*/ -/*--- Implementation for Linux m_aspacemgr-linux.c ---*/ +/*--- Implementation for Linux (and Darwin!) m_aspacemgr-linux.c ---*/ /*--------------------------------------------------------------------*/ /* @@ -848,9 +848,11 @@ static void sync_check_mapping_callback ( Addr addr, SizeT len, UInt prot, /* If a problem has already been detected, don't continue comparing segments, so as to avoid flooding the output with error messages. */ +#if !defined(VGO_darwin) + /* GrP fixme not */ if (!sync_check_ok) return; - +#endif if (len == 0) return; @@ -911,7 +913,7 @@ static void sync_check_mapping_callback ( Addr addr, SizeT len, UInt prot, = nsegments[i].dev != 0 || nsegments[i].ino != 0; /* Consider other reasons to not compare dev/inode */ - +#if defined(VGO_linux) /* bproc does some godawful hack on /dev/zero at process migration, which changes the name of it, and its dev & ino */ if (filename && 0==VG_(strcmp)(filename, "/dev/zero (deleted)")) @@ -920,7 +922,16 @@ static void sync_check_mapping_callback ( Addr addr, SizeT len, UInt prot, /* hack apparently needed on MontaVista Linux */ if (filename && VG_(strstr)(filename, "/.lib-ro/")) cmp_devino = False; +#endif +#if defined(VGO_darwin) + // GrP fixme kernel info doesn't have dev/inode + cmp_devino = False; + + // GrP fixme V and kernel don't agree on offsets + cmp_offsets = False; +#endif + /* If we are doing sloppy execute permission checks then we allow segment to have X permission when we weren't expecting it (but not vice versa) so if the kernel reported execute @@ -971,9 +982,11 @@ static void sync_check_gap_callback ( Addr addr, SizeT len ) /* If a problem has already been detected, don't continue comparing segments, so as to avoid flooding the output with error messages. */ +#if !defined(VGO_darwin) + /* GrP fixme not */ if (!sync_check_ok) return; - +#endif if (len == 0) return; @@ -1517,6 +1530,11 @@ static void read_maps_callback ( Addr addr, SizeT len, UInt prot, seg.kind = SkAnonV; if (dev != 0 && ino != 0) seg.kind = SkFileV; +#if defined(VGO_darwin) + // GrP fixme no dev/ino on darwin + if (offset != 0) + seg.kind = SkFileV; +#endif if (filename) seg.fnIdx = allocate_segname( filename ); @@ -1558,6 +1576,27 @@ Addr VG_(am_startup) ( Addr sp_at_startup ) nsegments[0] = seg; nsegments_used = 1; +#if defined(VGO_darwin) + +# if VG_WORDSIZE == 4 + aspacem_minAddr = (Addr) 0x00001000; + aspacem_maxAddr = (Addr) 0xffffffff; + + aspacem_cStart = aspacem_minAddr; + aspacem_vStart = 0xf0000000; // 0xc0000000..0xf0000000 available +# else + aspacem_minAddr = (Addr) 0x100000000; // 4GB page zero + aspacem_maxAddr = (Addr) 0x7fffffffffff; + + aspacem_cStart = aspacem_minAddr; + aspacem_vStart = 0x700000000000; // 0x7000:00000000..0x7fff:5c000000 avail + // 0x7fff:5c000000..0x7fff:ffe00000? is stack, dyld, shared cache +# endif + + suggested_clstack_top = -1; // ignored; Mach-O specifies its stack + +#else + /* Establish address limits and block out unusable parts accordingly. */ @@ -1588,6 +1627,8 @@ Addr VG_(am_startup) ( Addr sp_at_startup ) suggested_clstack_top = aspacem_maxAddr - 16*1024*1024ULL + VKI_PAGE_SIZE; +#endif + aspacem_assert(VG_IS_PAGE_ALIGNED(aspacem_minAddr)); aspacem_assert(VG_IS_PAGE_ALIGNED(aspacem_maxAddr + 1)); aspacem_assert(VG_IS_PAGE_ALIGNED(aspacem_cStart)); @@ -2089,6 +2130,12 @@ Bool VG_(am_notify_munmap)( Addr start, SizeT len ) SysRes VG_(am_mmap_file_fixed_client) ( Addr start, SizeT length, UInt prot, Int fd, Off64T offset ) +{ + return VG_(am_mmap_named_file_fixed_client)(start, length, prot, fd, offset, NULL); +} + +SysRes VG_(am_mmap_named_file_fixed_client) + ( Addr start, SizeT length, UInt prot, Int fd, Off64T offset, const HChar *name ) { SysRes sres; NSegment seg; @@ -2116,6 +2163,7 @@ SysRes VG_(am_mmap_file_fixed_client) /* We have been advised that the mapping is allowable at the specified address. So hand it off to the kernel, and propagate any resulting failure immediately. */ + // DDD: #warning GrP fixme MAP_FIXED can clobber memory! sres = VG_(am_do_mmap_NO_NOTIFY)( start, length, prot, VKI_MAP_FIXED|VKI_MAP_PRIVATE, @@ -2146,7 +2194,9 @@ SysRes VG_(am_mmap_file_fixed_client) seg.ino = ino; seg.mode = mode; } - if (ML_(am_resolve_filename)(fd, buf, VKI_PATH_MAX)) { + if (name) { + seg.fnIdx = allocate_segname( name ); + } else if (ML_(am_resolve_filename)(fd, buf, VKI_PATH_MAX)) { seg.fnIdx = allocate_segname( buf ); } add_segment( &seg ); @@ -2182,6 +2232,7 @@ SysRes VG_(am_mmap_anon_fixed_client) ( Addr start, SizeT length, UInt prot ) /* We have been advised that the mapping is allowable at the specified address. So hand it off to the kernel, and propagate any resulting failure immediately. */ + // DDD: #warning GrP fixme MAP_FIXED can clobber memory! sres = VG_(am_do_mmap_NO_NOTIFY)( start, length, prot, VKI_MAP_FIXED|VKI_MAP_PRIVATE|VKI_MAP_ANONYMOUS, @@ -2239,6 +2290,7 @@ SysRes VG_(am_mmap_anon_float_client) ( SizeT length, Int prot ) /* We have been advised that the mapping is allowable at the advised address. So hand it off to the kernel, and propagate any resulting failure immediately. */ + // DDD: #warning GrP fixme MAP_FIXED can clobber memory! sres = VG_(am_do_mmap_NO_NOTIFY)( advised, length, prot, VKI_MAP_FIXED|VKI_MAP_PRIVATE|VKI_MAP_ANONYMOUS, @@ -2309,18 +2361,41 @@ SysRes VG_(am_mmap_anon_float_valgrind)( SizeT length ) if (!ok) return VG_(mk_SysRes_Error)( VKI_EINVAL ); +// On Darwin, for anonymous maps you can pass in a tag which is used by +// programs like vmmap for statistical purposes. +#ifndef VM_TAG_VALGRIND +# define VM_TAG_VALGRIND 0 +#endif + /* We have been advised that the mapping is allowable at the specified address. So hand it off to the kernel, and propagate any resulting failure immediately. */ + /* GrP fixme darwin: use advisory as a hint only, otherwise syscall in + another thread can pre-empt our spot. [At one point on the DARWIN + branch the VKI_MAP_FIXED was commented out; unclear if this is + necessary or not given the second Darwin-only call that immediately + follows if this one fails. --njn] */ sres = VG_(am_do_mmap_NO_NOTIFY)( advised, length, VKI_PROT_READ|VKI_PROT_WRITE|VKI_PROT_EXEC, VKI_MAP_FIXED|VKI_MAP_PRIVATE|VKI_MAP_ANONYMOUS, - 0, 0 + VM_TAG_VALGRIND, 0 ); +#if defined(VGO_darwin) + if (sr_isError(sres)) { + /* try again, ignoring the advisory */ + sres = VG_(am_do_mmap_NO_NOTIFY)( + 0, length, + VKI_PROT_READ|VKI_PROT_WRITE|VKI_PROT_EXEC, + /*VKI_MAP_FIXED|*/VKI_MAP_PRIVATE|VKI_MAP_ANONYMOUS, + VM_TAG_VALGRIND, 0 + ); + } +#endif if (sr_isError(sres)) return sres; +#if defined(VGO_linux) if (sr_Res(sres) != advised) { /* I don't think this can happen. It means the kernel made a fixed map succeed but not at the requested location. Try to @@ -2328,6 +2403,7 @@ SysRes VG_(am_mmap_anon_float_valgrind)( SizeT length ) (void)ML_(am_do_munmap_NO_NOTIFY)( sr_Res(sres), length ); return VG_(mk_SysRes_Error)( VKI_EINVAL ); } +#endif /* Ok, the mapping succeeded. Now notify the interval map. */ init_nsegment( &seg ); @@ -2687,6 +2763,7 @@ Bool VG_(am_extend_into_adjacent_reservation_client) ( NSegment* seg, return False; /* Extend the kernel's mapping. */ + // DDD: #warning GrP fixme MAP_FIXED can clobber memory! sres = VG_(am_do_mmap_NO_NOTIFY)( nsegments[segR].start, delta, prot, @@ -2722,6 +2799,7 @@ Bool VG_(am_extend_into_adjacent_reservation_client) ( NSegment* seg, return False; /* Extend the kernel's mapping. */ + // DDD: #warning GrP fixme MAP_FIXED can clobber memory! sres = VG_(am_do_mmap_NO_NOTIFY)( nsegments[segA].start-delta, delta, prot, @@ -2750,6 +2828,8 @@ Bool VG_(am_extend_into_adjacent_reservation_client) ( NSegment* seg, /* --- --- --- resizing/move a mapping --- --- --- */ +#if HAVE_MREMAP + /* Let SEG be a client mapping (anonymous or file). This fn extends the mapping forwards only by DELTA bytes, and trashes whatever was in the new area. Fails if SEG is not a single client mapping or if @@ -2894,6 +2974,10 @@ Bool VG_(am_relocate_nooverlap_client)( /*OUT*/Bool* need_discard, return True; } +#endif // HAVE_MREMAP + + +#if HAVE_PROC /*-----------------------------------------------------------------*/ /*--- ---*/ @@ -3192,6 +3276,202 @@ static void parse_procselfmaps ( (*record_gap) ( gapStart, Addr_MAX - gapStart + 1 ); } +#elif defined(VGO_darwin) +#include +#include + +static unsigned int mach2vki(unsigned int vm_prot) +{ + return + ((vm_prot & VM_PROT_READ) ? VKI_PROT_READ : 0) | + ((vm_prot & VM_PROT_WRITE) ? VKI_PROT_WRITE : 0) | + ((vm_prot & VM_PROT_EXECUTE) ? VKI_PROT_EXEC : 0) ; +} + +static UInt stats_machcalls = 0; + +static void parse_procselfmaps ( + void (*record_mapping)( Addr addr, SizeT len, UInt prot, + ULong dev, ULong ino, Off64T offset, + const UChar* filename ), + void (*record_gap)( Addr addr, SizeT len ) + ) +{ + vm_address_t iter; + unsigned int depth; + vm_address_t last; + + iter = 0; + depth = 0; + last = 0; + while (1) { + mach_vm_address_t addr = iter; + mach_vm_size_t size; + vm_region_submap_short_info_data_64_t info; + kern_return_t kr; + + while (1) { + mach_msg_type_number_t info_count + = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; + stats_machcalls++; + kr = mach_vm_region_recurse(mach_task_self(), &addr, &size, &depth, + (vm_region_info_t)&info, &info_count); + if (kr) + return; + if (info.is_submap) { + depth++; + continue; + } + break; + } + iter = addr + size; + + if (addr > last && record_gap) { + (*record_gap)(last, addr - last); + } + if (record_mapping) { + (*record_mapping)(addr, size, mach2vki(info.protection), + 0, 0, info.offset, NULL); + } + last = addr + size; + } + + if ((Addr)-1 > last && record_gap) + (*record_gap)(last, (Addr)-1 - last); +} + +ChangedSeg* css_local; +Int css_size_local; +Int css_used_local; + +static void add_mapping_callback(Addr addr, SizeT len, UInt prot, + ULong dev, ULong ino, Off64T offset, + const UChar *filename) +{ + // derived from sync_check_mapping_callback() + + Int iLo, iHi, i; + + if (len == 0) return; + + /* The kernel should not give us wraparounds. */ + aspacem_assert(addr <= addr + len - 1); + + iLo = find_nsegment_idx( addr ); + iHi = find_nsegment_idx( addr + len - 1 ); + + + /* NSegments iLo .. iHi inclusive should agree with the presented + data. */ + for (i = iLo; i <= iHi; i++) { + + UInt seg_prot; + + if (nsegments[i].kind == SkAnonV || nsegments[i].kind == SkFileV) { + /* Ignore V regions */ + continue; + } + else if (nsegments[i].kind == SkFree || nsegments[i].kind == SkResvn) { + /* Add mapping for SkResvn regions */ + ChangedSeg* cs = &css_local[css_used_local]; + aspacem_assert(css_used_local < css_size_local); + cs->is_added = True; + cs->start = addr; + cs->end = addr + len - 1; + cs->prot = prot; + cs->offset = offset; + css_used_local++; + return; + + } else if (nsegments[i].kind == SkAnonC || + nsegments[i].kind == SkFileC || + nsegments[i].kind == SkShmC) + { + /* Check permissions on client regions */ + // GrP fixme + seg_prot = 0; + if (nsegments[i].hasR) seg_prot |= VKI_PROT_READ; + if (nsegments[i].hasW) seg_prot |= VKI_PROT_WRITE; +# if defined(VGA_x86) + // GrP fixme sloppyXcheck + // darwin: kernel X ignored and spuriously changes? (vm_copy) + seg_prot |= (prot & VKI_PROT_EXEC); +# else + if (nsegments[i].hasX) seg_prot |= VKI_PROT_EXEC; +# endif + if (seg_prot != prot) { + if (VG_(clo_trace_syscalls)) + VG_(debugLog)(0,"aspacem","\nregion %p..%p permission " + "mismatch (kernel %x, V %x)", + (void*)nsegments[i].start, + (void*)(nsegments[i].end+1), prot, seg_prot); + } + + } else { + aspacem_assert(0); + } + } +} + +static void remove_mapping_callback(Addr addr, SizeT len) +{ + // derived from sync_check_gap_callback() + + Int iLo, iHi, i; + + if (len == 0) + return; + + /* The kernel should not give us wraparounds. */ + aspacem_assert(addr <= addr + len - 1); + + iLo = find_nsegment_idx( addr ); + iHi = find_nsegment_idx( addr + len - 1 ); + + /* NSegments iLo .. iHi inclusive should agree with the presented data. */ + for (i = iLo; i <= iHi; i++) { + if (nsegments[i].kind != SkFree && nsegments[i].kind != SkResvn) { + // V has a mapping, kernel doesn't + ChangedSeg* cs = &css_local[css_used_local]; + aspacem_assert(css_used_local < css_size_local); + cs->is_added = True; + cs->is_added = False; + cs->start = nsegments[i].start; + cs->end = nsegments[i].end; + cs->prot = 0; + cs->offset = 0; + css_used_local++; + return; + } + } +} + + +void VG_(get_changed_segments)( + const HChar* when, const HChar* where, /*OUT*/ChangedSeg* css, + Int css_size, /*OUT*/Int* css_used) +{ + static UInt stats_synccalls = 1; + aspacem_assert(when && where); + + if (0) + VG_(debugLog)(0,"aspacem", + "[%u,%u] VG_(get_changed_segments)(%s, %s)\n", + stats_synccalls++, stats_machcalls, when, where + ); + + css_local = css; + css_size_local = css_size; + css_used_local = 0; + + // Get the list of segs that need to be added/removed. + parse_procselfmaps(&add_mapping_callback, &remove_mapping_callback); + + *css_used = css_used_local; +} + +#endif + /*--------------------------------------------------------------------*/ /*--- end ---*/ /*--------------------------------------------------------------------*/ diff --git a/coregrind/m_coredump/coredump-amd64-darwin.c b/coregrind/m_coredump/coredump-amd64-darwin.c new file mode 100644 index 000000000..6b18f3233 --- /dev/null +++ b/coregrind/m_coredump/coredump-amd64-darwin.c @@ -0,0 +1,39 @@ + +/*--------------------------------------------------------------------*/ +/*--- Dumping core. coredump-amd64-darwin.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2005 Apple Inc. + Greg Parker gparker@apple.com + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics.h" +#include "pub_core_vki.h" +#include "pub_core_coredump.h" +#include "pub_core_threadstate.h" + +void VG_(make_coredump)(ThreadId tid, const vki_siginfo_t *si, UInt max_size) +{ + // DDD: #warning GrP fixme coredump +} diff --git a/coregrind/m_coredump/coredump-x86-darwin.c b/coregrind/m_coredump/coredump-x86-darwin.c new file mode 100644 index 000000000..3618113cb --- /dev/null +++ b/coregrind/m_coredump/coredump-x86-darwin.c @@ -0,0 +1,39 @@ + +/*--------------------------------------------------------------------*/ +/*--- Dumping core. coredump-x86-darwin.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2005 Apple Inc. + Greg Parker gparker@apple.com + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics.h" +#include "pub_core_vki.h" +#include "pub_core_coredump.h" +#include "pub_core_threadstate.h" + +void VG_(make_coredump)(ThreadId tid, const vki_siginfo_t *si, UInt max_size) +{ + // DDD: #warning GrP fixme coredump +} diff --git a/coregrind/m_debugger.c b/coregrind/m_debugger.c index de28e3633..1bc9fe5f1 100644 --- a/coregrind/m_debugger.c +++ b/coregrind/m_debugger.c @@ -211,6 +211,12 @@ static Int ptrace_setregs(Int pid, VexGuestArchState* vex) #elif defined(VGP_ppc64_aix5) I_die_here; +#elif defined(VGP_x86_darwin) + I_die_here; + +#elif defined(VGP_amd64_darwin) + I_die_here; + #else # error Unknown arch #endif diff --git a/coregrind/m_debuginfo/d3basics.c b/coregrind/m_debuginfo/d3basics.c index f561c4cd5..055f81468 100644 --- a/coregrind/m_debuginfo/d3basics.c +++ b/coregrind/m_debuginfo/d3basics.c @@ -379,10 +379,10 @@ static Long read_leb128S( UChar **data ) static Bool get_Dwarf_Reg( /*OUT*/Addr* a, Word regno, RegSummary* regs ) { vg_assert(regs); -# if defined(VGP_x86_linux) +# if defined(VGP_x86_linux) || defined(VGP_x86_darwin) if (regno == 5/*EBP*/) { *a = regs->fp; return True; } if (regno == 4/*ESP*/) { *a = regs->sp; return True; } -# elif defined(VGP_amd64_linux) +# elif defined(VGP_amd64_linux) || defined(VGP_amd64_darwin) if (regno == 6/*RBP*/) { *a = regs->fp; return True; } if (regno == 7/*RSP*/) { *a = regs->sp; return True; } # elif defined(VGP_ppc32_linux) diff --git a/coregrind/m_debuginfo/debuginfo.c b/coregrind/m_debuginfo/debuginfo.c index d9d921777..4ec85c802 100644 --- a/coregrind/m_debuginfo/debuginfo.c +++ b/coregrind/m_debuginfo/debuginfo.c @@ -51,6 +51,7 @@ #include "pub_core_xarray.h" #include "pub_core_oset.h" #include "pub_core_stacktrace.h" // VG_(get_StackTrace) XXX: circular dependency +#include "pub_core_ume.h" #include "priv_misc.h" /* dinfo_zalloc/free */ #include "priv_d3basics.h" /* ML_(pp_GX) */ @@ -67,6 +68,9 @@ # include "pub_core_libcproc.h" # include "pub_core_libcfile.h" # include "priv_readxcoff.h" +#elif defined(VGO_darwin) +# include "priv_readmacho.h" +# include "priv_readpdb.h" #endif @@ -576,7 +580,7 @@ void VG_(di_initialise) ( void ) /*--- ---*/ /*--------------------------------------------------------------*/ -#if defined(VGO_linux) +#if defined(VGO_linux) || defined(VGO_darwin) /* The debug info system is driven by notifications that a text segment has been mapped in, or unmapped. When that happens it @@ -703,11 +707,10 @@ ULong VG_(di_notify_mmap)( Addr a, Bool allow_SkFileV ) */ is_rx_map = False; is_rw_map = False; -# if defined(VGP_x86_linux) +# if defined(VGA_x86) is_rx_map = seg->hasR && seg->hasX; is_rw_map = seg->hasR && seg->hasW; -# elif defined(VGP_amd64_linux) \ - || defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux) +# elif defined(VGA_amd64) || defined(VGA_ppc32) || defined(VGA_ppc64) is_rx_map = seg->hasR && seg->hasX && !seg->hasW; is_rw_map = seg->hasR && seg->hasW && !seg->hasX; # else @@ -750,8 +753,15 @@ ULong VG_(di_notify_mmap)( Addr a, Bool allow_SkFileV ) vg_assert(nread > 0 && nread <= sizeof(buf1k) ); /* We're only interested in mappings of ELF object files. */ +#if defined(HAVE_ELF) if (!ML_(is_elf_object_file)( buf1k, (SizeT)nread )) return 0; +#elif defined(HAVE_MACHO) + if (!ML_(is_macho_object_file)( buf1k, (SizeT)nread )) + return 0; +#else +# error "unknown executable type" +#endif /* See if we have a DebugInfo for this filename. If not, create one. */ @@ -803,7 +813,13 @@ ULong VG_(di_notify_mmap)( Addr a, Bool allow_SkFileV ) discard_DebugInfos_which_overlap_with( di ); /* .. and acquire new info. */ +#if defined(HAVE_ELF) ok = ML_(read_elf_debug_info)( di ); +#elif defined(HAVE_MACHO) + ok = ML_(read_macho_debug_info)( di ); +#else +# error "unknown executable type" +#endif if (ok) { @@ -863,7 +879,7 @@ void VG_(di_notify_munmap)( Addr a, SizeT len ) void VG_(di_notify_mprotect)( Addr a, SizeT len, UInt prot ) { Bool exe_ok = toBool(prot & VKI_PROT_EXEC); -# if defined(VGP_x86_linux) +# if defined(VGA_x86) exe_ok = exe_ok || toBool(prot & VKI_PROT_READ); # endif if (0 && !exe_ok) { @@ -1383,6 +1399,9 @@ Vg_FnNameKind VG_(get_fnname_kind) ( Char* name ) VG_STREQ("generic_start_main", name) || // Yellow Dog doggedness #elif defined(VGO_aix5) VG_STREQ("__start", name) || // AIX aches +#elif defined(VGO_darwin) + // See readmacho.c for an explanation of this. + VG_STREQ("start_according_to_valgrind", name) || // Darwin, darling #else # error Unknown OS #endif @@ -3384,7 +3403,6 @@ VgSectKind VG_(seginfo_sect_kind)( /*OUT*/UChar* name, SizeT n_name, } - /*--------------------------------------------------------------------*/ /*--- end ---*/ /*--------------------------------------------------------------------*/ diff --git a/coregrind/m_debuginfo/priv_readmacho.h b/coregrind/m_debuginfo/priv_readmacho.h new file mode 100644 index 000000000..e9da383a7 --- /dev/null +++ b/coregrind/m_debuginfo/priv_readmacho.h @@ -0,0 +1,52 @@ + +/*--------------------------------------------------------------------*/ +/*--- Reading of syms & debug info from Mach-O files. ---*/ +/*--- priv_readmacho.h ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2006 Apple Inc. + Greg Parker gparker@apple.com + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#ifndef __PRIV_READMACHO_H +#define __PRIV_READMACHO_H + +/* Identify a Mach-O object file by peering at the first few bytes of + it. */ +extern Bool ML_(is_macho_object_file)( const void* buf, SizeT size ); + +/* The central function for reading Mach-O debug info. For the + object/exe specified by the DebugInfo, find Mach-O sections, then read + the symbols, line number info, file name info, CFA (stack-unwind + info) and anything else we want, into the tables within the + supplied DebugInfo. +*/ +extern Bool ML_(read_macho_debug_info) ( struct _DebugInfo* si ); + + +#endif /* ndef __PRIV_READMACHO_H */ + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_debuginfo/priv_storage.h b/coregrind/m_debuginfo/priv_storage.h index 9749b2472..f6e6e82b0 100644 --- a/coregrind/m_debuginfo/priv_storage.h +++ b/coregrind/m_debuginfo/priv_storage.h @@ -619,6 +619,12 @@ void ML_(addLineInfo) ( struct _DebugInfo* di, UChar* dirname, /* NULL is allowable */ Addr this, Addr next, Int lineno, Int entry); +/* Shrink completed tables to save memory. */ +extern +void ML_(shrinkSym) ( struct _DebugInfo *di ); +extern +void ML_(shrinkLineInfo) ( struct _DebugInfo *di ); + /* Add a CFI summary record. The supplied DiCfSI is copied. */ extern void ML_(addDiCfSI) ( struct _DebugInfo* di, DiCfSI* cfsi ); diff --git a/coregrind/m_debuginfo/readdwarf.c b/coregrind/m_debuginfo/readdwarf.c index 30df436dc..69000e63e 100644 --- a/coregrind/m_debuginfo/readdwarf.c +++ b/coregrind/m_debuginfo/readdwarf.c @@ -1785,6 +1785,14 @@ void ML_(read_debuginfo_dwarf1) ( # define FP_REG 1 # define SP_REG 1 # define RA_REG_DEFAULT 8 // CAB: What's a good default ? +#elif defined(VGP_x86_darwin) +# define FP_REG 5 +# define SP_REG 4 +# define RA_REG_DEFAULT 8 +#elif defined(VGP_amd64_darwin) +# define FP_REG 6 +# define SP_REG 7 +# define RA_REG_DEFAULT 16 #else # error "Unknown platform" #endif diff --git a/coregrind/m_debuginfo/readdwarf3.c b/coregrind/m_debuginfo/readdwarf3.c index d8eed6a19..d94b17dea 100644 --- a/coregrind/m_debuginfo/readdwarf3.c +++ b/coregrind/m_debuginfo/readdwarf3.c @@ -138,6 +138,7 @@ #include "pub_core_libcassert.h" #include "pub_core_libcprint.h" #include "pub_core_options.h" +#include "pub_core_tooliface.h" /* VG_(needs) */ #include "pub_core_xarray.h" #include "pub_core_wordfm.h" #include "priv_misc.h" /* dinfo_zalloc/free */ @@ -3903,6 +3904,7 @@ ML_(new_dwarf3_reader) ( TRACE_SYMTAB("\n"); #endif + /*--------------------------------------------------------------------*/ /*--- end readdwarf3.c ---*/ /*--------------------------------------------------------------------*/ diff --git a/coregrind/m_debuginfo/readmacho.c b/coregrind/m_debuginfo/readmacho.c new file mode 100644 index 000000000..76293ad31 --- /dev/null +++ b/coregrind/m_debuginfo/readmacho.c @@ -0,0 +1,1095 @@ + +/*--------------------------------------------------------------------*/ +/*--- Reading of syms & debug info from Mach-O files. ---*/ +/*--- readmacho.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2005-2009 Apple Inc. + Greg Parker gparker@apple.com + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics.h" +#include "pub_core_vki.h" +#include "pub_core_libcbase.h" +#include "pub_core_libcprint.h" +#include "pub_core_libcassert.h" +#include "pub_core_libcfile.h" +#include "pub_core_libcproc.h" +#include "pub_core_aspacemgr.h" /* for mmaping debuginfo files */ +#include "pub_core_machine.h" /* VG_ELF_CLASS */ +#include "pub_core_options.h" +#include "pub_core_oset.h" +#include "pub_core_tooliface.h" /* VG_(needs) */ +#include "pub_core_xarray.h" +#include "pub_core_clientstate.h" +#include "pub_core_debuginfo.h" + +#include "priv_d3basics.h" +#include "priv_misc.h" +#include "priv_tytypes.h" +#include "priv_storage.h" +#include "priv_readmacho.h" +#include "priv_readdwarf.h" +#include "priv_readdwarf3.h" +#include "priv_readstabs.h" + +/* --- !!! --- EXTERNAL HEADERS start --- !!! --- */ +#include +#include +#include +/* --- !!! --- EXTERNAL HEADERS end --- !!! --- */ + +#if VG_WORDSIZE == 4 +# define MAGIC MH_MAGIC +# define MACH_HEADER mach_header +# define LC_SEGMENT_CMD LC_SEGMENT +# define SEGMENT_COMMAND segment_command +# define SECTION section +# define NLIST nlist +#else +# define MAGIC MH_MAGIC_64 +# define MACH_HEADER mach_header_64 +# define LC_SEGMENT_CMD LC_SEGMENT_64 +# define SEGMENT_COMMAND segment_command_64 +# define SECTION section_64 +# define NLIST nlist_64 +#endif + + +/*------------------------------------------------------------*/ +/*--- ---*/ +/*--- Mach-O file mapping/unmapping helpers ---*/ +/*--- ---*/ +/*------------------------------------------------------------*/ + +typedef + struct { + /* These two describe the entire mapped-in ("primary") image, + fat headers, kitchen sink, whatnot: the entire file. The + image is mapped into img[0 .. img_szB-1]. */ + UChar* img; + SizeT img_szB; + /* These two describe the Mach-O object of interest, which is + presumably somewhere inside the primary image. + map_image_aboard() below, which generates this info, will + carefully check that the macho_ fields denote a section of + memory that falls entirely inside img[0 .. img_szB-1]. */ + UChar* macho_img; + SizeT macho_img_szB; + } + ImageInfo; + + +Bool ML_(is_macho_object_file)( const void* buf, SizeT szB ) +{ + /* (JRS: the Mach-O headers might not be in this mapped data, + because we only mapped a page for this initial check, + or at least not very much, and what's at the start of the file + is in general a so-called fat header. The Mach-O object we're + interested in could be arbitrarily far along the image, and so + we can't assume its header will fall within this page.) */ + + /* But we can say that either it's a fat object, in which case it + begins with a fat header, or it's unadorned Mach-O, in which + case it starts with a normal header. At least do what checks we + can to establish whether or not we're looking at something + sane. */ + + const struct fat_header* fh_be = buf; + const struct MACH_HEADER* mh = buf; + + vg_assert(buf); + if (szB < sizeof(struct fat_header)) + return False; + if (VG_(ntohl)(fh_be->magic) == FAT_MAGIC) + return True; + + if (szB < sizeof(struct MACH_HEADER)) + return False; + if (mh->magic == MAGIC) + return True; + + return False; +} + + +/* Unmap an image mapped in by map_image_aboard. */ +static void unmap_image ( /*MOD*/ImageInfo* ii ) +{ + SysRes sres; + vg_assert(ii->img); + vg_assert(ii->img_szB > 0); + sres = VG_(am_munmap_valgrind)( (Addr)ii->img, ii->img_szB ); + /* Do we care if this fails? I suppose so; it would indicate + some fairly serious snafu with the mapping of the file. */ + vg_assert( !sr_isError(sres) ); + VG_(memset)(ii, 0, sizeof(*ii)); +} + + +/* Map a given fat or thin object aboard, find the thin part if + necessary, do some checks, and write details of both the fat and + thin parts into *ii. Returns False (and leaves the file unmapped) + on failure. Guarantees to return pointers to a valid(ish) Mach-O + image if it succeeds. */ +static Bool map_image_aboard ( DebugInfo* di, /* only for err msgs */ + /*OUT*/ImageInfo* ii, UChar* filename ) +{ + VG_(memset)(ii, 0, sizeof(*ii)); + + /* First off, try to map the thing in. */ + { SizeT size; + SysRes fd, sres; + struct vg_stat stat_buf; + + fd = VG_(stat)(filename, &stat_buf); + if (sr_isError(fd)) { + ML_(symerr)(di, True, "Can't stat image (to determine its size)?!"); + return False; + } + size = stat_buf.size; + + fd = VG_(open)(filename, VKI_O_RDONLY, 0); + if (sr_isError(fd)) { + ML_(symerr)(di, True, "Can't open image to read symbols?!"); + return False; + } + + sres = VG_(am_mmap_file_float_valgrind) + ( size, VKI_PROT_READ, sr_Res(fd), 0 ); + if (sr_isError(sres)) { + ML_(symerr)(di, True, "Can't mmap image to read symbols?!"); + return False; + } + + VG_(close)(sr_Res(fd)); + + ii->img = (UChar*)sr_Res(sres); + ii->img_szB = size; + } + + /* Now it's mapped in and we have .img and .img_szB set. Look for + the embedded Mach-O object. If not findable, unmap and fail. */ + { struct fat_header* fh_be; + struct fat_header fh; + struct MACH_HEADER* mh; + + // Assume initially that we have a thin image, and update + // these if it turns out to be fat. + ii->macho_img = ii->img; + ii->macho_img_szB = ii->img_szB; + + // Check for fat header. + if (ii->img_szB < sizeof(struct fat_header)) { + ML_(symerr)(di, True, "Invalid Mach-O file (0 too small)."); + goto unmap_and_fail; + } + + // Fat header is always BIG-ENDIAN + fh_be = (struct fat_header *)ii->img; + fh.magic = VG_(ntohl)(fh_be->magic); + fh.nfat_arch = VG_(ntohl)(fh_be->nfat_arch); + if (fh.magic == FAT_MAGIC) { + // Look for a good architecture. + struct fat_arch *arch_be; + struct fat_arch arch; + Int f; + if (ii->img_szB < sizeof(struct fat_header) + + fh.nfat_arch * sizeof(struct fat_arch)) { + ML_(symerr)(di, True, "Invalid Mach-O file (1 too small)."); + goto unmap_and_fail; + } + for (f = 0, arch_be = (struct fat_arch *)(fh_be+1); + f < fh.nfat_arch; + f++, arch_be++) { + Int cputype; +# if defined(VGA_ppc) + cputype = CPU_TYPE_POWERPC; +# elif defined(VGA_ppc64) + cputype = CPU_TYPE_POWERPC64; +# elif defined(VGA_x86) + cputype = CPU_TYPE_X86; +# elif defined(VGA_amd64) + cputype = CPU_TYPE_X86_64; +# else +# error "unknown architecture" +# endif + arch.cputype = VG_(ntohl)(arch_be->cputype); + arch.cpusubtype = VG_(ntohl)(arch_be->cpusubtype); + arch.offset = VG_(ntohl)(arch_be->offset); + arch.size = VG_(ntohl)(arch_be->size); + if (arch.cputype == cputype) { + if (ii->img_szB < arch.offset + arch.size) { + ML_(symerr)(di, True, "Invalid Mach-O file (2 too small)."); + goto unmap_and_fail; + } + ii->macho_img = ii->img + arch.offset; + ii->macho_img_szB = arch.size; + break; + } + } + if (f == fh.nfat_arch) { + ML_(symerr)(di, True, + "No acceptable architecture found in fat file."); + goto unmap_and_fail; + } + } + + /* Sanity check what we found. */ + + /* assured by logic above */ + vg_assert(ii->img_szB >= sizeof(struct fat_header)); + + if (ii->macho_img_szB < sizeof(struct MACH_HEADER)) { + ML_(symerr)(di, True, "Invalid Mach-O file (3 too small)."); + goto unmap_and_fail; + } + + if (ii->macho_img_szB > ii->img_szB) { + ML_(symerr)(di, True, "Invalid Mach-O file (thin bigger than fat)."); + goto unmap_and_fail; + } + + if (ii->macho_img >= ii->img + && ii->macho_img + ii->macho_img_szB <= ii->img + ii->img_szB) { + /* thin entirely within fat, as expected */ + } else { + ML_(symerr)(di, True, "Invalid Mach-O file (thin not inside fat)."); + goto unmap_and_fail; + } + + mh = (struct MACH_HEADER *)ii->macho_img; + if (mh->magic != MAGIC) { + ML_(symerr)(di, True, "Invalid Mach-O file (bad magic)."); + goto unmap_and_fail; + } + + if (ii->macho_img_szB < sizeof(struct MACH_HEADER) + mh->sizeofcmds) { + ML_(symerr)(di, True, "Invalid Mach-O file (4 too small)."); + goto unmap_and_fail; + } + } + + vg_assert(ii->img); + vg_assert(ii->macho_img); + vg_assert(ii->img_szB > 0); + vg_assert(ii->macho_img_szB > 0); + vg_assert(ii->macho_img >= ii->img); + vg_assert(ii->macho_img + ii->macho_img_szB <= ii->img + ii->img_szB); + return True; /* success */ + /*NOTREACHED*/ + + unmap_and_fail: + unmap_image(ii); + return False; /* bah! */ +} + + +/*------------------------------------------------------------*/ +/*--- ---*/ +/*--- Mach-O symbol table reading ---*/ +/*--- ---*/ +/*------------------------------------------------------------*/ + +/* Read a symbol table (nlist). Add the resulting candidate symbols + to 'syms'; the caller will post-process them and hand them off to + ML_(addSym) itself. */ +static +void read_symtab( /*OUT*/XArray* /* DiSym */ syms, + struct _DebugInfo* di, + struct NLIST* o_symtab, UInt o_symtab_count, + UChar* o_strtab, UInt o_strtab_sz ) +{ + Int i; + Addr sym_addr; + DiSym risym; + UChar* name; + + static UChar* s_a_t_v = NULL; /* do not make non-static */ + + for (i = 0; i < o_symtab_count; i++) { + struct NLIST *nl = o_symtab+i; + if ((nl->n_type & N_TYPE) == N_SECT) { + sym_addr = di->text_bias + nl->n_value; + /*} else if ((nl->n_type & N_TYPE) == N_ABS) { + GrP fixme don't ignore absolute symbols? + sym_addr = nl->n_value; */ + } else { + continue; + } + + if (di->trace_symtab) + VG_(printf)("nlist raw: avma %010lx %s\n", + sym_addr, o_strtab + nl->n_un.n_strx ); + + /* If no part of the symbol falls within the mapped range, + ignore it. */ + if (sym_addr <= di->text_avma + || sym_addr >= di->text_avma+di->text_size) { + continue; + } + + /* skip names which point outside the string table; + following these risks segfaulting Valgrind */ + name = o_strtab + nl->n_un.n_strx; + if (name < o_strtab || name >= o_strtab + o_strtab_sz) + continue; + + /* skip nameless symbols; these appear to be common, but + useless */ + if (*name == 0) + continue; + + risym.tocptr = 0; + risym.addr = sym_addr; + risym.size = // let canonicalize fix it + di->text_avma+di->text_size - sym_addr; + risym.name = ML_(addStr)(di, name, -1); + risym.isText = True; + // Lots of user function names get prepended with an underscore. Eg. the + // function 'f' becomes the symbol '_f'. And the "below main" + // function is called "start". So we skip the leading underscore, and + // if we see 'start' and --show-below-main=no, we rename it as + // "start_according_to_valgrind", which makes it easy to spot later + // and display as "(below main)". + if (risym.name[0] == '_') { + risym.name++; + } else if (!VG_(clo_show_below_main) && VG_STREQ(risym.name, "start")) { + if (s_a_t_v == NULL) + s_a_t_v = ML_(addStr)(di, "start_according_to_valgrind", -1); + vg_assert(s_a_t_v); + risym.name = s_a_t_v; + } + + vg_assert(risym.name); + VG_(addToXA)( syms, &risym ); + } +} + + +/* Compare DiSyms by their start address, and for equal addresses, use + the name as a secondary sort key. */ +static Int cmp_DiSym_by_start_then_name ( void* v1, void* v2 ) +{ + DiSym* s1 = (DiSym*)v1; + DiSym* s2 = (DiSym*)v2; + if (s1->addr < s2->addr) return -1; + if (s1->addr > s2->addr) return 1; + return VG_(strcmp)(s1->name, s2->name); +} + +/* 'cand' is a bunch of candidate symbols obtained by reading + nlist-style symbol table entries. Their ends may overlap, so sort + them and truncate them accordingly. The code in this routine is + copied almost verbatim from read_symbol_table() in readxcoff.c. */ +static void tidy_up_cand_syms ( /*MOD*/XArray* /* of DiSym */ syms, + Bool trace_symtab ) +{ + Word nsyms, i, j, k, m; + + nsyms = VG_(sizeXA)(syms); + + VG_(setCmpFnXA)(syms, cmp_DiSym_by_start_then_name); + VG_(sortXA)(syms); + + /* We only know for sure the start addresses (actual VMAs) of + symbols, and an overestimation of their end addresses. So sort + by start address, then clip each symbol so that its end address + does not overlap with the next one along. + + There is a small refinement: if a group of symbols have the same + address, treat them as a group: find the next symbol along that + has a higher start address, and clip all of the group + accordingly. This clips the group as a whole so as not to + overlap following symbols. This leaves prefersym() in + storage.c, which is not nlist-specific, to later decide which of + the symbols in the group to keep. + + Another refinement is that we need to get rid of symbols which, + after clipping, have identical starts, ends, and names. So the + sorting uses the name as a secondary key. + */ + + for (i = 0; i < nsyms; i++) { + for (k = i+1; + k < nsyms + && ((DiSym*)VG_(indexXA)(syms,i))->addr + == ((DiSym*)VG_(indexXA)(syms,k))->addr; + k++) + ; + /* So now [i .. k-1] is a group all with the same start address. + Clip their ending addresses so they don't overlap [k]. In + the normal case (no overlaps), k == i+1. */ + if (k < nsyms) { + DiSym* next = (DiSym*)VG_(indexXA)(syms,k); + for (m = i; m < k; m++) { + DiSym* here = (DiSym*)VG_(indexXA)(syms,m); + vg_assert(here->addr < next->addr); + if (here->addr + here->size > next->addr) + here->size = next->addr - here->addr; + } + } + i = k-1; + vg_assert(i <= nsyms); + } + + j = 0; + if (nsyms > 0) { + j = 1; + for (i = 1; i < nsyms; i++) { + DiSym *s_j1, *s_j, *s_i; + vg_assert(j <= i); + s_j1 = (DiSym*)VG_(indexXA)(syms, j-1); + s_j = (DiSym*)VG_(indexXA)(syms, j); + s_i = (DiSym*)VG_(indexXA)(syms, i); + if (s_i->addr != s_j1->addr + || s_i->size != s_j1->size + || 0 != VG_(strcmp)(s_i->name, s_j1->name)) { + *s_j = *s_i; + j++; + } else { + if (trace_symtab) + VG_(printf)("nlist cleanup: dump duplicate avma %010lx %s\n", + s_i->addr, s_i->name ); + } + } + } + vg_assert(j >= 0 && j <= nsyms); + VG_(dropTailXA)(syms, nsyms - j); +} + + +/*------------------------------------------------------------*/ +/*--- ---*/ +/*--- Mach-O top-level processing ---*/ +/*--- ---*/ +/*------------------------------------------------------------*/ + +#if !defined(APPLE_DSYM_EXT_AND_SUBDIRECTORY) +#define APPLE_DSYM_EXT_AND_SUBDIRECTORY ".dSYM/Contents/Resources/DWARF/" +#endif + + +static Bool file_exists_p(const Char *path) +{ + struct vg_stat sbuf; + SysRes res = VG_(stat)(path, &sbuf); + return sr_isError(res) ? False : True; +} + + +/* Search for an existing dSYM file as a possible separate debug file. + Adapted from gdb. */ +static Char * +find_separate_debug_file (const Char *executable_name) +{ + Char *basename_str; + Char *dot_ptr; + Char *slash_ptr; + Char *dsymfile; + + /* Make sure the object file name itself doesn't contain ".dSYM" in it or we + will end up with an infinite loop where after we add a dSYM symbol file, + it will then enter this function asking if there is a debug file for the + dSYM file itself. */ + if (VG_(strcasestr) (executable_name, ".dSYM") == NULL) + { + /* Check for the existence of a .dSYM file for a given executable. */ + basename_str = VG_(basename) (executable_name); + dsymfile = ML_(dinfo_zalloc)("di.readmacho.dsymfile", + VG_(strlen) (executable_name) + + VG_(strlen) (APPLE_DSYM_EXT_AND_SUBDIRECTORY) + + VG_(strlen) (basename_str) + + 1 + ); + + /* First try for the dSYM in the same directory as the original file. */ + VG_(strcpy) (dsymfile, executable_name); + VG_(strcat) (dsymfile, APPLE_DSYM_EXT_AND_SUBDIRECTORY); + VG_(strcat) (dsymfile, basename_str); + + if (file_exists_p (dsymfile)) + return dsymfile; + + /* Now search for any parent directory that has a '.' in it so we can find + Mac OS X applications, bundles, plugins, and any other kinds of files. + Mac OS X application bundles wil have their program in + "/some/path/MyApp.app/Contents/MacOS/MyApp" (or replace ".app" with + ".bundle" or ".plugin" for other types of bundles). So we look for any + prior '.' character and try appending the apple dSYM extension and + subdirectory and see if we find an existing dSYM file (in the above + MyApp example the dSYM would be at either: + "/some/path/MyApp.app.dSYM/Contents/Resources/DWARF/MyApp" or + "/some/path/MyApp.dSYM/Contents/Resources/DWARF/MyApp". */ + VG_(strcpy) (dsymfile, VG_(dirname) (executable_name)); + while ((dot_ptr = VG_(strrchr) (dsymfile, '.'))) + { + /* Find the directory delimiter that follows the '.' character since + we now look for a .dSYM that follows any bundle extension. */ + slash_ptr = VG_(strchr) (dot_ptr, '/'); + if (slash_ptr) + { + /* NULL terminate the string at the '/' character and append + the path down to the dSYM file. */ + *slash_ptr = '\0'; + VG_(strcat) (slash_ptr, APPLE_DSYM_EXT_AND_SUBDIRECTORY); + VG_(strcat) (slash_ptr, basename_str); + if (file_exists_p (dsymfile)) + return dsymfile; + } + + /* NULL terminate the string at the '.' character and append + the path down to the dSYM file. */ + *dot_ptr = '\0'; + VG_(strcat) (dot_ptr, APPLE_DSYM_EXT_AND_SUBDIRECTORY); + VG_(strcat) (dot_ptr, basename_str); + if (file_exists_p (dsymfile)) + return dsymfile; + + /* NULL terminate the string at the '.' locatated by the strrchr() + function again. */ + *dot_ptr = '\0'; + + /* We found a previous extension '.' character and did not find a + dSYM file so now find previous directory delimiter so we don't + try multiple times on a file name that may have a version number + in it such as "/some/path/MyApp.6.0.4.app". */ + slash_ptr = VG_(strrchr) (dsymfile, '/'); + if (!slash_ptr) + break; + /* NULL terminate the string at the previous directory character + and search again. */ + *slash_ptr = '\0'; + } + } + + return NULL; +} + + +static UChar *getsectdata(UChar* base, SizeT size, + Char *segname, Char *sectname, + /*OUT*/Word *sect_size) +{ + struct MACH_HEADER *mh = (struct MACH_HEADER *)base; + struct load_command *cmd; + Int c; + + for (c = 0, cmd = (struct load_command *)(mh+1); + c < mh->ncmds; + c++, cmd = (struct load_command *)(cmd->cmdsize + (Addr)cmd)) + { + if (cmd->cmd == LC_SEGMENT_CMD) { + struct SEGMENT_COMMAND *seg = (struct SEGMENT_COMMAND *)cmd; + if (0 == VG_(strncmp(seg->segname, segname, sizeof(seg->segname)))) { + struct SECTION *sects = (struct SECTION *)(seg+1); + Int s; + for (s = 0; s < seg->nsects; s++) { + if (0 == VG_(strncmp(sects[s].sectname, sectname, + sizeof(sects[s].sectname)))) + { + if (sect_size) *sect_size = sects[s].size; + return (UChar *)(base + sects[s].offset); + } + } + } + } + } + + if (sect_size) *sect_size = 0; + return 0; +} + + +/* Brute force just simply search for uuid[0..15] in img[0..n_img-1] */ +static Bool check_uuid_matches ( Addr imgA, Word n_img, UChar* uuid ) +{ + Word i; + UChar* img = (UChar*)imgA; + UChar first = uuid[0]; + if (n_img < 16) + return False; + for (i = 0; i < n_img-16; i++) { + if (img[i] != first) + continue; + if (0 == VG_(memcmp)( &img[i], &uuid[0], 16 )) + return True; + } + return False; +} + + +/* Heuristic kludge: return True if this looks like an installed + standard library; hence we shouldn't consider automagically running + dsymutil on it. */ +static Bool is_systemish_library_name ( UChar* name ) +{ + vg_assert(name); + if (0 == VG_(strncasecmp)(name, "/usr/", 5) + || 0 == VG_(strncasecmp)(name, "/bin/", 5) + || 0 == VG_(strncasecmp)(name, "/sbin/", 6) + || 0 == VG_(strncasecmp)(name, "/System/", 8) + || 0 == VG_(strncasecmp)(name, "/Library/", 9)) { + return True; + } else { + return False; + } +} + + +Bool ML_(read_macho_debug_info)( struct _DebugInfo* di ) +{ + struct symtab_command *symcmd = NULL; + struct dysymtab_command *dysymcmd = NULL; + HChar* dsymfilename = NULL; + Bool have_uuid = False; + UChar uuid[16]; + ImageInfo ii; /* main file */ + ImageInfo iid; /* auxiliary .dSYM file */ + Bool ok; + + /* mmap the object file to look for di->soname and di->text_bias + and uuid and nlist and STABS */ + + if (VG_(clo_verbosity) > 1) + VG_(message)(Vg_DebugMsg, + "%s (%#lx)", di->filename, di->rx_map_avma ); + + /* This should be ensured by our caller. */ + vg_assert(di->have_rx_map); + vg_assert(di->have_rw_map); + + VG_(memset)(&ii, 0, sizeof(ii)); + VG_(memset)(&iid, 0, sizeof(iid)); + VG_(memset)(&uuid, 0, sizeof(uuid)); + + ok = map_image_aboard( di, &ii, di->filename ); + if (!ok) goto fail; + + vg_assert(ii.macho_img != NULL && ii.macho_img_szB > 0); + + /* Poke around in the Mach-O header, to find some important + stuff. */ + // Find LC_SYMTAB and LC_DYSYMTAB, if present. + // Read di->soname from LC_ID_DYLIB if present, + // or from LC_ID_DYLINKER if present, + // or use "NONE". + // Get di->text_bias (aka slide) based on the corresponding LC_SEGMENT + // Get uuid for later dsym search + + di->text_bias = 0; + + { struct MACH_HEADER *mh = (struct MACH_HEADER *)ii.macho_img; + struct load_command *cmd; + Int c; + + for (c = 0, cmd = (struct load_command *)(mh+1); + c < mh->ncmds; + c++, cmd = (struct load_command *)(cmd->cmdsize + + (unsigned long)cmd)) { + if (cmd->cmd == LC_SYMTAB) { + symcmd = (struct symtab_command *)cmd; + } + else if (cmd->cmd == LC_DYSYMTAB) { + dysymcmd = (struct dysymtab_command *)cmd; + } + else if (cmd->cmd == LC_ID_DYLIB && mh->filetype == MH_DYLIB) { + // GrP fixme bundle? + struct dylib_command *dcmd = (struct dylib_command *)cmd; + UChar *dylibname = dcmd->dylib.name.offset + (UChar *)dcmd; + UChar *soname = VG_(strrchr)(dylibname, '/'); + if (!soname) soname = dylibname; + else soname++; + di->soname = ML_(dinfo_strdup)("di.readmacho.dylibname", + soname); + } + else if (cmd->cmd==LC_ID_DYLINKER && mh->filetype==MH_DYLINKER) { + struct dylinker_command *dcmd = (struct dylinker_command *)cmd; + UChar *dylinkername = dcmd->name.offset + (UChar *)dcmd; + UChar *soname = VG_(strrchr)(dylinkername, '/'); + if (!soname) soname = dylinkername; + else soname++; + di->soname = ML_(dinfo_strdup)("di.readmacho.dylinkername", + soname); + } + + // A comment from Julian about why varinfo[35] fail: + // + // My impression is, from comparing the output of otool -l for these + // executables with the logic in ML_(read_macho_debug_info), + // specifically the part that begins "else if (cmd->cmd == + // LC_SEGMENT_CMD) {", that it's a complete hack which just happens + // to work ok for text symbols. In particular, it appears to assume + // that in a "struct load_command" of type LC_SEGMENT_CMD, the first + // "struct SEGMENT_COMMAND" inside it is going to contain the info we + // need. However, otool -l shows, and also the Apple docs state, + // that a struct load_command may contain an arbitrary number of + // struct SEGMENT_COMMANDs, so I'm not sure why it's OK to merely + // snarf the first. But I'm not sure about this. + // + // The "Try for __DATA" block below simply adds acquisition of data + // svma/bias values using the same assumption. It also needs + // (probably) to deal with bss sections, but I don't understand how + // this all ties together really, so it requires further study. + // + // If you can get your head around the relationship between MachO + // segments, sections and load commands, this might be relatively + // easy to fix properly. + // + // Basically we need to come up with plausible numbers for di-> + // {text,data,bss}_{avma,svma}, from which the _bias numbers are + // then trivially derived. Then I think the debuginfo reader should + // work pretty well. + else if (cmd->cmd == LC_SEGMENT_CMD) { + struct SEGMENT_COMMAND *seg = (struct SEGMENT_COMMAND *)cmd; + /* Try for __TEXT */ + if (!di->text_present + && 0 == VG_(strcmp)(seg->segname, "__TEXT") + /* DDD: is the next line a kludge? -- JRS */ + && seg->fileoff == 0 && seg->filesize != 0) { + di->text_present = True; + di->text_svma = (Addr)seg->vmaddr; + di->text_avma = di->rx_map_avma; + di->text_size = seg->vmsize; + di->text_bias = di->text_avma - di->text_svma; + /* Make the _debug_ values be the same as the + svma/bias for the primary object, since there is + no secondary (debuginfo) object, but nevertheless + downstream biasing of Dwarf3 relies on the + _debug_ values. */ + di->text_debug_svma = di->text_svma; + di->text_debug_bias = di->text_bias; + } + /* Try for __DATA */ + if (!di->data_present + && 0 == VG_(strcmp)(seg->segname, "__DATA") + /* && DDD:seg->fileoff == 0 */ && seg->filesize != 0) { + di->data_present = True; + di->data_svma = (Addr)seg->vmaddr; + di->data_avma = di->rw_map_avma; + di->data_size = seg->vmsize; + di->data_bias = di->data_avma - di->data_svma; + di->data_debug_svma = di->data_svma; + di->data_debug_bias = di->data_bias; + } + } + else if (cmd->cmd == LC_UUID) { + struct uuid_command *uuid_cmd = (struct uuid_command *)cmd; + VG_(memcpy)(uuid, uuid_cmd->uuid, sizeof(uuid)); + have_uuid = True; + } + } + } + + if (!di->soname) { + di->soname = ML_(dinfo_strdup)("di.readmacho.noname", "NONE"); + } + + /* Now we have the base object to hand. Read symbols from it. */ + + if (ii.macho_img && ii.macho_img_szB > 0 && symcmd && dysymcmd) { + + /* Read nlist symbol table */ + struct NLIST *syms; + UChar *strs; + XArray* /* DiSym */ candSyms = NULL; + Word i, nCandSyms; + + if (ii.macho_img_szB < symcmd->stroff + symcmd->strsize + || ii.macho_img_szB < symcmd->symoff + symcmd->nsyms + * sizeof(struct NLIST)) { + ML_(symerr)(di, False, "Invalid Mach-O file (5 too small)."); + goto fail; + } + if (dysymcmd->ilocalsym + dysymcmd->nlocalsym > symcmd->nsyms + || dysymcmd->iextdefsym + dysymcmd->nextdefsym > symcmd->nsyms) { + ML_(symerr)(di, False, "Invalid Mach-O file (bad symbol table)."); + goto fail; + } + + syms = (struct NLIST *)(ii.macho_img + symcmd->symoff); + strs = (UChar *)(ii.macho_img + symcmd->stroff); + + if (VG_(clo_verbosity) > 1) + VG_(message)(Vg_DebugMsg, + " reading syms from primary file (%d %d)", + dysymcmd->nextdefsym, dysymcmd->nlocalsym ); + + /* Read candidate symbols into 'candSyms', so we can truncate + overlapping ends and generally tidy up, before presenting + them to ML_(addSym). */ + candSyms = VG_(newXA)( + ML_(dinfo_zalloc), "di.readmacho.candsyms.1", + ML_(dinfo_free), sizeof(DiSym) + ); + vg_assert(candSyms); + + // extern symbols + read_symtab(candSyms, + di, + syms + dysymcmd->iextdefsym, dysymcmd->nextdefsym, + strs, symcmd->strsize); + // static and private_extern symbols + read_symtab(candSyms, + di, + syms + dysymcmd->ilocalsym, dysymcmd->nlocalsym, + strs, symcmd->strsize); + + /* tidy up the cand syms -- trim overlapping ends. May resize + candSyms. */ + tidy_up_cand_syms( candSyms, di->trace_symtab ); + + /* and finally present them to ML_(addSym) */ + nCandSyms = VG_(sizeXA)( candSyms ); + for (i = 0; i < nCandSyms; i++) { + DiSym* cand = (DiSym*) VG_(indexXA)( candSyms, i ); + if (di->trace_symtab) + VG_(printf)("nlist final: acquire avma %010lx-%010lx %s\n", + cand->addr, cand->addr + cand->size - 1, cand->name ); + ML_(addSym)( di, cand ); + } + VG_(deleteXA)( candSyms ); + } + + /* If there's no UUID in the primary, don't even bother to try and + read any DWARF, since we won't be able to verify it matches. + Our policy is not to load debug info unless we can verify that + it matches the primary. Just declare success at this point. + And don't complain to the user, since that would cause us to + complain on objects compiled without -g. (Some versions of + XCode are observed to omit a UUID entry for object linked(?) + without -g. Others don't appear to omit it.) */ + if (!have_uuid) + goto success; + + /* mmap the dSYM file to look for DWARF debug info. If successful, + use the .macho_img and .macho_img_szB in iid. */ + + dsymfilename = find_separate_debug_file( di->filename ); + + /* Try to load it. */ + if (dsymfilename) { + Bool valid; + + if (VG_(clo_verbosity) > 1) + VG_(message)(Vg_DebugMsg, " dSYM= %s", dsymfilename); + + ok = map_image_aboard( di, &iid, dsymfilename ); + if (!ok) goto fail; + + /* check it has the right uuid. */ + vg_assert(have_uuid); + valid = iid.macho_img && iid.macho_img_szB > 0 + && check_uuid_matches( (Addr)iid.macho_img, + iid.macho_img_szB, uuid ); + if (valid) + goto read_the_dwarf; + + if (VG_(clo_verbosity) > 1) + VG_(message)(Vg_DebugMsg, " dSYM does not have " + "correct UUID (out of date?)"); + } + + /* There was no dsym file, or it doesn't match. We'll have to try + regenerating it, unless auto-run-dsymutil is disabled, in which + case just complain instead. */ + + /* If this looks like a lib that we shouldn't run dsymutil on, just + give up. (possible reasons: is system lib, or in /usr etc, or + the dsym dir would not be writable by the user, or we're running + as root) */ + vg_assert(di->filename); + if (is_systemish_library_name(di->filename)) + goto success; + + if (!VG_(clo_auto_run_dsymutil)) { + if (VG_(clo_verbosity) == 1) { + VG_(message)(Vg_DebugMsg, "%s:", di->filename); + } + if (VG_(clo_verbosity) > 0) + VG_(message)(Vg_DebugMsg, "%sdSYM directory %s; consider using " + "--auto-run-dsymutil=yes", + VG_(clo_verbosity) > 1 ? " " : "", + dsymfilename ? "has wrong UUID" : "is missing"); + goto success; + } + + /* Run dsymutil */ + + { Int r; + HChar* dsymutil = "/usr/bin/dsymutil "; + HChar* cmd = ML_(dinfo_zalloc)( "di.readmacho.tmp1", + VG_(strlen)(dsymutil) + + VG_(strlen)(di->filename) + + 30 /* misc */ ); + VG_(strcpy)(cmd, dsymutil); + if (0) VG_(strcat)(cmd, "--verbose "); + VG_(strcat)(cmd, di->filename); + VG_(message)(Vg_DebugMsg, "run: %s", cmd); + r = VG_(system)( cmd ); + if (r) + VG_(message)(Vg_DebugMsg, "run: %s FAILED", dsymutil); + ML_(dinfo_free)(cmd); + dsymfilename = find_separate_debug_file(di->filename); + } + + /* Try again to load it. */ + if (dsymfilename) { + Bool valid; + + if (VG_(clo_verbosity) > 1) + VG_(message)(Vg_DebugMsg, " dsyms= %s", dsymfilename); + + ok = map_image_aboard( di, &iid, dsymfilename ); + if (!ok) goto fail; + + /* check it has the right uuid. */ + vg_assert(have_uuid); + valid = iid.macho_img && iid.macho_img_szB > 0 + && check_uuid_matches( (Addr)iid.macho_img, + iid.macho_img_szB, uuid ); + if (!valid) { + if (VG_(clo_verbosity) > 0) { + VG_(message)(Vg_DebugMsg, + "WARNING: did not find expected UUID %02X%02X%02X%02X" + "-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X" + " in dSYM dir", + (UInt)uuid[0], (UInt)uuid[1], (UInt)uuid[2], (UInt)uuid[3], + (UInt)uuid[4], (UInt)uuid[5], (UInt)uuid[6], (UInt)uuid[7], + (UInt)uuid[8], (UInt)uuid[9], (UInt)uuid[10], + (UInt)uuid[11], (UInt)uuid[12], (UInt)uuid[13], + (UInt)uuid[14], (UInt)uuid[15] ); + VG_(message)(Vg_DebugMsg, + "WARNING: for %s", di->filename); + } + unmap_image( &iid ); + /* unmap_image zeroes the fields, so the following test makes + sense. */ + goto fail; + } + } + + /* Right. Finally we have our best try at the dwarf image, so go + on to reading stuff out of it. */ + + read_the_dwarf: + if (iid.macho_img && iid.macho_img_szB > 0) { + UChar* debug_info_img = NULL; + Word debug_info_sz; + UChar* debug_abbv_img; + Word debug_abbv_sz; + UChar* debug_line_img; + Word debug_line_sz; + UChar* debug_str_img; + Word debug_str_sz; + UChar* debug_ranges_img; + Word debug_ranges_sz; + UChar* debug_loc_img; + Word debug_loc_sz; + UChar* debug_name_img; + Word debug_name_sz; + + debug_info_img = + getsectdata(iid.macho_img, iid.macho_img_szB, + "__DWARF", "__debug_info", &debug_info_sz); + debug_abbv_img = + getsectdata(iid.macho_img, iid.macho_img_szB, + "__DWARF", "__debug_abbrev", &debug_abbv_sz); + debug_line_img = + getsectdata(iid.macho_img, iid.macho_img_szB, + "__DWARF", "__debug_line", &debug_line_sz); + debug_str_img = + getsectdata(iid.macho_img, iid.macho_img_szB, + "__DWARF", "__debug_str", &debug_str_sz); + debug_ranges_img = + getsectdata(iid.macho_img, iid.macho_img_szB, + "__DWARF", "__debug_ranges", &debug_ranges_sz); + debug_loc_img = + getsectdata(iid.macho_img, iid.macho_img_szB, + "__DWARF", "__debug_loc", &debug_loc_sz); + debug_name_img = + getsectdata(iid.macho_img, iid.macho_img_szB, + "__DWARF", "__debug_pubnames", &debug_name_sz); + + if (debug_info_img) { + if (VG_(clo_verbosity) > 1) { + if (0) + VG_(message)(Vg_DebugMsg, + "Reading dwarf3 for %s (%#lx) from %s" + " (%ld %ld %ld %ld %ld %ld)", + di->filename, di->text_avma, dsymfilename, + debug_info_sz, debug_abbv_sz, debug_line_sz, + debug_str_sz, debug_ranges_sz, debug_loc_sz + ); + VG_(message)(Vg_DebugMsg, + " reading dwarf3 from dsyms file"); + } + /* The old reader: line numbers and unwind info only */ + ML_(read_debuginfo_dwarf3) ( di, + debug_info_img, debug_info_sz, + debug_abbv_img, debug_abbv_sz, + debug_line_img, debug_line_sz, + debug_str_img, debug_str_sz ); + + /* The new reader: read the DIEs in .debug_info to acquire + information on variable types and locations. But only if + the tool asks for it, or the user requests it on the + command line. */ + if (VG_(needs).var_info /* the tool requires it */ + || VG_(clo_read_var_info) /* the user asked for it */) { + ML_(new_dwarf3_reader)( + di, debug_info_img, debug_info_sz, + debug_abbv_img, debug_abbv_sz, + debug_line_img, debug_line_sz, + debug_str_img, debug_str_sz, + debug_ranges_img, debug_ranges_sz, + debug_loc_img, debug_loc_sz + ); + } + } + } + + if (dsymfilename) ML_(dinfo_free)(dsymfilename); + + success: + if (ii.img) + unmap_image(&ii); + if (iid.img) + unmap_image(&iid); + return True; + + /* NOTREACHED */ + + fail: + ML_(symerr)(di, True, "Error reading Mach-O object."); + if (ii.img) + unmap_image(&ii); + if (iid.img) + unmap_image(&iid); + return False; +} + +/*--------------------------------------------------------------------*/ +/*--- end readmacho.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_debuginfo/readstabs.c b/coregrind/m_debuginfo/readstabs.c index 5bb9ac2d2..72de81dd8 100644 --- a/coregrind/m_debuginfo/readstabs.c +++ b/coregrind/m_debuginfo/readstabs.c @@ -46,7 +46,17 @@ #include "priv_readstabs.h" /* self */ /* --- !!! --- EXTERNAL HEADERS start --- !!! --- */ -#include /* stabs defns */ +#if defined(VGO_linux) +# include /* stabs defns */ +#elif defined(VGO_darwin) +# include +# define n_other n_sect +# if VG_WORDSIZE == 8 +# define nlist nlist_64 +# endif +#else +# error "Unknown OS" +#endif /* --- !!! --- EXTERNAL HEADERS end --- !!! --- */ /*------------------------------------------------------------*/ diff --git a/coregrind/m_debuginfo/storage.c b/coregrind/m_debuginfo/storage.c index 343cf3b12..f8d0834c8 100644 --- a/coregrind/m_debuginfo/storage.c +++ b/coregrind/m_debuginfo/storage.c @@ -229,6 +229,24 @@ void ML_(addSym) ( struct _DebugInfo* di, DiSym* sym ) } +/* Resize the symbol table to save memory. +*/ +void ML_(shrinkSym)( struct _DebugInfo* di ) +{ + DiSym* new_tab; + UInt new_sz = di->symtab_used; + if (new_sz == di->symtab_size) return; + + new_tab = ML_(dinfo_zalloc)( "di.storage.shrinkSym", + new_sz * sizeof(DiSym) ); + VG_(memcpy)(new_tab, di->symtab, new_sz * sizeof(DiSym)); + + ML_(dinfo_free)(di->symtab); + di->symtab = new_tab; + di->symtab_size = new_sz; +} + + /* Add a location to the location table. */ static void addLoc ( struct _DebugInfo* di, DiLoc* loc ) @@ -259,6 +277,24 @@ static void addLoc ( struct _DebugInfo* di, DiLoc* loc ) } +/* Resize the lineinfo table to save memory. +*/ +void ML_(shrinkLineInfo)( struct _DebugInfo* di ) +{ + DiLoc* new_tab; + UInt new_sz = di->loctab_used; + if (new_sz == di->loctab_size) return; + + new_tab = ML_(dinfo_zalloc)( "di.storage.shrinkLineInfo", + new_sz * sizeof(DiLoc) ); + VG_(memcpy)(new_tab, di->loctab, new_sz * sizeof(DiLoc)); + + ML_(dinfo_free)(di->loctab); + di->loctab = new_tab; + di->loctab_size = new_sz; +} + + /* Top-level place to call to add a source-location mapping entry. */ void ML_(addLineInfo) ( struct _DebugInfo* di, @@ -1060,6 +1096,8 @@ static DiSym* prefersym ( struct _DebugInfo* di, DiSym* a, DiSym* b ) #if defined(VGO_linux) || defined(VGO_aix5) # define VERSION_CHAR '@' +#elif defined(VGO_darwin) +# define VERSION_CHAR '$' #else # error Unknown OS #endif @@ -1145,6 +1183,7 @@ static DiSym* prefersym ( struct _DebugInfo* di, DiSym* a, DiSym* b ) if (cmp > 0) { preferB = True; goto out; } + /* If we get here, they are the same name. */ /* In this case we could choose either (arbitrarily), but might as @@ -1195,7 +1234,8 @@ static void canonicaliseSymtab ( struct _DebugInfo* di ) for (i = 0; i < j; i++) { if (i < j-1 && di->symtab[i].addr == di->symtab[i+1].addr - && di->symtab[i].size == di->symtab[i+1].size) { + && di->symtab[i].size == di->symtab[i+1].size + ) { n_merged++; /* merge the two into one */ di->symtab[di->symtab_used++] diff --git a/coregrind/m_debuglog.c b/coregrind/m_debuglog.c index 431e04e09..06f6988bb 100644 --- a/coregrind/m_debuglog.c +++ b/coregrind/m_debuglog.c @@ -46,6 +46,11 @@ /* This module is also notable because it is linked into both stage1 and stage2. */ +/* IMPORTANT: on Darwin it is essential to use the _nocancel versions + of syscalls rather than the vanilla version, if a _nocancel version + is available. See docs/internals/Darwin-notes.txt for the reason + why. */ + #include "pub_core_basics.h" /* basic types */ #include "pub_core_vkiscnums.h" /* for syscall numbers */ #include "pub_core_debuglog.h" /* our own iface */ @@ -393,6 +398,88 @@ static UInt local_sys_getpid ( void ) return (UInt)block[0]; } +#elif defined(VGP_x86_darwin) + +/* Using _SYSNO_INDEX rather than _SYSNO_NUM assumes that these are + Unix-class syscalls (which they are). Unfortunately _SYSNO_NUM + involves a C-style "cond ? :" expression which doesn't impress the + Darwin assembler very much. */ +__attribute__((noinline)) +static UInt local_sys_write_stderr ( HChar* buf, Int n ) +{ + UInt __res; + __asm__ volatile ( + "movl %2, %%eax\n" /* push n */ + "pushl %%eax\n" + "movl %1, %%eax\n" /* push buf */ + "pushl %%eax\n" + "movl $2, %%eax\n" /* push stderr */ + "pushl %%eax\n" + "movl $"VG_STRINGIFY(VG_DARWIN_SYSNO_INDEX(__NR_write_nocancel)) + ", %%eax\n" + "pushl %%eax\n" /* push fake return address */ + "int $0x80\n" /* write(stderr, buf, n) */ + "jnc 1f\n" /* jump if no error */ + "movl $-1, %%eax\n" /* return -1 if error */ + "1: " + "movl %%eax, %0\n" /* __res = eax */ + "addl $16, %%esp\n" /* pop x4 */ + : "=mr" (__res) + : "g" (buf), "g" (n) + : "eax", "edx", "cc" + ); + return __res; +} + +static UInt local_sys_getpid ( void ) +{ + UInt __res; + __asm__ volatile ( + "movl $"VG_STRINGIFY(VG_DARWIN_SYSNO_INDEX(__NR_getpid))", %%eax\n" + "int $0x80\n" /* getpid() */ + "movl %%eax, %0\n" /* set __res = eax */ + : "=mr" (__res) + : + : "eax", "cc" ); + return __res; +} + +#elif defined(VGP_amd64_darwin) + +__attribute__((noinline)) +static UInt local_sys_write_stderr ( HChar* buf, Int n ) +{ + UInt __res; + __asm__ volatile ( + "movq $2, %%rdi\n" /* push stderr */ + "movq %1, %%rsi\n" /* push buf */ + "movl %2, %%edx\n" /* push n */ + "movl $"VG_STRINGIFY(VG_DARWIN_SYSNO_NUM(__NR_write_nocancel)) + ", %%eax\n" + "syscall\n" /* write(stderr, buf, n) */ + "jnc 1f\n" /* jump if no error */ + "movq $-1, %%rax\n" /* return -1 if error */ + "1: " + "movl %%eax, %0\n" /* __res = eax */ + : "=mr" (__res) + : "g" (buf), "g" (n) + : "rdi", "rsi", "rdx", "rcx", "rax", "cc" ); + return __res; +} + +static UInt local_sys_getpid ( void ) +{ + UInt __res; + __asm__ volatile ( + "movl $"VG_STRINGIFY(VG_DARWIN_SYSNO_NUM(__NR_getpid))", %%eax\n" + "syscall\n" /* getpid() */ + "movl %%eax, %0\n" /* set __res = eax */ + : "=mr" (__res) + : + : "rax", "rcx", "cc" ); + return __res; +} + #else # error Unknown platform #endif diff --git a/coregrind/m_dispatch/dispatch-amd64-darwin.S b/coregrind/m_dispatch/dispatch-amd64-darwin.S new file mode 100644 index 000000000..bc095e4be --- /dev/null +++ b/coregrind/m_dispatch/dispatch-amd64-darwin.S @@ -0,0 +1,331 @@ + +/*--------------------------------------------------------------------*/ +/*--- The core dispatch loop, for jumping to a code address. ---*/ +/*--- dispatch-amd64-darwin.S ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2007 Julian Seward + jseward@acm.org + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics_asm.h" +#include "pub_core_dispatch_asm.h" +#include "pub_core_transtab_asm.h" +#include "libvex_guest_offsets.h" /* for OFFSET_amd64_RIP */ + + +/*------------------------------------------------------------*/ +/*--- ---*/ +/*--- The dispatch loop. VG_(run_innerloop) is used to ---*/ +/*--- run all translations except no-redir ones. ---*/ +/*--- ---*/ +/*------------------------------------------------------------*/ + +/*----------------------------------------------------*/ +/*--- Preamble (set everything up) ---*/ +/*----------------------------------------------------*/ + +/* signature: +UWord VG_(run_innerloop) ( void* guest_state, UWord do_profiling ); +*/ + +.text +.globl VG_(run_innerloop) +VG_(run_innerloop): + /* %rdi holds guest_state */ + /* %rsi holds do_profiling */ + + /* ----- entry point to VG_(run_innerloop) ----- */ + pushq %rbx + pushq %rcx + pushq %rdx + pushq %rsi + pushq %rbp + pushq %r8 + pushq %r9 + pushq %r10 + pushq %r11 + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 + pushq %rdi /* guest_state */ + + movq VG_(dispatch_ctr)@GOTPCREL(%rip), %r15 + movl (%r15), %r15d + pushq %r15 + + /* 8(%rsp) holds cached copy of guest_state ptr */ + /* 0(%rsp) holds cached copy of VG_(dispatch_ctr) */ + + /* Set up the guest state pointer */ + movq %rdi, %rbp + + /* fetch %RIP into %rax */ + movq OFFSET_amd64_RIP(%rbp), %rax + + /* set host FPU control word to the default mode expected + by VEX-generated code. See comments in libvex.h for + more info. */ + finit + pushq $0x027F + fldcw (%rsp) + addq $8, %rsp + + /* set host SSE control word to the default mode expected + by VEX-generated code. */ + pushq $0x1F80 + ldmxcsr (%rsp) + addq $8, %rsp + + /* set dir flag to known value */ + cld + + /* fall into main loop (the right one) */ + cmpq $0, %rsi + je VG_(run_innerloop__dispatch_unprofiled) + jmp VG_(run_innerloop__dispatch_profiled) + /*NOTREACHED*/ + +/*----------------------------------------------------*/ +/*--- NO-PROFILING (standard) dispatcher ---*/ +/*----------------------------------------------------*/ + +.align 4 +.globl VG_(run_innerloop__dispatch_unprofiled) +VG_(run_innerloop__dispatch_unprofiled): + /* AT ENTRY: %rax is next guest addr, %rbp is possibly + modified guest state ptr */ + + /* Has the guest state pointer been messed with? If yes, exit. */ + cmpq 8(%rsp), %rbp + movq VG_(tt_fast)@GOTPCREL(%rip), %rcx + jnz gsp_changed + + /* save the jump address in the guest state */ + movq %rax, OFFSET_amd64_RIP(%rbp) + + /* Are we out of timeslice? If yes, defer to scheduler. */ + subl $1, 0(%rsp) + jz counter_is_zero + + /* try a fast lookup in the translation cache */ + movq %rax, %rbx + andq $VG_TT_FAST_MASK, %rbx /* entry# */ + shlq $4, %rbx /* entry# * sizeof(FastCacheEntry) */ + movq 0(%rcx,%rbx,1), %r10 /* .guest */ + movq 8(%rcx,%rbx,1), %r11 /* .host */ + cmpq %rax, %r10 + jnz fast_lookup_failed + + /* Found a match. Jump to .host. */ + jmp *%r11 + ud2 /* persuade insn decoders not to speculate past here */ + /* generated code should run, then jump back to + VG_(run_innerloop__dispatch_unprofiled). */ + /*NOTREACHED*/ + +/*----------------------------------------------------*/ +/*--- PROFILING dispatcher (can be much slower) ---*/ +/*----------------------------------------------------*/ + +.align 4 +.globl VG_(run_innerloop__dispatch_profiled) +VG_(run_innerloop__dispatch_profiled): + /* AT ENTRY: %rax is next guest addr, %rbp is possibly + modified guest state ptr */ + + /* Has the guest state pointer been messed with? If yes, exit. */ + cmpq 8(%rsp), %rbp + movq VG_(tt_fast)@GOTPCREL(%rip), %rcx + jnz gsp_changed + + /* save the jump address in the guest state */ + movq %rax, OFFSET_amd64_RIP(%rbp) + + /* Are we out of timeslice? If yes, defer to scheduler. */ + subl $1, 0(%rsp) + jz counter_is_zero + + /* try a fast lookup in the translation cache */ + movq %rax, %rbx + andq $VG_TT_FAST_MASK, %rbx /* entry# */ + shlq $4, %rbx /* entry# * sizeof(FastCacheEntry) */ + movq 0(%rcx,%rbx,1), %r10 /* .guest */ + movq 8(%rcx,%rbx,1), %r11 /* .host */ + cmpq %rax, %r10 + jnz fast_lookup_failed + + /* increment bb profile counter */ + movq VG_(tt_fastN)@GOTPCREL(%rip), %rdx + shrq $1, %rbx /* entry# * sizeof(UInt*) */ + movq (%rdx,%rbx,1), %rdx + addl $1, (%rdx) + + /* Found a match. Jump to .host. */ + jmp *%r11 + ud2 /* persuade insn decoders not to speculate past here */ + /* generated code should run, then jump back to + VG_(run_innerloop__dispatch_profiled). */ + /*NOTREACHED*/ + +/*----------------------------------------------------*/ +/*--- exit points ---*/ +/*----------------------------------------------------*/ + +gsp_changed: + /* Someone messed with the gsp. Have to + defer to scheduler to resolve this. dispatch ctr + is not yet decremented, so no need to increment. */ + /* %RIP is NOT up to date here. First, need to write + %rax back to %RIP, but without trashing %rbp since + that holds the value we want to return to the scheduler. + Hence use %r15 transiently for the guest state pointer. */ + movq 8(%rsp), %r15 + movq %rax, OFFSET_amd64_RIP(%r15) + movq %rbp, %rax + jmp run_innerloop_exit + /*NOTREACHED*/ + +counter_is_zero: + /* %RIP is up to date here */ + /* back out decrement of the dispatch counter */ + addl $1, 0(%rsp) + movq $VG_TRC_INNER_COUNTERZERO, %rax + jmp run_innerloop_exit + +fast_lookup_failed: + /* %RIP is up to date here */ + /* back out decrement of the dispatch counter */ + addl $1, 0(%rsp) + movq $VG_TRC_INNER_FASTMISS, %rax + jmp run_innerloop_exit + + + +/* All exits from the dispatcher go through here. %rax holds + the return value. +*/ +run_innerloop_exit: + /* We're leaving. Check that nobody messed with + %mxcsr or %fpucw. We can't mess with %rax here as it + holds the tentative return value, but any other is OK. */ +#if !defined(ENABLE_INNER) + /* This check fails for self-hosting, so skip in that case */ + pushq $0 + fstcw (%rsp) + cmpl $0x027F, (%rsp) + popq %r15 /* get rid of the word without trashing %eflags */ + jnz invariant_violation +#endif + pushq $0 + stmxcsr (%rsp) + andl $0xFFFFFFC0, (%rsp) /* mask out status flags */ + cmpl $0x1F80, (%rsp) + popq %r15 + jnz invariant_violation + /* otherwise we're OK */ + jmp run_innerloop_exit_REALLY + +invariant_violation: + movq $VG_TRC_INVARIANT_FAILED, %rax + jmp run_innerloop_exit_REALLY + +run_innerloop_exit_REALLY: + + /* restore VG_(dispatch_ctr) */ + popq %r14 + movq VG_(dispatch_ctr)@GOTPCREL(%rip), %r15 + movl %r14d, (%r15) + + popq %rdi + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %r11 + popq %r10 + popq %r9 + popq %r8 + popq %rbp + popq %rsi + popq %rdx + popq %rcx + popq %rbx + ret + + +/*------------------------------------------------------------*/ +/*--- ---*/ +/*--- A special dispatcher, for running no-redir ---*/ +/*--- translations. Just runs the given translation once. ---*/ +/*--- ---*/ +/*------------------------------------------------------------*/ + +/* signature: +void VG_(run_a_noredir_translation) ( UWord* argblock ); +*/ + +/* Run a no-redir translation. argblock points to 4 UWords, 2 to carry args + and 2 to carry results: + 0: input: ptr to translation + 1: input: ptr to guest state + 2: output: next guest PC + 3: output: guest state pointer afterwards (== thread return code) +*/ +.align 4 +.globl VG_(run_a_noredir_translation) +VG_(run_a_noredir_translation): + /* Save callee-saves regs */ + pushq %rbx + pushq %rbp + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 + + pushq %rdi /* we will need it after running the translation */ + movq 8(%rdi), %rbp + jmp *0(%rdi) + /*NOTREACHED*/ + ud2 + /* If the translation has been correctly constructed, we + should resume at the the following label. */ +.globl VG_(run_a_noredir_translation__return_point) +VG_(run_a_noredir_translation__return_point): + popq %rdi + movq %rax, 16(%rdi) + movq %rbp, 24(%rdi) + + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %rbp + popq %rbx + ret + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_dispatch/dispatch-x86-darwin.S b/coregrind/m_dispatch/dispatch-x86-darwin.S new file mode 100644 index 000000000..11956a80a --- /dev/null +++ b/coregrind/m_dispatch/dispatch-x86-darwin.S @@ -0,0 +1,318 @@ + +/*--------------------------------------------------------------------*/ +/*--- The core dispatch loop, for jumping to a code address. ---*/ +/*--- dispatch-x86.S ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2006 Julian Seward + jseward@acm.org + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics_asm.h" +#include "pub_core_dispatch_asm.h" +#include "pub_core_transtab_asm.h" +#include "libvex_guest_offsets.h" /* for OFFSET_x86_EIP */ + + +/* Global variables */ +/* These are defined here instead of in their respective C files to + avoid extra PIC branch code here. */ +.data +.align 2 + +/* m_transtab.c */ +.globl VG_(tt_fast) +.align 4 +VG_(tt_fast): .space VG_TT_FAST_SIZE*8, 0 /* (2*Addr) [VG_TT_FAST_SIZE] */ +.globl VG_(tt_fastN) +VG_(tt_fastN): .space VG_TT_FAST_SIZE*4, 0 /* (UInt *) [VG_TT_FAST_SIZE] */ + +/* scheduler.c */ +.globl VG_(dispatch_ctr) +VG_(dispatch_ctr): .long 0 + + +/*------------------------------------------------------------*/ +/*--- ---*/ +/*--- The dispatch loop. VG_(run_innerloop) is used to ---*/ +/*--- run all translations except no-redir ones. ---*/ +/*--- ---*/ +/*------------------------------------------------------------*/ + +/*----------------------------------------------------*/ +/*--- Preamble (set everything up) ---*/ +/*----------------------------------------------------*/ + +/* signature: +UWord VG_(run_innerloop) ( void* guest_state, UWord do_profiling ); +*/ +.text +.globl VG_(run_innerloop) +VG_(run_innerloop): + /* 4(%esp) holds guest_state */ + /* 8(%esp) holds do_profiling */ + + /* ----- entry point to VG_(run_innerloop) ----- */ + pushl %ebx + pushl %ecx + pushl %edx + pushl %esi + pushl %edi + pushl %ebp + + /* 28(%esp) holds guest_state */ + /* 32(%esp) holds do_profiling */ + + /* Set up the guest state pointer */ + movl 28(%esp), %ebp + + /* fetch %EIP into %eax */ + movl OFFSET_x86_EIP(%ebp), %eax + + /* set host FPU control word to the default mode expected + by VEX-generated code. See comments in libvex.h for + more info. */ + finit + pushl $0x027F + fldcw (%esp) + addl $4, %esp + + /* set host SSE control word to the default mode expected + by VEX-generated code. */ + cmpl $0, VG_(machine_x86_have_mxcsr) + jz L1 + pushl $0x1F80 + ldmxcsr (%esp) + addl $4, %esp +L1: + /* set dir flag to known value */ + cld + + /* fall into main loop (the right one) */ + cmpl $0, 32(%esp) /* do_profiling */ + je VG_(run_innerloop__dispatch_unprofiled) + jmp VG_(run_innerloop__dispatch_profiled) + /*NOTREACHED*/ + +/*----------------------------------------------------*/ +/*--- NO-PROFILING (standard) dispatcher ---*/ +/*----------------------------------------------------*/ + +.globl VG_(run_innerloop__dispatch_unprofiled) +VG_(run_innerloop__dispatch_unprofiled): + /* AT ENTRY: %eax is next guest addr, %ebp is possibly + modified guest state ptr */ + + /* Has the guest state pointer been messed with? If yes, exit. */ + cmpl 28(%esp), %ebp + jnz gsp_changed + + /* save the jump address in the guest state */ + movl %eax, OFFSET_x86_EIP(%ebp) + + /* Are we out of timeslice? If yes, defer to scheduler. */ + subl $1, VG_(dispatch_ctr) + jz counter_is_zero + + /* try a fast lookup in the translation cache */ + movl %eax, %ebx + andl $VG_TT_FAST_MASK, %ebx + movl 0+VG_(tt_fast)(,%ebx,8), %esi /* .guest */ + movl 4+VG_(tt_fast)(,%ebx,8), %edi /* .host */ + cmpl %eax, %esi + jnz fast_lookup_failed + + /* Found a match. Jump to .host. */ + jmp *%edi + ud2 /* persuade insn decoders not to speculate past here */ + /* generated code should run, then jump back to + VG_(run_innerloop__dispatch_unprofiled). */ + /*NOTREACHED*/ + +/*----------------------------------------------------*/ +/*--- PROFILING dispatcher (can be much slower) ---*/ +/*----------------------------------------------------*/ + +.globl VG_(run_innerloop__dispatch_profiled) +VG_(run_innerloop__dispatch_profiled): + /* AT ENTRY: %eax is next guest addr, %ebp is possibly + modified guest state ptr */ + + /* Has the guest state pointer been messed with? If yes, exit. */ + cmpl 28(%esp), %ebp + jnz gsp_changed + + /* save the jump address in the guest state */ + movl %eax, OFFSET_x86_EIP(%ebp) + + /* Are we out of timeslice? If yes, defer to scheduler. */ + subl $1, VG_(dispatch_ctr) + jz counter_is_zero + + /* try a fast lookup in the translation cache */ + movl %eax, %ebx + andl $VG_TT_FAST_MASK, %ebx + movl 0+VG_(tt_fast)(,%ebx,8), %esi /* .guest */ + movl 4+VG_(tt_fast)(,%ebx,8), %edi /* .host */ + cmpl %eax, %esi + jnz fast_lookup_failed + /* increment bb profile counter */ + /* note: innocuous as this sounds, it causes a huge amount more + stress on D1 and significantly slows everything down. */ + movl VG_(tt_fastN)(,%ebx,4), %edx + /* Use "addl $1", not "incl", to avoid partial-flags stall on P4 */ + addl $1, (%edx) + + /* Found a match. Jump to .host. */ + jmp *%edi + ud2 /* persuade insn decoders not to speculate past here */ + /* generated code should run, then jump back to + VG_(run_innerloop__dispatch_profiled). */ + /*NOTREACHED*/ + +/*----------------------------------------------------*/ +/*--- exit points ---*/ +/*----------------------------------------------------*/ + +gsp_changed: + /* Someone messed with the gsp. Have to + defer to scheduler to resolve this. dispatch ctr + is not yet decremented, so no need to increment. */ + /* %EIP is NOT up to date here. First, need to write + %eax back to %EIP, but without trashing %ebp since + that holds the value we want to return to the scheduler. + Hence use %esi transiently for the guest state pointer. */ + movl 28(%esp), %esi + movl %eax, OFFSET_x86_EIP(%esi) + movl %ebp, %eax + jmp run_innerloop_exit + /*NOTREACHED*/ + +counter_is_zero: + /* %EIP is up to date here */ + /* back out decrement of the dispatch counter */ + addl $1, VG_(dispatch_ctr) + movl $VG_TRC_INNER_COUNTERZERO, %eax + jmp run_innerloop_exit + /*NOTREACHED*/ + +fast_lookup_failed: + /* %EIP is up to date here */ + /* back out decrement of the dispatch counter */ + addl $1, VG_(dispatch_ctr) + movl $VG_TRC_INNER_FASTMISS, %eax + jmp run_innerloop_exit + /*NOTREACHED*/ + + + +/* All exits from the dispatcher go through here. %eax holds + the return value. +*/ +run_innerloop_exit: + /* We're leaving. Check that nobody messed with + %mxcsr or %fpucw. We can't mess with %eax here as it + holds the tentative return value, but any other is OK. */ +#if !defined(ENABLE_INNER) + /* This check fails for self-hosting, so skip in that case */ + pushl $0 + fstcw (%esp) + cmpl $0x027F, (%esp) + popl %esi /* get rid of the word without trashing %eflags */ + jnz invariant_violation +#endif + cmpl $0, VG_(machine_x86_have_mxcsr) + jz L2 + pushl $0 + stmxcsr (%esp) + andl $0xFFFFFFC0, (%esp) /* mask out status flags */ + cmpl $0x1F80, (%esp) + popl %esi + jnz invariant_violation +L2: /* otherwise we're OK */ + jmp run_innerloop_exit_REALLY + +invariant_violation: + movl $VG_TRC_INVARIANT_FAILED, %eax + jmp run_innerloop_exit_REALLY + +run_innerloop_exit_REALLY: + popl %ebp + popl %edi + popl %esi + popl %edx + popl %ecx + popl %ebx + ret + + +/*------------------------------------------------------------*/ +/*--- ---*/ +/*--- A special dispatcher, for running no-redir ---*/ +/*--- translations. Just runs the given translation once. ---*/ +/*--- ---*/ +/*------------------------------------------------------------*/ + +/* signature: +void VG_(run_a_noredir_translation) ( UWord* argblock ); +*/ + +/* Run a no-redir translation. argblock points to 4 UWords, 2 to carry args + and 2 to carry results: + 0: input: ptr to translation + 1: input: ptr to guest state + 2: output: next guest PC + 3: output: guest state pointer afterwards (== thread return code) +*/ +.globl VG_(run_a_noredir_translation) +VG_(run_a_noredir_translation): + /* Save callee-saves regs */ + pushl %esi + pushl %edi + pushl %ebp + pushl %ebx + + movl 20(%esp), %edi /* %edi = argblock */ + movl 4(%edi), %ebp /* argblock[1] */ + jmp *0(%edi) /* argblock[0] */ + /*NOTREACHED*/ + ud2 + /* If the translation has been correctly constructed, we + should resume at the the following label. */ +.globl VG_(run_a_noredir_translation__return_point) +VG_(run_a_noredir_translation__return_point): + movl 20(%esp), %edi + movl %eax, 8(%edi) /* argblock[2] */ + movl %ebp, 12(%edi) /* argblock[3] */ + + popl %ebx + popl %ebp + popl %edi + popl %esi + ret + + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_initimg/initimg-darwin.c b/coregrind/m_initimg/initimg-darwin.c new file mode 100644 index 000000000..13da3bc46 --- /dev/null +++ b/coregrind/m_initimg/initimg-darwin.c @@ -0,0 +1,621 @@ + +/*--------------------------------------------------------------------*/ +/*--- Startup: create initial process image on Darwin ---*/ +/*--- initimg-darwin.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2007 Julian Seward + jseward@acm.org + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics.h" +#include "pub_core_vki.h" +#include "pub_core_debuglog.h" +#include "pub_core_libcbase.h" +#include "pub_core_libcassert.h" +#include "pub_core_libcfile.h" +#include "pub_core_libcproc.h" +#include "pub_core_libcprint.h" +#include "pub_core_xarray.h" +#include "pub_core_clientstate.h" +#include "pub_core_aspacemgr.h" +#include "pub_core_mallocfree.h" +#include "pub_core_machine.h" +#include "pub_core_ume.h" +#include "pub_core_options.h" +#include "pub_core_tooliface.h" /* VG_TRACK */ +#include "pub_core_threadstate.h" /* ThreadArchState */ +#include "priv_initimg_pathscan.h" +#include "pub_core_initimg.h" /* self */ + + +/*====================================================================*/ +/*=== Loading the client ===*/ +/*====================================================================*/ + +/* Load the client whose name is VG_(argv_the_exename). */ + +static void load_client ( /*OUT*/ExeInfo* info, + /*OUT*/Addr* client_ip) +{ + HChar* exe_name; + Int ret; + SysRes res; + + vg_assert( VG_(args_the_exename) != NULL); + exe_name = ML_(find_executable)( VG_(args_the_exename) ); + + if (!exe_name) { + VG_(printf)("valgrind: %s: command not found\n", VG_(args_the_exename)); + VG_(exit)(127); // 127 is Posix NOTFOUND + } + + VG_(memset)(info, 0, sizeof(*info)); + ret = VG_(do_exec)(exe_name, info); + + // The client was successfully loaded! Continue. + + /* Get hold of a file descriptor which refers to the client + executable. This is needed for attaching to GDB. */ + res = VG_(open)(exe_name, VKI_O_RDONLY, VKI_S_IRUSR); + if (!sr_isError(res)) + VG_(cl_exec_fd) = sr_Res(res); + + /* Copy necessary bits of 'info' that were filled in */ + *client_ip = info->init_ip; +} + + +/*====================================================================*/ +/*=== Setting up the client's environment ===*/ +/*====================================================================*/ + +/* Prepare the client's environment. This is basically a copy of our + environment, except: + + DYLD_INSERT_LIBRARIES=$VALGRIND_LIB/vgpreload_core-PLATFORM.so: + ($VALGRIND_LIB/vgpreload_TOOL-PLATFORM.so:)? + DYLD_INSERT_LIBRARIES + + If this is missing, then it is added. + + Also, remove any binding for VALGRIND_LAUNCHER=. The client should + not be able to see this. + + Also, add DYLD_SHARED_REGION=avoid, because V doesn't know how + to process the dyld shared cache file. + + Also, change VYLD_* (mangled by launcher) back to DYLD_*. + + If this needs to handle any more variables it should be hacked + into something table driven. The copy is VG_(malloc)'d space. +*/ +static HChar** setup_client_env ( HChar** origenv, const HChar* toolname) +{ + HChar* preload_core = "vgpreload_core"; + HChar* ld_preload = "DYLD_INSERT_LIBRARIES="; + HChar* dyld_cache = "DYLD_SHARED_REGION="; + HChar* dyld_cache_value= "avoid"; + HChar* v_launcher = VALGRIND_LAUNCHER "="; + Int ld_preload_len = VG_(strlen)( ld_preload ); + Int dyld_cache_len = VG_(strlen)( dyld_cache ); + Int v_launcher_len = VG_(strlen)( v_launcher ); + Bool ld_preload_done = False; + Bool dyld_cache_done = False; + Int vglib_len = VG_(strlen)(VG_(libdir)); + + HChar** cpp; + HChar** ret; + HChar* preload_tool_path; + Int envc, i; + + /* Alloc space for the vgpreload_core.so path and vgpreload_.so + paths. We might not need the space for vgpreload_.so, but it + doesn't hurt to over-allocate briefly. The 16s are just cautious + slop. */ + Int preload_core_path_len = vglib_len + sizeof(preload_core) + + sizeof(VG_PLATFORM) + 16; + Int preload_tool_path_len = vglib_len + VG_(strlen)(toolname) + + sizeof(VG_PLATFORM) + 16; + Int preload_string_len = preload_core_path_len + preload_tool_path_len; + HChar* preload_string = VG_(malloc)("initimg-darwin.sce.1", preload_string_len); + vg_assert(preload_string); + + /* Determine if there's a vgpreload__.so file, and setup + preload_string. */ + preload_tool_path = VG_(malloc)("initimg-darwin.sce.2", preload_tool_path_len); + vg_assert(preload_tool_path); + VG_(snprintf)(preload_tool_path, preload_tool_path_len, + "%s/vgpreload_%s-%s.so", VG_(libdir), toolname, VG_PLATFORM); + if (VG_(access)(preload_tool_path, True/*r*/, False/*w*/, False/*x*/) == 0) { + VG_(snprintf)(preload_string, preload_string_len, "%s/%s-%s.so:%s", + VG_(libdir), preload_core, VG_PLATFORM, preload_tool_path); + } else { + VG_(snprintf)(preload_string, preload_string_len, "%s/%s-%s.so", + VG_(libdir), preload_core, VG_PLATFORM); + } + VG_(free)(preload_tool_path); + + VG_(debugLog)(2, "initimg", "preload_string:\n"); + VG_(debugLog)(2, "initimg", " \"%s\"\n", preload_string); + + /* Count the original size of the env */ + envc = 0; + for (cpp = origenv; cpp && *cpp; cpp++) + envc++; + + /* Allocate a new space */ + ret = VG_(malloc) ("initimg-darwin.sce.3", + sizeof(HChar *) * (envc+2+1)); /* 2 new entries + NULL */ + vg_assert(ret); + + /* copy it over */ + for (cpp = ret; *origenv; ) + *cpp++ = *origenv++; + *cpp = NULL; + + vg_assert(envc == (cpp - ret)); + + /* Walk over the new environment, mashing as we go */ + for (cpp = ret; cpp && *cpp; cpp++) { + if (VG_(memcmp)(*cpp, ld_preload, ld_preload_len) == 0) { + Int len = VG_(strlen)(*cpp) + preload_string_len; + HChar *cp = VG_(malloc)("initimg-darwin.sce.4", len); + vg_assert(cp); + + VG_(snprintf)(cp, len, "%s%s:%s", + ld_preload, preload_string, (*cpp)+ld_preload_len); + + *cpp = cp; + + ld_preload_done = True; + } + if (VG_(memcmp)(*cpp, dyld_cache, dyld_cache_len) == 0) { + Int len = dyld_cache_len + VG_(strlen)(dyld_cache_value) + 1; + HChar *cp = VG_(malloc)("initimg-darwin.sce.4.2", len); + vg_assert(cp); + + VG_(snprintf)(cp, len, "%s%s", dyld_cache, dyld_cache_value); + + *cpp = cp; + + ld_preload_done = True; + } + } + + /* Add the missing bits */ + if (!ld_preload_done) { + Int len = ld_preload_len + preload_string_len; + HChar *cp = VG_(malloc) ("initimg-darwin.sce.5", len); + vg_assert(cp); + + VG_(snprintf)(cp, len, "%s%s", ld_preload, preload_string); + + ret[envc++] = cp; + } + if (!dyld_cache_done) { + Int len = dyld_cache_len + VG_(strlen)(dyld_cache_value) + 1; + HChar *cp = VG_(malloc) ("initimg-darwin.sce.5.2", len); + vg_assert(cp); + + VG_(snprintf)(cp, len, "%s%s", dyld_cache, dyld_cache_value); + + ret[envc++] = cp; + } + + + /* ret[0 .. envc-1] is live now. */ + /* Find and remove a binding for VALGRIND_LAUNCHER. */ + for (i = 0; i < envc; i++) + if (0 == VG_(memcmp)(ret[i], v_launcher, v_launcher_len)) + break; + + if (i < envc) { + for (; i < envc-1; i++) + ret[i] = ret[i+1]; + envc--; + } + + /* Change VYLD_ to DYLD */ + for (i = 0; i < envc; i++) { + if (0 == VG_(strncmp)(ret[i], "VYLD_", 5)) { + ret[i][0] = 'D'; + } + } + + + VG_(free)(preload_string); + ret[envc] = NULL; + return ret; +} + + +/*====================================================================*/ +/*=== Setting up the client's stack ===*/ +/*====================================================================*/ + +/* Add a string onto the string table, and return its address */ +static char *copy_str(char **tab, const char *str) +{ + char *cp = *tab; + char *orig = cp; + + while(*str) + *cp++ = *str++; + *cp++ = '\0'; + + if (0) + VG_(printf)("copied %p \"%s\" len %lld\n", orig, orig, (Long)(cp-orig)); + + *tab = cp; + + return orig; +} + + +/* ---------------------------------------------------------------- + + This sets up the client's initial stack, containing the args, + environment and aux vector. + + The format of the stack on Darwin is: + + higher address +-----------------+ <- clstack_end + | | + : string table : + | | + +-----------------+ + | NULL | + +-----------------+ + | executable_path | (first arg to execve()) + +-----------------+ + | NULL | + - - + | envp | + +-----------------+ + | NULL | + - - + | argv | + +-----------------+ + | argc | + +-----------------+ + | mach_header * | (dynamic only) + lower address +-----------------+ <- sp + | undefined | + : : + + Allocate and create the initial client stack. It is allocated down + from clstack_end, which was previously determined by the address + space manager. The returned value is the SP value for the client. + + ---------------------------------------------------------------- */ + +static +Addr setup_client_stack( void* init_sp, + char** orig_envp, + const ExeInfo* info, + Addr clstack_end, + SizeT clstack_max_size ) +{ + char **cpp; + char *strtab; /* string table */ + char *stringbase; + Addr *ptr; + unsigned stringsize; /* total size of strings in bytes */ + unsigned auxsize; /* total size of auxv in bytes */ + Int argc; /* total argc */ + Int envc; /* total number of env vars */ + unsigned stacksize; /* total client stack size */ + Addr client_SP; /* client stack base (initial SP) */ + Addr clstack_start; + Int i; + Bool have_exename; + + vg_assert(VG_IS_PAGE_ALIGNED(clstack_end+1)); + vg_assert( VG_(args_for_client) ); + + /* ==================== compute sizes ==================== */ + + /* first of all, work out how big the client stack will be */ + stringsize = 0; + auxsize = 0; + have_exename = VG_(args_the_exename) != NULL; + + /* paste on the extra args if the loader needs them (ie, the #! + interpreter and its argument) */ + argc = 0; + if (info->interp_name != NULL) { + argc++; + stringsize += VG_(strlen)(info->interp_name) + 1; + } + if (info->interp_args != NULL) { + argc++; + stringsize += VG_(strlen)(info->interp_args) + 1; + } + + /* now scan the args we're given... */ + if (have_exename) + stringsize += VG_(strlen)( VG_(args_the_exename) ) + 1; + + for (i = 0; i < VG_(sizeXA)( VG_(args_for_client) ); i++) { + argc++; + stringsize += VG_(strlen)( * (HChar**) + VG_(indexXA)( VG_(args_for_client), i )) + + 1; + } + + /* ...and the environment */ + envc = 0; + for (cpp = orig_envp; cpp && *cpp; cpp++) { + envc++; + stringsize += VG_(strlen)(*cpp) + 1; + } + + /* Darwin executable_path + NULL */ + auxsize += 2 * sizeof(Word); + if (info->executable_path) { + stringsize += 1 + VG_(strlen)(info->executable_path); + } + + /* Darwin mach_header */ + if (info->dynamic) auxsize += sizeof(Word); + + /* OK, now we know how big the client stack is */ + stacksize = + sizeof(Word) + /* argc */ + (have_exename ? sizeof(char **) : 0) + /* argc[0] == exename */ + sizeof(char **)*argc + /* argv */ + sizeof(char **) + /* terminal NULL */ + sizeof(char **)*envc + /* envp */ + sizeof(char **) + /* terminal NULL */ + auxsize + /* auxv */ + VG_ROUNDUP(stringsize, sizeof(Word)); /* strings (aligned) */ + + if (0) VG_(printf)("stacksize = %d\n", stacksize); + + /* client_SP is the client's stack pointer */ + client_SP = clstack_end - stacksize; + client_SP = VG_ROUNDDN(client_SP, 32); /* make stack 32 byte aligned */ + + /* base of the string table (aligned) */ + stringbase = strtab = (char *)clstack_end + - VG_ROUNDUP(stringsize, sizeof(int)); + + /* The max stack size */ + clstack_max_size = VG_PGROUNDUP(clstack_max_size); + + /* Darwin stack is chosen by the ume loader */ + clstack_start = clstack_end - clstack_max_size; + + /* Record stack extent -- needed for stack-change code. */ + /* GrP fixme really? */ + VG_(clstk_base) = clstack_start; + VG_(clstk_end) = clstack_end; + + if (0) + VG_(printf)("stringsize=%d auxsize=%d stacksize=%d maxsize=0x%x\n" + "clstack_start %p\n" + "clstack_end %p\n", + stringsize, auxsize, stacksize, (Int)clstack_max_size, + (void*)clstack_start, (void*)clstack_end); + + /* ==================== allocate space ==================== */ + + /* Stack was allocated by the ume loader. */ + + /* ==================== create client stack ==================== */ + + ptr = (Addr*)client_SP; + + /* --- mach_header --- */ + if (info->dynamic) *ptr++ = info->text; + + /* --- client argc --- */ + *ptr++ = (Addr)(argc + (have_exename ? 1 : 0)); + + /* --- client argv --- */ + if (info->interp_name) { + *ptr++ = (Addr)copy_str(&strtab, info->interp_name); + VG_(free)(info->interp_name); + } + if (info->interp_args) { + *ptr++ = (Addr)copy_str(&strtab, info->interp_args); + VG_(free)(info->interp_args); + } + + if (have_exename) + *ptr++ = (Addr)copy_str(&strtab, VG_(args_the_exename)); + + for (i = 0; i < VG_(sizeXA)( VG_(args_for_client) ); i++) { + *ptr++ = (Addr)copy_str( + &strtab, + * (HChar**) VG_(indexXA)( VG_(args_for_client), i ) + ); + } + *ptr++ = 0; + + /* --- envp --- */ + VG_(client_envp) = (Char **)ptr; + for (cpp = orig_envp; cpp && *cpp; ptr++, cpp++) + *ptr = (Addr)copy_str(&strtab, *cpp); + *ptr++ = 0; + + /* --- executable_path + NULL --- */ + if (info->executable_path) + *ptr++ = (Addr)copy_str(&strtab, info->executable_path); + else + *ptr++ = 0; + *ptr++ = 0; + + vg_assert((strtab-stringbase) == stringsize); + + /* client_SP is pointing at client's argc/argv */ + + if (0) VG_(printf)("startup SP = %#lx\n", client_SP); + return client_SP; +} + + +/*====================================================================*/ +/*=== Record system memory regions ===*/ +/*====================================================================*/ + +static void record_system_memory(void) +{ + /* Tell aspacem where the client's kernel commpage is */ +#if defined(VGA_amd64) + /* commpage 0x7fff:ffe00000+ - not in vm_region */ + // GrP fixme check again + VG_(am_notify_client_mmap)(0x7fffffe00000, 0x7ffffffff000-0x7fffffe00000, + VKI_PROT_READ|VKI_PROT_EXEC, 0, -1, 0); + +#elif defined(VGA_x86) + /* commpage 0xfffec000+ - not in vm_region */ + // GrP fixme check again + VG_(am_notify_client_mmap)(0xfffec000, 0xfffff000-0xfffec000, + VKI_PROT_READ|VKI_PROT_EXEC, 0, -1, 0); + +#else +# error unknown architecture +#endif +} + + +/*====================================================================*/ +/*=== TOP-LEVEL: VG_(ii_create_image) ===*/ +/*====================================================================*/ + +/* Create the client's initial memory image. */ +IIFinaliseImageInfo VG_(ii_create_image)( IICreateImageInfo iicii ) +{ + ExeInfo info; + HChar** env = NULL; + + IIFinaliseImageInfo iifii; + VG_(memset)( &iifii, 0, sizeof(iifii) ); + + //-------------------------------------------------------------- + // Load client executable, finding in $PATH if necessary + // p: get_helprequest_and_toolname() [for 'exec', 'need_help'] + // p: layout_remaining_space [so there's space] + //-------------------------------------------------------------- + VG_(debugLog)(1, "initimg", "Loading client\n"); + + if (VG_(args_the_exename) == NULL) + VG_(err_missing_prog)(); + + load_client(&info, &iifii.initial_client_IP); + + //-------------------------------------------------------------- + // Set up client's environment + // p: set-libdir [for VG_(libdir)] + // p: get_helprequest_and_toolname [for toolname] + //-------------------------------------------------------------- + VG_(debugLog)(1, "initimg", "Setup client env\n"); + env = setup_client_env(iicii.envp, iicii.toolname); + + //-------------------------------------------------------------- + // Setup client stack, eip, and VG_(client_arg[cv]) + // p: load_client() [for 'info'] + // p: fix_environment() [for 'env'] + //-------------------------------------------------------------- + iicii.clstack_top = info.stack_end - 1; + iifii.clstack_max_size = info.stack_end - info.stack_start; + + iifii.initial_client_SP = + setup_client_stack( iicii.argv - 1, env, &info, + iicii.clstack_top, iifii.clstack_max_size ); + + VG_(free)(env); + + VG_(debugLog)(2, "initimg", + "Client info: " + "initial_IP=%p initial_SP=%p stack=%p..%p\n", + (void*)(iifii.initial_client_IP), + (void*)(iifii.initial_client_SP), + (void*)(info.stack_start), + (void*)(info.stack_end)); + + + // Tell aspacem about commpage, etc + record_system_memory(); + + return iifii; +} + + +/*====================================================================*/ +/*=== TOP-LEVEL: VG_(ii_finalise_image) ===*/ +/*====================================================================*/ + +/* Just before starting the client, we may need to make final + adjustments to its initial image. Also we need to set up the VEX + guest state for thread 1 (the root thread) and copy in essential + starting values. This is handed the IIFinaliseImageInfo created by + VG_(ii_create_image). +*/ +void VG_(ii_finalise_image)( IIFinaliseImageInfo iifii ) +{ + ThreadArchState* arch = &VG_(threads)[1].arch; + + /* GrP fixme doesn't handle all registers from LC_THREAD or LC_UNIXTHREAD */ + +# if defined(VGP_x86_darwin) + vg_assert(0 == sizeof(VexGuestX86State) % 16); + + /* Zero out the initial state, and set up the simulated FPU in a + sane way. */ + LibVEX_GuestX86_initialise(&arch->vex); + + /* Zero out the shadow areas. */ + VG_(memset)(&arch->vex_shadow1, 0, sizeof(VexGuestX86State)); + VG_(memset)(&arch->vex_shadow2, 0, sizeof(VexGuestX86State)); + + /* Put essential stuff into the new state. */ + arch->vex.guest_ESP = iifii.initial_client_SP; + arch->vex.guest_EIP = iifii.initial_client_IP; + +# elif defined(VGP_amd64_darwin) + vg_assert(0 == sizeof(VexGuestAMD64State) % 16); + + /* Zero out the initial state, and set up the simulated FPU in a + sane way. */ + LibVEX_GuestAMD64_initialise(&arch->vex); + + /* Zero out the shadow areas. */ + VG_(memset)(&arch->vex_shadow1, 0, sizeof(VexGuestAMD64State)); + VG_(memset)(&arch->vex_shadow2, 0, sizeof(VexGuestAMD64State)); + + /* Put essential stuff into the new state. */ + arch->vex.guest_RSP = iifii.initial_client_SP; + arch->vex.guest_RIP = iifii.initial_client_IP; + +# else +# error Unknown platform +# endif + + /* Tell the tool that we just wrote to the registers. */ + VG_TRACK( post_reg_write, Vg_CoreStartup, /*tid*/1, /*offset*/0, + sizeof(VexGuestArchState)); +} diff --git a/coregrind/m_libcassert.c b/coregrind/m_libcassert.c index 8dbded319..b6bdbbee3 100644 --- a/coregrind/m_libcassert.c +++ b/coregrind/m_libcassert.c @@ -45,7 +45,7 @@ Assertery. ------------------------------------------------------------------ */ -#if defined(VGP_x86_linux) +#if defined(VGP_x86_linux) || defined(VGP_x86_darwin) # define GET_REAL_PC_SP_AND_FP(pc, sp, fp) \ asm("call 0f;" \ "0: popl %0;" \ @@ -54,7 +54,7 @@ : "=r" (pc),\ "=r" (sp),\ "=r" (fp)); -#elif defined(VGP_amd64_linux) +#elif defined(VGP_amd64_linux) || defined(VGP_amd64_darwin) # define GET_REAL_PC_SP_AND_FP(pc, sp, fp) \ asm("leaq 0(%%rip), %0;" \ "movq %%rsp, %1;" \ @@ -101,7 +101,7 @@ void VG_(exit)( Int status ) { #if defined(VGO_linux) (void)VG_(do_syscall1)(__NR_exit_group, status ); -#elif defined(VGO_aix5) +#elif defined(VGO_aix5) || defined(VGO_darwin) (void)VG_(do_syscall1)(__NR_exit, status ); #else # error Unknown OS diff --git a/coregrind/m_libcbase.c b/coregrind/m_libcbase.c index 42aba3e68..3614b9202 100644 --- a/coregrind/m_libcbase.c +++ b/coregrind/m_libcbase.c @@ -155,6 +155,15 @@ double VG_(strtod) ( Char* str, Char** endptr ) return n; } +Char VG_(tolower) ( Char c ) +{ + if ( c >= 'A' && c <= 'Z' ) { + return c - 'A' + 'a'; + } else { + return c; + } +} + /* --------------------------------------------------------------------- String functions ------------------------------------------------------------------ */ @@ -247,6 +256,22 @@ Int VG_(strcmp) ( const Char* s1, const Char* s2 ) } } +Int VG_(strcasecmp) ( const Char* s1, const Char* s2 ) +{ + while (True) { + UChar c1 = (UChar)VG_(tolower)(*s1); + UChar c2 = (UChar)VG_(tolower)(*s2); + if (c1 == 0 && c2 == 0) return 0; + if (c1 == 0) return -1; + if (c2 == 0) return 1; + + if (c1 < c2) return -1; + if (c1 > c2) return 1; + + s1++; s2++; + } +} + Int VG_(strncmp) ( const Char* s1, const Char* s2, SizeT nmax ) { SizeT n = 0; @@ -263,6 +288,26 @@ Int VG_(strncmp) ( const Char* s1, const Char* s2, SizeT nmax ) } } +Int VG_(strncasecmp) ( const Char* s1, const Char* s2, SizeT nmax ) +{ + Int n = 0; + while (True) { + UChar c1; + UChar c2; + if (n >= nmax) return 0; + c1 = (UChar)VG_(tolower)(*s1); + c2 = (UChar)VG_(tolower)(*s2); + if (c1 == 0 && c2 == 0) return 0; + if (c1 == 0) return -1; + if (c2 == 0) return 1; + + if (c1 < c2) return -1; + if (c1 > c2) return 1; + + s1++; s2++; n++; + } +} + Char* VG_(strstr) ( const Char* haystack, Char* needle ) { SizeT n; @@ -278,6 +323,21 @@ Char* VG_(strstr) ( const Char* haystack, Char* needle ) } } +Char* VG_(strcasestr) ( const Char* haystack, Char* needle ) +{ + Int n; + if (haystack == NULL) + return NULL; + n = VG_(strlen)(needle); + while (True) { + if (haystack[0] == 0) + return NULL; + if (VG_(strncasecmp)(haystack, needle, n) == 0) + return (Char*)haystack; + haystack++; + } +} + Char* VG_(strchr) ( const Char* s, Char c ) { while (True) { diff --git a/coregrind/m_libcfile.c b/coregrind/m_libcfile.c index 9cddfc005..72a9ea624 100644 --- a/coregrind/m_libcfile.c +++ b/coregrind/m_libcfile.c @@ -41,6 +41,11 @@ #include "pub_core_clientstate.h" // VG_(fd_hard_limit) #include "pub_core_syscall.h" +/* IMPORTANT: on Darwin it is essential to use the _nocancel versions + of syscalls rather than the vanilla version, if a _nocancel version + is available. See docs/internals/Darwin-notes.txt for the reason + why. */ + /* --------------------------------------------------------------------- File stuff ------------------------------------------------------------------ */ @@ -87,6 +92,17 @@ Bool VG_(resolve_filename) ( Int fd, HChar* buf, Int n_buf ) I_die_here; /* maybe just return False? */ return False; +# elif defined(VGO_darwin) + HChar tmp[VKI_MAXPATHLEN+1]; + if (0 == VG_(fcntl)(fd, VKI_F_GETPATH, (UWord)tmp)) { + if (n_buf > 0) { + VG_(strncpy)( buf, tmp, n_buf < sizeof(tmp) ? n_buf : sizeof(tmp) ); + buf[n_buf-1] = 0; + } + if (tmp[0] == '/') return True; + } + return False; + # else # error Unknown OS # endif @@ -94,19 +110,40 @@ Bool VG_(resolve_filename) ( Int fd, HChar* buf, Int n_buf ) SysRes VG_(open) ( const Char* pathname, Int flags, Int mode ) { - SysRes res = VG_(do_syscall3)(__NR_open, (UWord)pathname, flags, mode); +# if defined(VGO_linux) || defined(VGO_aix5) + SysRes res = VG_(do_syscall3)(__NR_open, + (UWord)pathname, flags, mode); +# elif defined(VGO_darwin) + SysRes res = VG_(do_syscall3)(__NR_open_nocancel, + (UWord)pathname, flags, mode); +# else +# error Unknown OS +# endif return res; } void VG_(close) ( Int fd ) { + /* Hmm. Return value is not checked. That's uncool. */ +# if defined(VGO_linux) || defined(VGO_aix5) (void)VG_(do_syscall1)(__NR_close, fd); +# elif defined(VGO_darwin) + (void)VG_(do_syscall1)(__NR_close_nocancel, fd); +# else +# error Unknown OS +# endif } Int VG_(read) ( Int fd, void* buf, Int count) { Int ret; +# if defined(VGO_linux) || defined(VGO_aix5) SysRes res = VG_(do_syscall3)(__NR_read, fd, (UWord)buf, count); +# elif defined(VGO_darwin) + SysRes res = VG_(do_syscall3)(__NR_read_nocancel, fd, (UWord)buf, count); +# else +# error Unknown OS +# endif if (sr_isError(res)) { ret = - (Int)(Word)sr_Err(res); vg_assert(ret < 0); @@ -120,7 +157,13 @@ Int VG_(read) ( Int fd, void* buf, Int count) Int VG_(write) ( Int fd, const void* buf, Int count) { Int ret; +# if defined(VGO_linux) || defined(VGO_aix5) SysRes res = VG_(do_syscall3)(__NR_write, fd, (UWord)buf, count); +# elif defined(VGO_darwin) + SysRes res = VG_(do_syscall3)(__NR_write_nocancel, fd, (UWord)buf, count); +# else +# error "Unknown OS" +# endif if (sr_isError(res)) { ret = - (Int)(Word)sr_Err(res); vg_assert(ret < 0); @@ -137,6 +180,14 @@ Int VG_(pipe) ( Int fd[2] ) # if defined(VGO_linux) || defined(VGO_aix5) SysRes res = VG_(do_syscall1)(__NR_pipe, (UWord)fd); return sr_isError(res) ? -1 : 0; +# elif defined(VGO_darwin) + /* __NR_pipe is UX64, so produces a double-word result */ + SysRes res = VG_(do_syscall0)(__NR_pipe); + if (!sr_isError(res)) { + fd[0] = (Int)sr_Res(res); + fd[1] = (Int)sr_ResHI(res); + } + return sr_isError(res) ? -1 : 0; # else # error "Unknown OS" # endif @@ -144,9 +195,12 @@ Int VG_(pipe) ( Int fd[2] ) OffT VG_(lseek) ( Int fd, OffT offset, Int whence ) { -#if defined(VGO_linux) || defined(VGO_aix5) +# if defined(VGO_linux) || defined(VGO_aix5) || defined(VGP_amd64_darwin) SysRes res = VG_(do_syscall3)(__NR_lseek, fd, offset, whence); vg_assert(sizeof(OffT) == sizeof(Word)); +# elif defined(VGP_x86_darwin) + SysRes res = VG_(do_syscall4)(__NR_lseek, fd, + offset & 0xffffffff, offset >> 32, whence); # else # error "Unknown plat" # endif @@ -185,7 +239,7 @@ SysRes VG_(stat) ( const Char* file_name, struct vg_stat* vgbuf ) SysRes res; VG_(memset)(vgbuf, 0, sizeof(*vgbuf)); -# if defined(VGO_linux) +# if defined(VGO_linux) || defined(VGO_darwin) /* First try with stat64. If that doesn't work out, fall back to the vanilla version. */ # if defined(__NR_stat64) @@ -235,7 +289,7 @@ Int VG_(fstat) ( Int fd, struct vg_stat* vgbuf ) SysRes res; VG_(memset)(vgbuf, 0, sizeof(*vgbuf)); -# if defined(VGO_linux) +# if defined(VGO_linux) || defined(VGO_darwin) /* First try with fstat64. If that doesn't work out, fall back to the vanilla version. */ # if defined(__NR_fstat64) @@ -289,7 +343,7 @@ SysRes VG_(dup) ( Int oldfd ) SysRes VG_(dup2) ( Int oldfd, Int newfd ) { -# if defined(VGO_linux) +# if defined(VGO_linux) || defined(VGO_darwin) return VG_(do_syscall2)(__NR_dup2, oldfd, newfd); # elif defined(VGO_aix5) I_die_here; @@ -301,7 +355,13 @@ SysRes VG_(dup2) ( Int oldfd, Int newfd ) /* Returns -1 on error. */ Int VG_(fcntl) ( Int fd, Int cmd, Addr arg ) { +# if defined(VGO_linux) || defined(VGO_aix5) SysRes res = VG_(do_syscall3)(__NR_fcntl, fd, cmd, arg); +# elif defined(VGO_darwin) + SysRes res = VG_(do_syscall3)(__NR_fcntl_nocancel, fd, cmd, arg); +# else +# error "Unknown OS" +# endif return sr_isError(res) ? -1 : sr_Res(res); } @@ -349,8 +409,8 @@ Bool VG_(record_startup_wd) ( void ) return True; } } -# elif defined(VGO_aix5) - /* We can't ask the kernel, so instead rely on launcher-aix5.c to +# elif defined(VGO_aix5) || defined(VGO_darwin) + /* We can't ask the kernel, so instead rely on launcher-*.c to tell us the startup path. Note the env var is keyed to the parent's PID, not ours, since our parent is the launcher process. */ @@ -399,6 +459,8 @@ Int VG_(getdents) (Int fd, struct vki_dirent *dirp, UInt count) /* res = getdents( fd, dirp, count ); */ res = VG_(do_syscall3)(__NR_getdents, fd, (UWord)dirp, count); return sr_isError(res) ? -1 : sr_Res(res); +# elif defined(VGO_darwin) + I_die_here; # else # error "Unknown OS" # endif @@ -503,9 +565,10 @@ Int VG_(check_executable)(/*OUT*/Bool* is_setuid, return 0; } -/* Note this moves (or at least, is believed to move) the file pointer +/* DDD: Note this moves (or at least, is believed to move) the file pointer on Linux and AIX5 but doesn't on Darwin. This inconsistency should - be fixed. */ + be fixed. (In other words, why isn't the Linux/AIX5 version implemented in + terms of pread()?) */ SysRes VG_(pread) ( Int fd, void* buf, Int count, OffT offset ) { SysRes res; @@ -516,6 +579,14 @@ SysRes VG_(pread) ( Int fd, void* buf, Int count, OffT offset ) return VG_(mk_SysRes_Error)( VKI_EINVAL ); res = VG_(do_syscall3)(__NR_read, fd, (UWord)buf, count ); return res; +# elif defined(VGP_amd64_darwin) + res = VG_(do_syscall4)(__NR_pread_nocancel, fd, (UWord)buf, count, offset); + return res; +# elif defined(VGP_x86_darwin) + /* ppc32-darwin is the same, but with the args inverted */ + res = VG_(do_syscall5)(__NR_pread_nocancel, fd, (UWord)buf, count, + offset & 0xffffffff, offset >> 32); + return res; # else # error "Unknown platform" # endif @@ -632,7 +703,7 @@ UShort VG_(ntohs) ( UShort x ) */ Int VG_(connect_via_socket)( UChar* str ) { -# if defined(VGO_linux) +# if defined(VGO_linux) || defined(VGO_darwin) Int sd, res; struct vki_sockaddr_in servAddr; UInt ip = 0; @@ -741,6 +812,20 @@ Int VG_(socket) ( Int domain, Int type, Int protocol ) # elif defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) I_die_here; +# elif defined(VGO_darwin) + SysRes res; + res = VG_(do_syscall3)(__NR_socket, domain, type, protocol); + if (!sr_isError(res)) { + // Set SO_NOSIGPIPE so write() returns EPIPE instead of raising SIGPIPE + Int optval = 1; + SysRes res2; + res2 = VG_(do_syscall5)(__NR_setsockopt, sr_Res(res), VKI_SOL_SOCKET, + VKI_SO_NOSIGPIPE, (UWord)&optval, + sizeof(optval)); + // ignore setsockopt() error + } + return sr_isError(res) ? -1 : sr_Res(res); + # else # error "Unknown arch" # endif @@ -768,6 +853,12 @@ Int my_connect ( Int sockfd, struct vki_sockaddr_in* serv_addr, Int addrlen ) # elif defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) I_die_here; +# elif defined(VGO_darwin) + SysRes res; + res = VG_(do_syscall3)(__NR_connect_nocancel, + sockfd, (UWord)serv_addr, addrlen); + return sr_isError(res) ? -1 : sr_Res(res); + # else # error "Unknown arch" # endif @@ -776,9 +867,13 @@ Int my_connect ( Int sockfd, struct vki_sockaddr_in* serv_addr, Int addrlen ) Int VG_(write_socket)( Int sd, void *msg, Int count ) { /* This is actually send(). */ - /* For Linux, VKI_MSG_NOSIGNAL is a request not to send SIGPIPE on + + /* For Linux, VKI_MSG_NOSIGNAL is a request not to send SIGPIPE on errors on stream oriented sockets when the other end breaks the - connection. The EPIPE error is still returned. */ + connection. The EPIPE error is still returned. + + For Darwin, VG_(socket)() sets SO_NOSIGPIPE to get EPIPE instead of + SIGPIPE */ # if defined(VGP_x86_linux) || defined(VGP_ppc32_linux) \ || defined(VGP_ppc64_linux) @@ -800,6 +895,11 @@ Int VG_(write_socket)( Int sd, void *msg, Int count ) # elif defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) I_die_here; +# elif defined(VGP_x86_darwin) || defined(VGP_amd64_darwin) + SysRes res; + res = VG_(do_syscall3)(__NR_write_nocancel, sd, (UWord)msg, count); + return sr_isError(res) ? -1 : sr_Res(res); + # else # error "Unknown platform" # endif @@ -826,6 +926,12 @@ Int VG_(getsockname) ( Int sd, struct vki_sockaddr *name, Int *namelen) # elif defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) I_die_here; +# elif defined(VGO_darwin) + SysRes res; + res = VG_(do_syscall3)( __NR_getsockname, + (UWord)sd, (UWord)name, (UWord)namelen ); + return sr_isError(res) ? -1 : sr_Res(res); + # else # error "Unknown platform" # endif @@ -852,6 +958,12 @@ Int VG_(getpeername) ( Int sd, struct vki_sockaddr *name, Int *namelen) # elif defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) I_die_here; +# elif defined(VGO_darwin) + SysRes res; + res = VG_(do_syscall3)( __NR_getpeername, + (UWord)sd, (UWord)name, (UWord)namelen ); + return sr_isError(res) ? -1 : sr_Res(res); + # else # error "Unknown platform" # endif @@ -882,12 +994,96 @@ Int VG_(getsockopt) ( Int sd, Int level, Int optname, void *optval, # elif defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) I_die_here; +# elif defined(VGO_darwin) + SysRes res; + res = VG_(do_syscall5)( __NR_getsockopt, + (UWord)sd, (UWord)level, (UWord)optname, + (UWord)optval, (UWord)optlen ); + return sr_isError(res) ? -1 : sr_Res(res); + # else # error "Unknown platform" # endif } +Char *VG_(basename)(const Char *path) +{ + static Char buf[VKI_PATH_MAX]; + + const Char *p, *end; + + if (path == NULL || + 0 == VG_(strcmp)(path, "")) + { + return "."; + } + + p = path + VG_(strlen)(path); + while (p > path && *p == '/') { + // skip all trailing '/' + p--; + } + + if (p == path && *p == '/') return "/"; // all slashes + + end = p; + + while (p > path && *p != '/') { + // now skip non '/' + p--; + } + + if (*p == '/') p++; + + VG_(strncpy)(buf, p, end-p+1); + buf[end-p+1] = '\0'; + + return buf; +} + + +Char *VG_(dirname)(const Char *path) +{ + static Char buf[VKI_PATH_MAX]; + + const Char *p; + + if (path == NULL || + 0 == VG_(strcmp)(path, "") || + 0 == VG_(strcmp)(path, "/")) + { + return "."; + } + + p = path + VG_(strlen)(path); + while (p > path && *p == '/') { + // skip all trailing '/' + p--; + } + + while (p > path && *p != '/') { + // now skip non '/' + p--; + } + + if (p == path) { + if (*p == '/') return "/"; // all slashes + else return "."; // no slashes + } + + while (p > path && *p == '/') { + // skip '/' again + p--; + } + + VG_(strncpy)(buf, path, p-path+1); + buf[p-path+1] = '\0'; + + return buf; +} + + /*--------------------------------------------------------------------*/ /*--- end ---*/ /*--------------------------------------------------------------------*/ diff --git a/coregrind/m_libcproc.c b/coregrind/m_libcproc.c index 86ebd23a7..1c02ba4ef 100644 --- a/coregrind/m_libcproc.c +++ b/coregrind/m_libcproc.c @@ -42,6 +42,17 @@ #include "pub_core_xarray.h" #include "pub_core_clientstate.h" +#if defined(VGO_darwin) +/* --- !!! --- EXTERNAL HEADERS start --- !!! --- */ +#include /* mach_thread_self */ +/* --- !!! --- EXTERNAL HEADERS end --- !!! --- */ +#endif + +/* IMPORTANT: on Darwin it is essential to use the _nocancel versions + of syscalls rather than the vanilla version, if a _nocancel version + is available. See docs/internals/Darwin-notes.txt for the reason + why. */ + /* --------------------------------------------------------------------- Command line and environment stuff ------------------------------------------------------------------ */ @@ -194,17 +205,32 @@ static void mash_colon_env(Char *varp, const Char *remove_pattern) // when starting child processes, so they don't see that added stuff. void VG_(env_remove_valgrind_env_stuff)(Char** envp) { + +#if defined(VGO_darwin) + + // Environment cleanup is also handled during parent launch + // in vg_preloaded.c:vg_cleanup_env(). + +#endif + Int i; Char* ld_preload_str = NULL; Char* ld_library_path_str = NULL; + Char* dyld_insert_libraries_str = NULL; Char* buf; // Find LD_* variables + // DDD: should probably conditionally compiled some of this: + // - LD_LIBRARY_PATH is universal? + // - LD_PRELOAD is on Linux, not on Darwin, not sure about AIX + // - DYLD_INSERT_LIBRARIES and DYLD_SHARED_REGION are Darwin-only for (i = 0; envp[i] != NULL; i++) { if (VG_(strncmp)(envp[i], "LD_PRELOAD=", 11) == 0) ld_preload_str = &envp[i][11]; if (VG_(strncmp)(envp[i], "LD_LIBRARY_PATH=", 16) == 0) ld_library_path_str = &envp[i][16]; + if (VG_(strncmp)(envp[i], "DYLD_INSERT_LIBRARIES=", 22) == 0) + dyld_insert_libraries_str = &envp[i][22]; } buf = VG_(arena_malloc)(VG_AR_CORE, "libcproc.erves.1", @@ -213,12 +239,16 @@ void VG_(env_remove_valgrind_env_stuff)(Char** envp) // Remove Valgrind-specific entries from LD_*. VG_(sprintf)(buf, "%s*/vgpreload_*.so", VG_(libdir)); mash_colon_env(ld_preload_str, buf); + mash_colon_env(dyld_insert_libraries_str, buf); VG_(sprintf)(buf, "%s*", VG_(libdir)); mash_colon_env(ld_library_path_str, buf); // Remove VALGRIND_LAUNCHER variable. VG_(env_unsetenv)(envp, VALGRIND_LAUNCHER); + // Remove DYLD_SHARED_REGION variable. + VG_(env_unsetenv)(envp, "DYLD_SHARED_REGION"); + // XXX if variable becomes empty, remove it completely? VG_(arena_free)(VG_AR_CORE, buf); @@ -231,7 +261,12 @@ void VG_(env_remove_valgrind_env_stuff)(Char** envp) Int VG_(waitpid)(Int pid, Int *status, Int options) { # if defined(VGO_linux) - SysRes res = VG_(do_syscall4)(__NR_wait4, pid, (UWord)status, options, 0); + SysRes res = VG_(do_syscall4)(__NR_wait4, + pid, (UWord)status, options, 0); + return sr_isError(res) ? -1 : sr_Res(res); +# elif defined(VGO_darwin) + SysRes res = VG_(do_syscall4)(__NR_wait4_nocancel, + pid, (UWord)status, options, 0); return sr_isError(res) ? -1 : sr_Res(res); # elif defined(VGO_aix5) /* magic number 4 obtained by truss-ing a C program doing @@ -415,6 +450,11 @@ Int VG_(gettid)(void) r = sr_Res(res); return r; +# elif defined(VGO_darwin) + // Darwin's gettid syscall is something else. + // Use Mach thread ports for lwpid instead. + return mach_thread_self(); + # else # error "Unknown OS" # endif @@ -489,7 +529,8 @@ Int VG_(getgroups)( Int size, UInt* list ) return sr_Res(sres); # elif defined(VGP_amd64_linux) || defined(VGP_ppc64_linux) \ - || defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) + || defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) \ + || defined(VGO_darwin) SysRes sres; sres = VG_(do_syscall2)(__NR_getgroups, size, (Addr)list); if (sr_isError(sres)) @@ -527,6 +568,17 @@ Int VG_(fork) ( void ) return -1; return sr_Res(res); +# elif defined(VGO_darwin) + SysRes res; + res = VG_(do_syscall0)(__NR_fork); /* __NR_fork is UX64 */ + if (sr_isError(res)) + return -1; + /* on success: wLO = child pid; wHI = 1 for child, 0 for parent */ + if (sr_ResHI(res) != 0) { + return 0; /* this is child: return 0 instead of child pid */ + } + return sr_Res(res); + # else # error "Unknown OS" # endif @@ -576,6 +628,14 @@ UInt VG_(read_millisecond_timer) ( void ) now = ((ULong)sec1) * 1000000ULL; now += (ULong)(nsec / 1000); +# elif defined(VGO_darwin) + { SysRes res; + struct vki_timeval tv_now; + res = VG_(do_syscall2)(__NR_gettimeofday, (UWord)&tv_now, (UWord)NULL); + vg_assert(! sr_isError(res)); + now = tv_now.tv_sec * 1000000ULL + tv_now.tv_usec; + } + # else # error "Unknown OS" # endif @@ -652,6 +712,7 @@ void VG_(do_atfork_child)(ThreadId tid) (*atforks[i].child)(tid); } + /*--------------------------------------------------------------------*/ /*--- end ---*/ /*--------------------------------------------------------------------*/ diff --git a/coregrind/m_libcsignal.c b/coregrind/m_libcsignal.c index cd79b19a6..50aa2a8ef 100644 --- a/coregrind/m_libcsignal.c +++ b/coregrind/m_libcsignal.c @@ -37,6 +37,11 @@ #include "pub_core_syscall.h" #include "pub_core_libcsignal.h" /* self */ +/* IMPORTANT: on Darwin it is essential to use the _nocancel versions + of syscalls rather than the vanilla version, if a _nocancel version + is available. See docs/internals/Darwin-notes.txt for the reason + why. */ + /* sigemptyset, sigfullset, sigaddset and sigdelset return 0 on success and -1 on error. */ /* I believe the indexing scheme in ->sig[] is also correct for @@ -181,6 +186,12 @@ Int VG_(sigprocmask)( Int how, const vki_sigset_t* set, vki_sigset_t* oldset) how, (UWord)set, (UWord)oldset); # endif +# elif defined(VGO_darwin) + /* On Darwin, __NR_sigprocmask appears to affect the entire + process, not just this thread. Hence need to use + __NR___pthread_sigmask instead. */ + SysRes res = VG_(do_syscall3)(__NR___pthread_sigmask, + how, (UWord)set, (UWord)oldset); # else # error "Unknown OS" # endif @@ -188,6 +199,24 @@ Int VG_(sigprocmask)( Int how, const vki_sigset_t* set, vki_sigset_t* oldset) } +#if defined(VGO_darwin) +/* A helper function for sigaction on Darwin. */ +static +void darwin_signal_demux(void* a1, UWord a2, UWord a3, void* a4, void* a5) { + VG_(debugLog)(2, "libcsignal", + "PRE demux sig, a2 = %lu, signo = %lu\n", a2, a3); + if (a2 == 1) + ((void(*)(int))a1) (a3); + else + ((void(*)(int,void*,void*))a1) (a3,a4,a5); + VG_(debugLog)(2, "libcsignal", + "POST demux sig, a2 = %lu, signo = %lu\n", a2, a3); + VG_(do_syscall2)(__NR_sigreturn, (UWord)a5, 0x1E); + /* NOTREACHED */ + __asm__ __volatile__("ud2"); +} +#endif + Int VG_(sigaction) ( Int signum, const vki_sigaction_toK_t* act, vki_sigaction_fromK_t* oldact) @@ -200,6 +229,51 @@ Int VG_(sigaction) ( Int signum, _VKI_NSIG_WORDS * sizeof(UWord)); return sr_isError(res) ? -1 : 0; +# elif defined(VGO_darwin) + /* If we're passing a new action to the kernel, make a copy of the + new action, install our own sa_tramp field in it, and ignore + whatever we were provided with. This is OK because all the + sigaction requests come from m_signals, and are not directly + what the client program requested, so there is no chance that we + will inadvertantly ignore the sa_tramp field requested by the + client. (In fact m_signals does ignore it when building signal + frames for the client, but that's a completely different + matter). + + If we're receiving an old action from the kernel, be very + paranoid and make sure the kernel doesn't trash bits of memory + that we don't expect it to. */ + SysRes res; + + vki_sigaction_toK_t actCopy; + struct { + ULong before[2]; + vki_sigaction_fromK_t oa; + ULong after[2]; + } + oldactCopy; + + vki_sigaction_toK_t* real_act; + vki_sigaction_fromK_t* real_oldact; + + real_act = act ? &actCopy : NULL; + real_oldact = oldact ? &oldactCopy.oa : NULL; + VG_(memset)(&oldactCopy, 0x55, sizeof(oldactCopy)); + if (real_act) { + *real_act = *act; + real_act->sa_tramp = (void*)&darwin_signal_demux; + } + res = VG_(do_syscall3)(__NR_sigaction, + signum, (UWord)real_act, (UWord)real_oldact); + if (real_oldact) { + vg_assert(oldactCopy.before[0] == 0x5555555555555555ULL); + vg_assert(oldactCopy.before[1] == 0x5555555555555555ULL); + vg_assert(oldactCopy.after[0] == 0x5555555555555555ULL); + vg_assert(oldactCopy.after[1] == 0x5555555555555555ULL); + *oldact = *real_oldact; + } + return sr_isError(res) ? -1 : 0; + # else # error "Unsupported OS" # endif @@ -213,6 +287,11 @@ VG_(convert_sigaction_fromK_to_toK)( vki_sigaction_fromK_t* fromK, { # if defined(VGO_linux) || defined(VGO_aix5) *toK = *fromK; +# elif defined(VGO_darwin) + toK->ksa_handler = fromK->ksa_handler; + toK->sa_tramp = NULL; /* the cause of all the difficulty */ + toK->sa_mask = fromK->sa_mask; + toK->sa_flags = fromK->sa_flags; # else # error "Unsupported OS" # endif @@ -221,7 +300,14 @@ VG_(convert_sigaction_fromK_to_toK)( vki_sigaction_fromK_t* fromK, Int VG_(kill)( Int pid, Int signo ) { +# if defined(VGO_linux) || defined(VGO_aix5) SysRes res = VG_(do_syscall2)(__NR_kill, pid, signo); +# elif defined(VGO_darwin) + SysRes res = VG_(do_syscall3)(__NR_kill, + pid, signo, 1/*posix-compliant*/); +# else +# error "Unsupported OS" +# endif return sr_isError(res) ? -1 : 0; } @@ -234,6 +320,12 @@ Int VG_(tkill)( Int lwpid, Int signo ) res = VG_(do_syscall2)(__NR_kill, lwpid, signo); return sr_isError(res) ? -1 : 0; +# elif defined(VGO_darwin) + // Note that the __pthread_kill syscall takes a Mach thread, not a pthread. + SysRes res; + res = VG_(do_syscall2)(__NR___pthread_kill, lwpid, signo); + return sr_isError(res) ? -1 : 0; + # else # error "Unsupported plat" # endif @@ -360,8 +452,113 @@ Int VG_(sigtimedwait_zero)( const vki_sigset_t *set, return i; } +/* ---------- sigtimedwait_zero: Darwin ----------- */ + +#elif defined(VGO_darwin) + +//static void show_set ( HChar* str, const vki_sigset_t* set ) { +// Int i; +// VG_(printf)("%s { ", str); +// for (i = 1; i <= _VKI_NSIG; i++) { +// if (VG_(sigismember)(set, i)) +// VG_(printf)("%u ", i); +// } +// VG_(printf)("}\n"); +//} + +static void sigtimedwait_zero_handler ( Int sig ) +{ + /* XXX this is wrong -- get rid of these. We could + get _any_ signal here */ + vg_assert(sig != VKI_SIGILL); + vg_assert(sig != VKI_SIGSEGV); + vg_assert(sig != VKI_SIGBUS); + vg_assert(sig != VKI_SIGTRAP); + /* do nothing */ +} + +Int VG_(sigtimedwait_zero)( const vki_sigset_t *set, + vki_siginfo_t *info ) +{ + const Bool debug = False; + Int i, ir; + SysRes sr; + vki_sigset_t pending, blocked, allbutone; + vki_sigaction_toK_t sa, saved_sa2; + vki_sigaction_fromK_t saved_sa; + + //show_set("STWZ: looking for", set); + + /* Find out what's pending: Darwin sigpending */ + sr = VG_(do_syscall1)(__NR_sigpending, (UWord)&pending); + vg_assert(!sr_isError(sr)); + + /* don't try for signals not in 'set' */ + /* pending = pending `intersect` set */ + VG_(sigintersectset)(&pending, (vki_sigset_t*)set); + + /* don't try for signals not blocked at the moment */ + ir = VG_(sigprocmask)(VKI_SIG_SETMASK, NULL, &blocked); + vg_assert(ir == 0); + + /* pending = pending `intersect` blocked */ + VG_(sigintersectset)(&pending, &blocked); + + /* decide which signal we're going to snarf */ + for (i = 1; i < _VKI_NSIG; i++) + if (VG_(sigismember)(&pending,i)) + break; + + if (i == _VKI_NSIG) + return 0; + + if (debug) + VG_(debugLog)(0, "libcsignal", + "sigtimedwait_zero: snarfing signal %d\n", i ); + + /* fetch signal i. + pre: i is blocked and pending + pre: we are the only thread running + */ + /* Set up alternative signal handler */ + VG_(sigfillset)(&sa.sa_mask); + sa.ksa_handler = &sigtimedwait_zero_handler; + sa.sa_flags = 0; + ir = VG_(sigaction)(i, &sa, &saved_sa); + vg_assert(ir == 0); + + /* Switch signal masks and wait for the signal. This should happen + immediately, since we've already established it is pending and + blocked. */ + VG_(sigfillset)(&allbutone); + VG_(sigdelset)(&allbutone, i); + /* Note: pass the sig mask by value here, not reference (!) */ + vg_assert(_VKI_NSIG_WORDS == 1); + sr = VG_(do_syscall3)(__NR_sigsuspend_nocancel, + (UWord)allbutone.sig[0], 0,0); + if (debug) + VG_(debugLog)(0, "libcsignal", + "sigtimedwait_zero: sigsuspend got " + "res: %s %#lx\n", + sr_isError(sr) ? "FAIL" : "SUCCESS", + sr_isError(sr) ? sr_Err(sr) : sr_Res(sr)); + vg_assert(sr_isError(sr)); + vg_assert(sr_Err(sr) == VKI_EINTR); + + /* Restore signal's handler to whatever it was before */ + VG_(convert_sigaction_fromK_to_toK)( &saved_sa, &saved_sa2 ); + ir = VG_(sigaction)(i, &saved_sa2, NULL); + vg_assert(ir == 0); + + /* This is bogus - we could get more info from the sighandler. */ + VG_(memset)( info, 0, sizeof(*info) ); + info->si_signo = i; + + return i; +} + #else -# error Unknown OS +# error "Unknown OS" #endif /*--------------------------------------------------------------------*/ diff --git a/coregrind/m_mach/mach_basics.c b/coregrind/m_mach/mach_basics.c new file mode 100644 index 000000000..cd6df6c59 --- /dev/null +++ b/coregrind/m_mach/mach_basics.c @@ -0,0 +1,101 @@ + +/*--------------------------------------------------------------------*/ +/*--- Basic Mach interface functions mach_basics.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2005 Apple Inc. + Greg Parker gparker@apple.com + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics.h" +#include "pub_core_mach.h" + +#if defined(VGO_darwin) + +#include +#include + +/* From mach_traps--darwin.S */ +//extern mach_port_name_t host_self_trap(void); +extern mach_port_name_t thread_self_trap(void); +extern mach_port_t mach_reply_port(void); + +/* Global variables set in mach_init() */ +vm_size_t vm_page_size = 0; +mach_port_name_t mach_task_self_ = 0; + + +mach_port_name_t mach_thread_self(void) +{ + return thread_self_trap(); +} + +static mach_port_t reply = 0; + +mach_port_t mig_get_reply_port(void) +{ + if (!reply) reply = mach_reply_port(); + return reply; + // GrP fixme is just one enough for valgrind's own use? + // might work if valgrind never blocks in mig calls on + // its own behalf, and doesn't call mig outside the semaphore +} + + +void mig_dealloc_reply_port(mach_port_t reply_port) +{ +} + + +void mig_put_reply_port(mach_port_t reply_port) +{ +} + + +/* Initialize Mach global data. + Should be called early in main(). */ +void VG_(mach_init)(void) +{ + reply = 0; + mach_task_self_ = task_self_trap(); + + // GrP fixme host_page_size(host_self_trap(), &vm_page_size); + vm_page_size = 4096; +} + + +#else + +// For platforms that don't actually have Mach kernel interfaces + +void VG_(mach_init)(void) +{ + // do nothing +} + +#endif + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_mach/mach_msg.c b/coregrind/m_mach/mach_msg.c new file mode 100644 index 000000000..0b9aa968e --- /dev/null +++ b/coregrind/m_mach/mach_msg.c @@ -0,0 +1,106 @@ +#include "pub_core_basics.h" +#include "pub_core_mach.h" + +#if defined(VGO_darwin) + +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * HISTORY + * $Log:mach_msg.c,v $ + * Revision 2.3 92/01/23 15:22:17 rpd + * Fixed to not pass MACH_SEND_INTERRUPT and MACH_RCV_INTERRUPT + * to the kernel. + * [92/01/20 rpd] + * + * Revision 2.2 92/01/15 17:17:13 rpd + * Created from msg.c. + * [92/01/15 rpd] + * + */ + +#include +#include + +#define LIBMACH_OPTIONS (MACH_SEND_INTERRUPT|MACH_RCV_INTERRUPT) + +extern mach_msg_return_t +mach_msg_trap(mach_msg_header_t *msg, + mach_msg_option_t option, + mach_msg_size_t send_size, + mach_msg_size_t rcv_size, + mach_port_t rcv_name, + mach_msg_timeout_t timeout, + mach_port_t notify); + +mach_msg_return_t +mach_msg(msg, option, send_size, rcv_size, rcv_name, timeout, notify) + mach_msg_header_t *msg; + mach_msg_option_t option; + mach_msg_size_t send_size; + mach_msg_size_t rcv_size; + mach_port_t rcv_name; + mach_msg_timeout_t timeout; + mach_port_t notify; +{ + mach_msg_return_t mr; + + /* + * Consider the following cases: + *1) Errors in pseudo-receive (eg, MACH_SEND_INTERRUPTED + *plus special bits). + *2) Use of MACH_SEND_INTERRUPT/MACH_RCV_INTERRUPT options. + *3) RPC calls with interruptions in one/both halves. + * + * We refrain from passing the option bits that we implement + * to the kernel. This prevents their presence from inhibiting + * the kernel's fast paths (when it checks the option value). + */ + + mr = mach_msg_trap(msg, option &~ LIBMACH_OPTIONS, + send_size, rcv_size, rcv_name, + timeout, notify); + if (mr == MACH_MSG_SUCCESS) + return MACH_MSG_SUCCESS; + + if ((option & MACH_SEND_INTERRUPT) == 0) + while (mr == MACH_SEND_INTERRUPTED) + mr = mach_msg_trap(msg, + option &~ LIBMACH_OPTIONS, + send_size, rcv_size, rcv_name, + timeout, notify); + + if ((option & MACH_RCV_INTERRUPT) == 0) + while (mr == MACH_RCV_INTERRUPTED) + mr = mach_msg_trap(msg, + option &~ (LIBMACH_OPTIONS|MACH_SEND_MSG), + 0, rcv_size, rcv_name, + timeout, notify); + + return mr; +} + +#endif diff --git a/coregrind/m_mach/mach_traps-amd64-darwin.S b/coregrind/m_mach/mach_traps-amd64-darwin.S new file mode 100644 index 000000000..7a85e41c5 --- /dev/null +++ b/coregrind/m_mach/mach_traps-amd64-darwin.S @@ -0,0 +1,136 @@ +/*--------------------------------------------------------------------*/ +/*--- Basic Mach traps mach_traps.S ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2007 Apple Inc. + Greg Parker gparker@apple.com + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "vki/vki-scnums-darwin.h" + + // mach_port_name_t task_self_trap(void) + .text + .align 4 + .globl _task_self_trap +_task_self_trap: + movq $__NR_task_self_trap, %rax + movq %rcx, %r10 + syscall + ret + +// DDD: doesn't get used... +// // mach_port_name_t host_self_trap(void) +// .text +// .align 4 +// .globl _host_self_trap +//_host_self_trap: +// movq $__NR_host_self_trap, %rax +// movq %rcx, %r10 +// syscall +// ret + + // mach_port_name_t thread_self_trap(void) + .text + .align 4 + .globl _thread_self_trap +_thread_self_trap: + movq $__NR_thread_self_trap, %rax + movq %rcx, %r10 + syscall + ret + + // mach_msg_return_t mach_msg_trap(...) + .text + .align 4 + .globl _mach_msg_trap +_mach_msg_trap: + movq $__NR_mach_msg_trap, %rax + movq %rcx, %r10 + syscall + ret + + // mach_port_t mach_reply_port(...) + .text + .align 4 + .globl _mach_reply_port +_mach_reply_port: + movq $__NR_mach_reply_port, %rax + movq %rcx, %r10 + syscall + ret + + // boolean_t swtch_pri(int) + .text + .align 4 + .globl _swtch_pri +_swtch_pri: + movq $__NR_swtch_pri, %rax + movq %rcx, %r10 + syscall + ret + + // kern_return_t semaphore_wait(semaphore_t) + .text + .align 4 + .globl _semaphore_wait +_semaphore_wait: + movq $__NR_semaphore_wait_trap, %rax + movq %rcx, %r10 + syscall + ret + + // kern_return_t semaphore_signal(semaphore_t) + .text + .align 4 + .globl _semaphore_signal +_semaphore_signal: + movq $__NR_semaphore_signal_trap, %rax + movq %rcx, %r10 + syscall + ret + + // kern_return_t semaphore_signal_thread(semaphore_t, thread_t) + .text + .align 4 + .globl _semaphore_signal_thread +_semaphore_signal_thread: + movq $__NR_semaphore_signal_thread_trap, %rax + movq %rcx, %r10 + syscall + ret + + // kern_return_t semaphore_wait_signal(semaphore_t, semaphore_t) + .text + .align 4 + .globl _semaphore_wait_signal +_semaphore_wait_signal: + movq $__NR_semaphore_wait_signal_trap, %rax + movq %rcx, %r10 + syscall + ret + + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_mach/mach_traps-x86-darwin.S b/coregrind/m_mach/mach_traps-x86-darwin.S new file mode 100644 index 000000000..b843834a4 --- /dev/null +++ b/coregrind/m_mach/mach_traps-x86-darwin.S @@ -0,0 +1,124 @@ +/*--------------------------------------------------------------------*/ +/*--- Basic Mach traps mach_traps.S ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2006 Apple Inc. + Greg Parker gparker@apple.com + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + + + // mach_port_name_t task_self_trap(void) + .text + .align 4 + .globl _task_self_trap +_task_self_trap: + mov $-28, %eax + int $0x81 + ret + + // mach_port_name_t host_self_trap(void) + .text + .align 4 + .globl _host_self_trap +_host_self_trap: + mov $-29, %eax + int $0x81 + ret + + // mach_port_name_t thread_self_trap(void) + .text + .align 4 + .globl _thread_self_trap +_thread_self_trap: + mov $-27, %eax + int $0x81 + ret + + // mach_msg_return_t mach_msg_trap(...) + .text + .align 4 + .globl _mach_msg_trap +_mach_msg_trap: + mov $-31, %eax + int $0x81 + ret + + // mach_port_t mach_reply_port(...) + .text + .align 4 + .globl _mach_reply_port +_mach_reply_port: + mov $-26, %eax + int $0x81 + ret + + // boolean_t swtch_pri(int) + .text + .align 4 + .globl _swtch_pri +_swtch_pri: + mov $-59, %eax + int $0x81 + ret + + // kern_return_t semaphore_wait(semaphore_t) + .text + .align 4 + .globl _semaphore_wait +_semaphore_wait: + mov $-36, %eax + int $0x81 + ret + + // kern_return_t semaphore_signal(semaphore_t) + .text + .align 4 + .globl _semaphore_signal +_semaphore_signal: + mov $-33, %eax + int $0x81 + ret + + // kern_return_t semaphore_signal_thread(semaphore_t, thread_t) + .text + .align 4 + .globl _semaphore_signal_thread +_semaphore_signal_thread: + mov $-35, %eax + int $0x81 + ret + + // kern_return_t semaphore_wait_signal(semaphore_t, semaphore_t) + .text + .align 4 + .globl _semaphore_wait_signal +_semaphore_wait_signal: + mov $-37, %eax + int $0x81 + ret + + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_machine.c b/coregrind/m_machine.c index 0429bfe6f..e85b917c2 100644 --- a/coregrind/m_machine.c +++ b/coregrind/m_machine.c @@ -98,6 +98,8 @@ void VG_(set_syscall_return_shadows) ( ThreadId tid, VG_(threads)[tid].arch.vex_shadow2.guest_GPR3 = s2res; VG_(threads)[tid].arch.vex_shadow1.guest_GPR4 = s1err; VG_(threads)[tid].arch.vex_shadow2.guest_GPR4 = s2err; +# elif defined(VGO_darwin) + // GrP fixme darwin syscalls may return more values (2 registers plus error) # else # error "Unknown plat" # endif @@ -672,8 +674,8 @@ void VG_(machine_get_VexArchInfo)( /*OUT*/VexArch* pVa, // produce a pointer to the actual entry point for the function. void* VG_(fnptr_to_fnentry)( void* f ) { -#if defined(VGP_x86_linux) || defined(VGP_amd64_linux) \ - || defined(VGP_ppc32_linux) +#if defined(VGP_x86_linux) || defined(VGP_amd64_linux) || \ + defined(VGP_ppc32_linux) || defined(VGO_darwin) return f; #elif defined(VGP_ppc64_linux) || defined(VGP_ppc32_aix5) \ || defined(VGP_ppc64_aix5) diff --git a/coregrind/m_main.c b/coregrind/m_main.c index e880e3c14..f3dfbcd57 100644 --- a/coregrind/m_main.c +++ b/coregrind/m_main.c @@ -48,6 +48,7 @@ #include "pub_core_libcproc.h" #include "pub_core_libcsignal.h" #include "pub_core_syscall.h" // VG_(strerror) +#include "pub_core_mach.h" #include "pub_core_machine.h" #include "pub_core_mallocfree.h" #include "pub_core_options.h" @@ -435,6 +436,9 @@ static Bool main_process_cmd_line_options( const HChar* toolname ) else if VG_STR_CLO (arg, "--kernel-variant", VG_(clo_kernel_variant)) {} + else if VG_BOOL_CLO(arg, "--auto-run-dsymutil", + VG_(clo_auto_run_dsymutil)) {} + else if VG_BINT_CLO(arg, "--vex-iropt-verbosity", VG_(clo_vex_control).iropt_verbosity, 0, 10) {} else if VG_BINT_CLO(arg, "--vex-iropt-level", @@ -956,6 +960,14 @@ static void setup_file_descriptors(void) rl.rlim_max = 1024; } +# if defined(VGO_darwin) + /* Darwin lies. It reports file max as RLIM_INFINITY but + silently disallows anything bigger than 10240. */ + if (rl.rlim_cur >= 10240 && rl.rlim_max == 0x7fffffffffffffffULL) { + rl.rlim_max = 10240; + } +# endif + if (show) VG_(printf)("fd limits: host, before: cur %lu max %lu\n", (UWord)rl.rlim_cur, (UWord)rl.rlim_max); @@ -1152,6 +1164,14 @@ Int valgrind_main ( Int argc, HChar **argv, HChar **envp ) /* This is needed to make VG_(getenv) usable early. */ VG_(client_envp) = (Char**)envp; + //-------------------------------------------------------------- + // Start up Mach kernel interface, if any + // p: none + //-------------------------------------------------------------- +# if defined(VGO_darwin) + VG_(mach_init)(); +# endif + //-------------------------------------------------------------- // Start up the logging mechanism // p: none @@ -1228,10 +1248,29 @@ Int valgrind_main ( Int argc, HChar **argv, HChar **envp ) } # endif + //-------------------------------------------------------------- + // Darwin only: munmap address-space-filling segments + // (oversized pagezero or stack) + // p: none + //-------------------------------------------------------------- +#if defined(VGO_darwin) +# if VG_WORDSIZE == 4 + VG_(do_syscall2)(__NR_munmap, 0x00000000, 0xf0000000); +# else + // open up client space + VG_(do_syscall2)(__NR_munmap, 0x100000000, 0x700000000000-0x100000000); + // open up client stack and dyld + VG_(do_syscall2)(__NR_munmap, 0x7fff5c000000, 0x4000000); +# endif +#endif + //-------------------------------------------------------------- // Ensure we're on a plausible stack. // p: logging //-------------------------------------------------------------- +#if defined(VGO_darwin) + // Darwin doesn't use the interim stack. +#else VG_(debugLog)(1, "main", "Checking current stack is plausible\n"); { HChar* limLo = (HChar*)(&VG_(interim_stack).bytes[0]); HChar* limHi = limLo + sizeof(VG_(interim_stack)); @@ -1259,11 +1298,12 @@ Int valgrind_main ( Int argc, HChar **argv, HChar **envp ) VG_(debugLog)(0, "main", " Cannot continue. Sorry.\n"); VG_(exit)(1); } +#endif //-------------------------------------------------------------- // Start up the address space manager, and determine the // approximate location of the client's stack - // p: logging, plausible-stack + // p: logging, plausible-stack, darwin-munmap //-------------------------------------------------------------- VG_(debugLog)(1, "main", "Starting the address space manager\n"); vg_assert(VKI_PAGE_SIZE == 4096 || VKI_PAGE_SIZE == 65536); @@ -1453,7 +1493,7 @@ Int valgrind_main ( Int argc, HChar **argv, HChar **envp ) if (!need_help) { VG_(debugLog)(1, "main", "Create initial image\n"); -# if defined(VGO_linux) +# if defined(VGO_linux) || defined(VGO_darwin) the_iicii.argv = argv; the_iicii.envp = envp; the_iicii.toolname = toolname; @@ -1509,6 +1549,10 @@ Int valgrind_main ( Int argc, HChar **argv, HChar **envp ) // when it tries to open /proc//cmdline for itself. // p: setup file descriptors //-------------------------------------------------------------- +#if !HAVE_PROC + // client shouldn't be using /proc! + VG_(cl_cmdline_fd) = -1; +#else if (!need_help) { HChar buf[50], buf2[50+64]; HChar nul[1]; @@ -1546,6 +1590,7 @@ Int valgrind_main ( Int argc, HChar **argv, HChar **envp ) VG_(cl_cmdline_fd) = fd; } +#endif //-------------------------------------------------------------- // Init tool part 1: pre_clo_init @@ -1650,6 +1695,8 @@ Int valgrind_main ( Int argc, HChar **argv, HChar **envp ) iters = 5; # elif defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) iters = 4; +# elif defined(VGO_darwin) + iters = 3; # else # error "Unknown plat" # endif @@ -1752,6 +1799,20 @@ Int valgrind_main ( Int argc, HChar **argv, HChar **envp ) VG_(free)(changes); } +# elif defined(VGO_darwin) + { Addr* seg_starts; + Int n_seg_starts; + seg_starts = VG_(get_segment_starts)( &n_seg_starts ); + vg_assert(seg_starts && n_seg_starts >= 0); + + /* show them all to the debug info reader. + Don't read from V segments (unlike Linux) */ + // GrP fixme really? + for (i = 0; i < n_seg_starts; i++) + VG_(di_notify_mmap)( seg_starts[i], False/*don't allow_SkFileV*/ ); + + VG_(free)( seg_starts ); + } # else # error Unknown OS # endif @@ -2168,6 +2229,13 @@ void shutdown_actions_NORETURN( ThreadId tid, /* We were killed by a fatal signal, so replicate the effect */ vg_assert(VG_(threads)[tid].os_state.fatalsig != 0); VG_(kill_self)(VG_(threads)[tid].os_state.fatalsig); + /* we shouldn't be alive at this point. But VG_(kill_self) + sometimes fails with EPERM on Darwin, for unclear reasons. */ +# if defined(VGO_darwin) + VG_(debugLog)(0, "main", "VG_(kill_self) failed. Exiting normally.\n"); + VG_(exit)(0); /* bogus, but we really need to exit now */ + /* fall through .. */ +# endif VG_(core_panic)("main(): signal was supposed to be fatal"); break; @@ -2181,9 +2249,11 @@ void shutdown_actions_NORETURN( ThreadId tid, /* Final clean-up before terminating the process. Clean up the client by calling __libc_freeres() (if requested) This is Linux-specific? + GrP fixme glibc-specific, anyway */ static void final_tidyup(ThreadId tid) { +#if !defined(VGO_darwin) # if defined(VGP_ppc64_linux) Addr r2; # endif @@ -2247,6 +2317,7 @@ static void final_tidyup(ThreadId tid) VG_(scheduler)(tid); vg_assert(VG_(is_exiting)(tid)); +#endif } @@ -2585,6 +2656,59 @@ void getpass_auto ( void ) { vg_assert(0); } void max_pw_passlen ( void ) { vg_assert(0); } +/*====================================================================*/ +/*=== Getting to main() alive: darwin ===*/ +/*====================================================================*/ + +#elif defined(VGO_darwin) + +void* __memcpy_chk(void *dest, const void *src, SizeT n, SizeT n2); +void* __memcpy_chk(void *dest, const void *src, SizeT n, SizeT n2) { + // skip check + return VG_(memcpy)(dest,src,n); +} +void* __memset_chk(void *s, int c, SizeT n, SizeT n2); +void* __memset_chk(void *s, int c, SizeT n, SizeT n2) { + // skip check + return VG_(memset)(s,c,n); +} +void bzero(void *s, SizeT n); +void bzero(void *s, SizeT n) { + VG_(memset)(s,0,n); +} + +void* memcpy(void *dest, const void *src, SizeT n); +void* memcpy(void *dest, const void *src, SizeT n) { + return VG_(memcpy)(dest,src,n); +} +void* memset(void *s, int c, SizeT n); +void* memset(void *s, int c, SizeT n) { + return VG_(memset)(s,c,n); +} + +/* _start in m_start--darwin.S calls _start_in_C_darwin(). */ + +/* Avoid compiler warnings: this fn _is_ used, but labelling it + 'static' causes gcc to complain it isn't. */ +void _start_in_C_darwin ( UWord* pArgc ); +void _start_in_C_darwin ( UWord* pArgc ) +{ + Int r; + Int argc = *(Int *)pArgc; // not pArgc[0] on LP64 + HChar** argv = (HChar**)&pArgc[1]; + HChar** envp = (HChar**)&pArgc[1+argc+1]; + + VG_(memset)( &the_iicii, 0, sizeof(the_iicii) ); + VG_(memset)( &the_iifii, 0, sizeof(the_iifii) ); + + the_iicii.sp_at_startup = (Addr)pArgc; + + r = valgrind_main( (Int)argc, argv, envp ); + /* NOTREACHED */ + VG_(exit)(r); +} + + #else # error "Unknown OS" diff --git a/coregrind/m_options.c b/coregrind/m_options.c index 2a06fcd12..089ed1d5b 100644 --- a/coregrind/m_options.c +++ b/coregrind/m_options.c @@ -90,6 +90,7 @@ Word VG_(clo_main_stacksize) = 0; /* use client's rlimit.stack */ Bool VG_(clo_wait_for_gdb) = False; VgSmc VG_(clo_smc_check) = Vg_SmcStack; HChar* VG_(clo_kernel_variant) = NULL; +Bool VG_(clo_auto_run_dsymutil) = False; /*====================================================================*/ diff --git a/coregrind/m_redir.c b/coregrind/m_redir.c index d31d90f79..d824a79a4 100644 --- a/coregrind/m_redir.c +++ b/coregrind/m_redir.c @@ -940,6 +940,27 @@ void VG_(redir_initialise) ( void ) # elif defined(VGP_ppc64_aix5) /* nothing so far */ +# elif defined(VGO_darwin) + /* If we're using memcheck, use these intercepts right from + the start, otherwise dyld makes a lot of noise. */ + if (0==VG_(strcmp)("Memcheck", VG_(details).name)) { + add_hardwired_spec("dyld", "strcmp", + (Addr)&VG_(darwin_REDIR_FOR_strcmp), NULL); + add_hardwired_spec("dyld", "strlen", + (Addr)&VG_(darwin_REDIR_FOR_strlen), NULL); + add_hardwired_spec("dyld", "strcat", + (Addr)&VG_(darwin_REDIR_FOR_strcat), NULL); + add_hardwired_spec("dyld", "strcpy", + (Addr)&VG_(darwin_REDIR_FOR_strcpy), NULL); + add_hardwired_spec("dyld", "strlcat", + (Addr)&VG_(darwin_REDIR_FOR_strlcat), NULL); +#if defined(VGP_amd64_darwin) + // DDD: #warning fixme rdar://6166275 + add_hardwired_spec("dyld", "arc4random", + (Addr)&VG_(darwin_REDIR_FOR_arc4random), NULL); +#endif + } + # else # error Unknown platform # endif diff --git a/coregrind/m_replacemalloc/vg_replace_malloc.c b/coregrind/m_replacemalloc/vg_replace_malloc.c index 5e2757bae..b36e80282 100644 --- a/coregrind/m_replacemalloc/vg_replace_malloc.c +++ b/coregrind/m_replacemalloc/vg_replace_malloc.c @@ -144,6 +144,21 @@ static void init(void) __attribute__((constructor)); return v; \ } +#define ZONEALLOC_or_NULL(soname, fnname, vg_replacement) \ + \ + void* VG_REPLACE_FUNCTION_ZU(soname,fnname) (void *zone, SizeT n); \ + void* VG_REPLACE_FUNCTION_ZU(soname,fnname) (void *zone, SizeT n) \ + { \ + void* v; \ + \ + if (!init_done) init(); \ + MALLOC_TRACE(#fnname "(%p, %llu)", zone, (ULong)n ); \ + \ + v = (void*)VALGRIND_NON_SIMD_CALL1( info.tl_##vg_replacement, n ); \ + MALLOC_TRACE(" = %p", v ); \ + return v; \ + } + /* Generate a replacement for 'fnname' in object 'soname', which calls 'vg_replacement' to allocate memory. If that fails, it bombs the @@ -178,6 +193,8 @@ ALLOC_or_NULL(VG_Z_LIBSTDCXX_SONAME, malloc, malloc); ALLOC_or_NULL(VG_Z_LIBC_SONAME, malloc, malloc); #if defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) ALLOC_or_NULL(VG_Z_LIBC_SONAME, malloc_common, malloc); +#elif defined(VGO_darwin) +ZONEALLOC_or_NULL(VG_Z_LIBC_SONAME, malloc_zone_malloc, malloc); #endif @@ -197,7 +214,7 @@ ALLOC_or_BOMB(VG_Z_LIBC_SONAME, __builtin_new, __builtin_new); #endif // operator new(unsigned long), GNU mangling -#if VG_WORDSIZE == 8 || defined(VGP_ppc32_aix5) +#if VG_WORDSIZE == 8 || defined(VGP_ppc32_aix5) || defined(VGO_darwin) ALLOC_or_BOMB(VG_Z_LIBSTDCXX_SONAME, _Znwm, __builtin_new); ALLOC_or_BOMB(VG_Z_LIBC_SONAME, _Znwm, __builtin_new); #endif @@ -217,7 +234,7 @@ ALLOC_or_BOMB(VG_Z_LIBC_SONAME, __builtin_new, __builtin_new); #endif // operator new(unsigned long, std::nothrow_t const&), GNU mangling -#if VG_WORDSIZE == 8 || defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) +#if VG_WORDSIZE == 8 || defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) || defined(VGO_darwin) ALLOC_or_NULL(VG_Z_LIBSTDCXX_SONAME, _ZnwmRKSt9nothrow_t, __builtin_new); ALLOC_or_NULL(VG_Z_LIBC_SONAME, _ZnwmRKSt9nothrow_t, __builtin_new); #endif @@ -241,7 +258,7 @@ ALLOC_or_BOMB(VG_Z_LIBC_SONAME, __builtin_vec_new, __builtin_vec_new ); #endif // operator new[](unsigned long), GNU mangling -#if VG_WORDSIZE == 8 || defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) +#if VG_WORDSIZE == 8 || defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) || defined(VGO_darwin) ALLOC_or_BOMB(VG_Z_LIBSTDCXX_SONAME, _Znam, __builtin_vec_new ); ALLOC_or_BOMB(VG_Z_LIBC_SONAME, _Znam, __builtin_vec_new ); #endif @@ -261,7 +278,7 @@ ALLOC_or_BOMB(VG_Z_LIBC_SONAME, __builtin_vec_new, __builtin_vec_new ); #endif // operator new[](unsigned long, std::nothrow_t const&), GNU mangling -#if VG_WORDSIZE == 8 || defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) +#if VG_WORDSIZE == 8 || defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) || defined(VGO_darwin) ALLOC_or_NULL(VG_Z_LIBSTDCXX_SONAME, _ZnamRKSt9nothrow_t, __builtin_vec_new ); ALLOC_or_NULL(VG_Z_LIBC_SONAME, _ZnamRKSt9nothrow_t, __builtin_vec_new ); #endif @@ -277,6 +294,18 @@ ALLOC_or_BOMB(VG_Z_LIBC_SONAME, __builtin_vec_new, __builtin_vec_new ); /* Generate a replacement for 'fnname' in object 'soname', which calls 'vg_replacement' to free previously allocated memory. */ +#define ZONEFREE(soname, fnname, vg_replacement) \ + \ + void VG_REPLACE_FUNCTION_ZU(soname,fnname) (void *zone, void *p); \ + void VG_REPLACE_FUNCTION_ZU(soname,fnname) (void *zone, void *p) \ + { \ + if (!init_done) init(); \ + MALLOC_TRACE(#vg_replacement "(%p, %p)", zone, p ); \ + if (p == NULL) \ + return; \ + (void)VALGRIND_NON_SIMD_CALL1( info.tl_##vg_replacement, p ); \ + } + #define FREE(soname, fnname, vg_replacement) \ \ void VG_REPLACE_FUNCTION_ZU(soname,fnname) (void *p); \ @@ -294,6 +323,8 @@ FREE(VG_Z_LIBSTDCXX_SONAME, free, free ); FREE(VG_Z_LIBC_SONAME, free, free ); #if defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) FREE(VG_Z_LIBC_SONAME, free_common, free ); +#elif defined(VGO_darwin) +ZONEFREE(VG_Z_LIBC_SONAME, malloc_zone_free, free ); #endif @@ -350,6 +381,21 @@ FREE(VG_Z_LIBC_SONAME, _ZdaPvRKSt9nothrow_t, __builtin_vec_delete ); /*---------------------- calloc ----------------------*/ +#define ZONECALLOC(soname, fnname) \ + \ + void* VG_REPLACE_FUNCTION_ZU(soname,fnname) ( void *zone, SizeT nmemb, SizeT size ); \ + void* VG_REPLACE_FUNCTION_ZU(soname,fnname) ( void *zone, SizeT nmemb, SizeT size ) \ + { \ + void* v; \ + \ + if (!init_done) init(); \ + MALLOC_TRACE("calloc(%p, %llu,%llu)", zone, (ULong)nmemb, (ULong)size ); \ + \ + v = (void*)VALGRIND_NON_SIMD_CALL2( info.tl_calloc, nmemb, size ); \ + MALLOC_TRACE(" = %p", v ); \ + return v; \ + } + #define CALLOC(soname, fnname) \ \ void* VG_REPLACE_FUNCTION_ZU(soname,fnname) ( SizeT nmemb, SizeT size ); \ @@ -368,11 +414,37 @@ FREE(VG_Z_LIBC_SONAME, _ZdaPvRKSt9nothrow_t, __builtin_vec_delete ); CALLOC(VG_Z_LIBC_SONAME, calloc); #if defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) CALLOC(VG_Z_LIBC_SONAME, calloc_common); +#elif defined(VGO_darwin) +ZONECALLOC(VG_Z_LIBC_SONAME, malloc_zone_calloc); #endif /*---------------------- realloc ----------------------*/ +#define ZONEREALLOC(soname, fnname) \ + \ + void* VG_REPLACE_FUNCTION_ZU(soname,fnname) ( void *zone, void* ptrV, SizeT new_size );\ + void* VG_REPLACE_FUNCTION_ZU(soname,fnname) ( void *zone, void* ptrV, SizeT new_size ) \ + { \ + void* v; \ + \ + if (!init_done) init(); \ + MALLOC_TRACE("realloc(%p,%p,%llu)", zone, ptrV, (ULong)new_size ); \ + \ + if (ptrV == NULL) \ + /* We need to call a malloc-like function; so let's use \ + one which we know exists. GrP fixme use zonemalloc instead? */ \ + return VG_REPLACE_FUNCTION_ZU(VG_Z_LIBC_SONAME,malloc) (new_size); \ + if (new_size <= 0) { \ + VG_REPLACE_FUNCTION_ZU(VG_Z_LIBC_SONAME,free)(ptrV); \ + MALLOC_TRACE(" = 0"); \ + return NULL; \ + } \ + v = (void*)VALGRIND_NON_SIMD_CALL2( info.tl_realloc, ptrV, new_size ); \ + MALLOC_TRACE(" = %p", v ); \ + return v; \ + } + #define REALLOC(soname, fnname) \ \ void* VG_REPLACE_FUNCTION_ZU(soname,fnname) ( void* ptrV, SizeT new_size );\ @@ -400,11 +472,36 @@ CALLOC(VG_Z_LIBC_SONAME, calloc_common); REALLOC(VG_Z_LIBC_SONAME, realloc); #if defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) REALLOC(VG_Z_LIBC_SONAME, realloc_common); +#elif defined(VGO_darwin) +ZONEREALLOC(VG_Z_LIBC_SONAME, malloc_zone_realloc); #endif /*---------------------- memalign ----------------------*/ +#define ZONEMEMALIGN(soname, fnname) \ + \ + void* VG_REPLACE_FUNCTION_ZU(soname,fnname) ( void *zone, SizeT alignment, SizeT n ); \ + void* VG_REPLACE_FUNCTION_ZU(soname,fnname) ( void *zone, SizeT alignment, SizeT n ) \ + { \ + void* v; \ + \ + if (!init_done) init(); \ + MALLOC_TRACE("memalign(%p, al %llu, size %llu)", \ + zone, (ULong)alignment, (ULong)n ); \ + \ + /* Round up to minimum alignment if necessary. */ \ + if (alignment < VG_MIN_MALLOC_SZB) \ + alignment = VG_MIN_MALLOC_SZB; \ + \ + /* Round up to nearest power-of-two if necessary (like glibc). */ \ + while (0 != (alignment & (alignment - 1))) alignment++; \ + \ + v = (void*)VALGRIND_NON_SIMD_CALL2( info.tl_memalign, alignment, n ); \ + MALLOC_TRACE(" = %p", v ); \ + return v; \ + } + #define MEMALIGN(soname, fnname) \ \ void* VG_REPLACE_FUNCTION_ZU(soname,fnname) ( SizeT alignment, SizeT n ); \ @@ -429,6 +526,9 @@ REALLOC(VG_Z_LIBC_SONAME, realloc_common); } MEMALIGN(VG_Z_LIBC_SONAME, memalign); +#if defined(VGO_darwin) +ZONEMEMALIGN(VG_Z_LIBC_SONAME, malloc_zone_memalign); +#endif /*---------------------- valloc ----------------------*/ @@ -454,7 +554,23 @@ static int local__getpagesize ( void ) { ((SizeT)pszB, size); \ } +#define ZONEVALLOC(soname, fnname) \ + \ + void* VG_REPLACE_FUNCTION_ZU(soname,fnname) ( void *zone, SizeT size ); \ + void* VG_REPLACE_FUNCTION_ZU(soname,fnname) ( void *zone, SizeT size ) \ + { \ + static int pszB = 0; \ + extern int getpagesize (void); \ + if (pszB == 0) \ + pszB = getpagesize(); \ + return VG_REPLACE_FUNCTION_ZU(VG_Z_LIBC_SONAME,memalign) \ + ((SizeT)pszB, size); \ + } + VALLOC(VG_Z_LIBC_SONAME, valloc); +#if defined(VGO_darwin) +ZONEVALLOC(VG_Z_LIBC_SONAME, malloc_zone_valloc); +#endif /*---------------------- mallopt ----------------------*/ @@ -567,6 +683,7 @@ POSIX_MEMALIGN(VG_Z_LIBC_SONAME, memalign_common); } MALLOC_USABLE_SIZE(VG_Z_LIBC_SONAME, malloc_usable_size); +MALLOC_USABLE_SIZE(VG_Z_LIBC_SONAME, malloc_size); /*---------------------- (unimplemented) ----------------------*/ @@ -623,6 +740,58 @@ MALLOC_STATS(VG_Z_LIBC_SONAME, malloc_stats); MALLINFO(VG_Z_LIBC_SONAME, mallinfo); +#if defined(VGO_darwin) + +static vki_malloc_zone_t vg_default_zone = { + NULL, // reserved + NULL, // reserved + NULL, // GrP fixme malloc_size + (void*)VG_REPLACE_FUNCTION_ZU(VG_Z_LIBC_SONAME, malloc_zone_malloc), + (void*)VG_REPLACE_FUNCTION_ZU(VG_Z_LIBC_SONAME, malloc_zone_calloc), + (void*)VG_REPLACE_FUNCTION_ZU(VG_Z_LIBC_SONAME, malloc_zone_valloc), + (void*)VG_REPLACE_FUNCTION_ZU(VG_Z_LIBC_SONAME, malloc_zone_free), + (void*)VG_REPLACE_FUNCTION_ZU(VG_Z_LIBC_SONAME, malloc_zone_realloc), + NULL, // GrP fixme destroy + "ValgrindMallocZone", + NULL, // batch_malloc + NULL, // batch_free + NULL, // GrP fixme introspect + 2, // version (GrP fixme 3?) + // DDD: this field exists in Mac OS 10.6, but not 10.5. + #if 0 + (void*)VG_REPLACE_FUNCTION_ZU(VG_Z_LIBC_SONAME, malloc_zone_memalign) + #endif +}; + +#define DEFAULT_ZONE(soname, fnname) \ + \ + void *VG_REPLACE_FUNCTION_ZU(soname, fnname) ( void ); \ + void *VG_REPLACE_FUNCTION_ZU(soname, fnname) ( void ) \ + { \ + return &vg_default_zone; \ + } + +#if defined(VGO_darwin) +DEFAULT_ZONE(VG_Z_LIBC_SONAME, malloc_zone_from_ptr); +DEFAULT_ZONE(VG_Z_LIBC_SONAME, malloc_default_zone); +#endif + +// GrP fixme bypass libc's use of zone->introspect->check +#define ZONE_CHECK(soname, fnname) \ + \ + int VG_REPLACE_FUNCTION_ZU(soname, fnname)(void* zone); \ + int VG_REPLACE_FUNCTION_ZU(soname, fnname)(void* zone) \ + { \ + return 1; \ + } + +#if defined(VGO_darwin) +ZONE_CHECK(VG_Z_LIBC_SONAME, malloc_zone_check); +#endif + +#endif + + /* All the code in here is unused until this function is called */ static void init(void) diff --git a/coregrind/m_scheduler/priv_sema.h b/coregrind/m_scheduler/priv_sema.h index dfcab13ef..6693e88a3 100644 --- a/coregrind/m_scheduler/priv_sema.h +++ b/coregrind/m_scheduler/priv_sema.h @@ -33,16 +33,17 @@ /* Not really a semaphore, but use a pipe for a token-passing scheme */ typedef struct { - Int pipe[2]; - Int owner_lwpid; /* who currently has it */ + Int pipe[2]; + Int owner_lwpid; /* who currently has it */ + Bool held_as_LL; /* if held, True == held by a _LL call */ } vg_sema_t; // Nb: this may be OS-specific, but let's not factor it out until we // implement an OS port for which this isn't ok. void ML_(sema_init) ( vg_sema_t *sema ); void ML_(sema_deinit) ( vg_sema_t *sema ); -void ML_(sema_down) ( vg_sema_t *sema ); -void ML_(sema_up) ( vg_sema_t *sema ); +void ML_(sema_down) ( vg_sema_t *sema, Bool as_LL ); +void ML_(sema_up) ( vg_sema_t *sema, Bool as_LL ); #endif // __PRIV_SEMA_H diff --git a/coregrind/m_scheduler/scheduler.c b/coregrind/m_scheduler/scheduler.c index f1177ba12..d0d17e6c9 100644 --- a/coregrind/m_scheduler/scheduler.c +++ b/coregrind/m_scheduler/scheduler.c @@ -71,6 +71,9 @@ #include "pub_core_libcprint.h" #include "pub_core_libcproc.h" #include "pub_core_libcsignal.h" +#if defined(VGO_darwin) +#include "pub_core_mach.h" +#endif #include "pub_core_machine.h" #include "pub_core_mallocfree.h" #include "pub_core_options.h" @@ -154,6 +157,8 @@ HChar* name_of_sched_event ( UInt event ) case VEX_TRC_JMP_SYS_SYSCALL: return "SYSCALL"; case VEX_TRC_JMP_SYS_INT32: return "INT32"; case VEX_TRC_JMP_SYS_INT128: return "INT128"; + case VEX_TRC_JMP_SYS_INT129: return "INT129"; + case VEX_TRC_JMP_SYS_INT130: return "INT130"; case VEX_TRC_JMP_SYS_SYSENTER: return "SYSENTER"; case VEX_TRC_JMP_CLIENTREQ: return "CLIENTREQ"; case VEX_TRC_JMP_YIELD: return "YIELD"; @@ -211,7 +216,7 @@ void VG_(acquire_BigLock)(ThreadId tid, HChar* who) /* First, acquire the_BigLock. We can't do anything else safely prior to this point. Even doing debug printing prior to this point is, technically, wrong. */ - ML_(sema_down)(&the_BigLock); + ML_(sema_down)(&the_BigLock, False/*not LL*/); tst = VG_(get_ThreadState)(tid); @@ -267,9 +272,22 @@ void VG_(release_BigLock)(ThreadId tid, ThreadStatus sleepstate, HChar* who) /* Release the_BigLock; this will reschedule any runnable thread. */ - ML_(sema_up)(&the_BigLock); + ML_(sema_up)(&the_BigLock, False/*not LL*/); } +/* See pub_core_scheduler.h for description */ +void VG_(acquire_BigLock_LL) ( HChar* who ) +{ + ML_(sema_down)(&the_BigLock, True/*LL*/); +} + +/* See pub_core_scheduler.h for description */ +void VG_(release_BigLock_LL) ( HChar* who ) +{ + ML_(sema_up)(&the_BigLock, True/*LL*/); +} + + /* Clear out the ThreadState and release the semaphore. Leaves the ThreadState in VgTs_Zombie state, so that it doesn't get reallocated until the caller is really ready. */ @@ -288,7 +306,7 @@ void VG_(exit_thread)(ThreadId tid) if (VG_(clo_trace_sched)) print_sched_event(tid, "release lock in VG_(exit_thread)"); - ML_(sema_up)(&the_BigLock); + ML_(sema_up)(&the_BigLock, False/*not LL*/); } /* If 'tid' is blocked in a syscall, send it SIGVGKILL so as to get it @@ -300,11 +318,39 @@ void VG_(get_thread_out_of_syscall)(ThreadId tid) vg_assert(!VG_(is_running_thread)(tid)); if (VG_(threads)[tid].status == VgTs_WaitSys) { - if (VG_(clo_trace_signals)) + if (VG_(clo_trace_signals)) { VG_(message)(Vg_DebugMsg, "get_thread_out_of_syscall zaps tid %d lwp %d", tid, VG_(threads)[tid].os_state.lwpid); - VG_(tkill)(VG_(threads)[tid].os_state.lwpid, VG_SIGVGKILL); + } +# if defined(VGO_darwin) + { + // GrP fixme use mach primitives on darwin? + // GrP fixme thread_abort_safely? + // GrP fixme race for thread with WaitSys set but not in syscall yet? + extern kern_return_t thread_abort(mach_port_t); + thread_abort(VG_(threads)[tid].os_state.lwpid); + } +# else + { + __attribute__((unused)) + Int r = VG_(tkill)(VG_(threads)[tid].os_state.lwpid, VG_SIGVGKILL); + /* JRS 2009-Mar-20: should we assert for r==0 (tkill succeeded)? + I'm really not sure. Here's a race scenario which argues + that we shoudn't; but equally I'm not sure the scenario is + even possible, because of constraints caused by the question + of who holds the BigLock when. + + Target thread tid does sys_read on a socket and blocks. This + function gets called, and we observe correctly that tid's + status is WaitSys but then for whatever reason this function + goes very slowly for a while. Then data arrives from + wherever, tid's sys_read returns, tid exits. Then we do + tkill on tid, but tid no longer exists; tkill returns an + error code and the assert fails. */ + /* vg_assert(r == 0); */ + } +# endif } } @@ -355,10 +401,24 @@ static void os_state_clear(ThreadState *tst) { tst->os_state.lwpid = 0; tst->os_state.threadgroup = 0; -# if defined(VGO_aix5) +# if defined(VGO_linux) + /* no other fields to clear */ +# elif defined(VGO_aix5) tst->os_state.cancel_async = False; tst->os_state.cancel_disabled = False; tst->os_state.cancel_progress = Canc_NoRequest; +# elif defined(VGO_darwin) + tst->os_state.post_mach_trap_fn = NULL; + tst->os_state.pthread = 0; + tst->os_state.func_arg = 0; + VG_(memset)(&tst->os_state.child_go, 0, sizeof(tst->os_state.child_go)); + VG_(memset)(&tst->os_state.child_done, 0, sizeof(tst->os_state.child_done)); + tst->os_state.wq_jmpbuf_valid = False; + tst->os_state.remote_port = 0; + tst->os_state.msgh_id = 0; + VG_(memset)(&tst->os_state.mach_args, 0, sizeof(tst->os_state.mach_args)); +# else +# error "Unknown OS" # endif } @@ -419,6 +479,11 @@ static void sched_fork_cleanup(ThreadId me) ThreadId tid; vg_assert(VG_(running_tid) == me); +# if defined(VGO_darwin) + // GrP fixme hack reset Mach ports + VG_(mach_init)(); +# endif + VG_(threads)[me].os_state.lwpid = VG_(gettid)(); VG_(threads)[me].os_state.threadgroup = VG_(getpid)(); @@ -434,7 +499,7 @@ static void sched_fork_cleanup(ThreadId me) /* re-init and take the sema */ ML_(sema_deinit)(&the_BigLock); ML_(sema_init)(&the_BigLock); - ML_(sema_down)(&the_BigLock); + ML_(sema_down)(&the_BigLock, False/*not LL*/); } @@ -807,7 +872,7 @@ static void handle_tt_miss ( ThreadId tid ) } } -static void handle_syscall(ThreadId tid) +static void handle_syscall(ThreadId tid, UInt trc) { ThreadState * volatile tst = VG_(get_ThreadState)(tid); Bool jumped; @@ -820,7 +885,7 @@ static void handle_syscall(ThreadId tid) if (VG_(clo_sanity_level >= 3)) VG_(am_do_sync_check)("(BEFORE SYSCALL)",__FILE__,__LINE__); - SCHEDSETJMP(tid, jumped, VG_(client_syscall)(tid)); + SCHEDSETJMP(tid, jumped, VG_(client_syscall)(tid, trc)); if (VG_(clo_sanity_level >= 3)) VG_(am_do_sync_check)("(AFTER SYSCALL)",__FILE__,__LINE__); @@ -1013,8 +1078,10 @@ VgSchedReturnCode VG_(scheduler) ( ThreadId tid ) break; case VEX_TRC_JMP_SYS_INT128: /* x86-linux */ - case VEX_TRC_JMP_SYS_SYSCALL: /* amd64-linux, ppc32-linux */ - handle_syscall(tid); + case VEX_TRC_JMP_SYS_INT129: /* x86-darwin */ + case VEX_TRC_JMP_SYS_INT130: /* x86-darwin */ + case VEX_TRC_JMP_SYS_SYSCALL: /* amd64-linux, ppc32-linux, amd64-darwin */ + handle_syscall(tid, trc); if (VG_(clo_sanity_level) > 2) VG_(sanity_check_general)(True); /* sanity-check every syscall */ break; @@ -1154,10 +1221,16 @@ VgSchedReturnCode VG_(scheduler) ( ThreadId tid ) # if defined(VGP_x86_linux) vg_assert2(0, "VG_(scheduler), phase 3: " "sysenter_x86 on x86-linux is not supported"); +# elif defined(VGP_x86_darwin) + /* return address in client edx */ + VG_(threads)[tid].arch.vex.guest_EIP + = VG_(threads)[tid].arch.vex.guest_EDX; + handle_syscall(tid, trc); # else vg_assert2(0, "VG_(scheduler), phase 3: " "sysenter_x86 on non-x86 platform?!?!"); # endif + break; default: vg_assert2(0, "VG_(scheduler), phase 3: " @@ -1466,12 +1539,15 @@ void scheduler_sanity ( ThreadId tid ) bad = True; } +#if !defined(VGO_darwin) + // GrP fixme if (lwpid != the_BigLock.owner_lwpid) { VG_(message)(Vg_DebugMsg, "Thread (LWPID) %d doesn't own the_BigLock\n", tid); bad = True; } +#endif /* Periodically show the state of all threads, for debugging purposes. */ diff --git a/coregrind/m_scheduler/sema.c b/coregrind/m_scheduler/sema.c index 22b46765a..ed84505a0 100644 --- a/coregrind/m_scheduler/sema.c +++ b/coregrind/m_scheduler/sema.c @@ -87,7 +87,7 @@ void ML_(sema_deinit)(vg_sema_t *sema) } /* get a token */ -void ML_(sema_down)(vg_sema_t *sema) +void ML_(sema_down)( vg_sema_t *sema, Bool as_LL ) { Char buf[2]; Int ret; @@ -114,13 +114,15 @@ void ML_(sema_down)(vg_sema_t *sema) if (sema_char == 'Z') sema_char = 'A'; else sema_char++; sema->owner_lwpid = lwpid; + sema->held_as_LL = as_LL; } /* put token back */ -void ML_(sema_up)(vg_sema_t *sema) +void ML_(sema_up)( vg_sema_t *sema, Bool as_LL ) { Int ret; Char buf[2]; + vg_assert(as_LL == sema->held_as_LL); buf[0] = sema_char; buf[1] = 0; vg_assert(sema->owner_lwpid != -1); /* must be initialised */ diff --git a/coregrind/m_sigframe/sigframe-amd64-darwin.c b/coregrind/m_sigframe/sigframe-amd64-darwin.c new file mode 100644 index 000000000..4c8bc926a --- /dev/null +++ b/coregrind/m_sigframe/sigframe-amd64-darwin.c @@ -0,0 +1,68 @@ + +/*--------------------------------------------------------------------*/ +/*--- Create/destroy signal delivery frames. ---*/ +/*--- sigframe-amd64-darwin.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2006-2009 OpenWorks Ltd + info@open-works.co.uk + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics.h" +#include "pub_core_vki.h" +#include "pub_core_vkiscnums.h" +#include "pub_core_threadstate.h" +#include "pub_core_aspacemgr.h" +#include "pub_core_libcbase.h" +#include "pub_core_libcassert.h" +#include "pub_core_libcprint.h" +#include "pub_core_machine.h" +#include "pub_core_options.h" +#include "pub_core_signals.h" +#include "pub_core_tooliface.h" +#include "pub_core_trampoline.h" +#include "pub_core_sigframe.h" /* self */ + + +void VG_(sigframe_create) ( ThreadId tid, + Addr sp_top_of_frame, + const vki_siginfo_t *siginfo, + const struct vki_ucontext *siguc, + void *handler, + UInt flags, + const vki_sigset_t *mask, + void *restorer ) +{ + I_die_here; +} + + +void VG_(sigframe_destroy)( ThreadId tid, Bool isRT ) +{ + I_die_here; +} + +/*--------------------------------------------------------------------*/ +/*--- end sigframe-amd64-darwin.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_sigframe/sigframe-x86-darwin.c b/coregrind/m_sigframe/sigframe-x86-darwin.c new file mode 100644 index 000000000..698d94079 --- /dev/null +++ b/coregrind/m_sigframe/sigframe-x86-darwin.c @@ -0,0 +1,232 @@ + +/*--------------------------------------------------------------------*/ +/*--- Create/destroy signal delivery frames. ---*/ +/*--- sigframe-x86-darwin.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2006-2009 OpenWorks Ltd + info@open-works.co.uk + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics.h" +#include "pub_core_vki.h" +#include "pub_core_vkiscnums.h" +#include "pub_core_threadstate.h" +#include "pub_core_aspacemgr.h" +#include "pub_core_libcbase.h" +#include "pub_core_libcassert.h" +#include "pub_core_libcprint.h" +#include "pub_core_machine.h" +#include "pub_core_options.h" +#include "pub_core_signals.h" +#include "pub_core_tooliface.h" +#include "pub_core_trampoline.h" +#include "pub_core_sigframe.h" /* self */ + + +/* Cheap-ass hack copied from ppc32-aix5 code, just to get started. + Produce a frame with layout entirely of our own choosing. */ + +/* This module creates and removes signal frames for signal deliveries + on x86-darwin. Kludgey; the machine state ought to be saved in a + ucontext and retrieved from it later, so the handler can modify it + and return. However .. for now .. just stick the vex guest state + in the frame and snarf it again later. + + Also, don't bother with creating siginfo and ucontext in the + handler, although do point them somewhere non-faulting. + + Frame should have a 16-aligned size, just in case that turns out to + be important for Darwin. (be conservative) +*/ +struct hacky_sigframe { + /* first four words look like a call to a 3-arg x86 function */ + UInt returnAddr; + UInt a1_signo; + UInt a2_siginfo; + UInt a3_ucontext; + UChar lower_guardzone[512]; // put nothing here + VexGuestX86State gst; + VexGuestX86State gshadow1; + VexGuestX86State gshadow2; + vki_siginfo_t fake_siginfo; + struct vki_ucontext fake_ucontext; + UInt magicPI; + UInt sigNo_private; + vki_sigset_t mask; // saved sigmask; restore when hdlr returns + UInt __pad[1]; + UChar upper_guardzone[512]; // put nothing here +}; + + +/* Extend the stack segment downwards if needed so as to ensure the + new signal frames are mapped to something. Return a Bool + indicating whether or not the operation was successful. +*/ +static Bool extend ( ThreadState *tst, Addr addr, SizeT size ) +{ + ThreadId tid = tst->tid; + /* For tracking memory events, indicate the entire frame has been + allocated. Except, don't mess with the area which + overlaps the previous frame's redzone. */ + /* XXX is the following call really right? compared with the + amd64-linux version, this doesn't appear to handle the redzone + in the same way. */ + VG_TRACK( new_mem_stack_signal, + addr, size - VG_STACK_REDZONE_SZB, tid ); + return True; +} + + +/* Create a signal frame for thread 'tid'. Make a 3-arg frame + regardless of whether the client originally requested a 1-arg + version (no SA_SIGINFO) or a 3-arg one (SA_SIGINFO) since in the + former case, the x86 calling conventions will simply cause the + extra 2 args to be ignored (inside the handler). */ +void VG_(sigframe_create) ( ThreadId tid, + Addr sp_top_of_frame, + const vki_siginfo_t *siginfo, + const struct vki_ucontext *siguc, + void *handler, + UInt flags, + const vki_sigset_t *mask, + void *restorer ) +{ + ThreadState* tst; + Addr esp; + struct hacky_sigframe* frame; + Int sigNo = siginfo->si_signo; + + vg_assert(VG_IS_16_ALIGNED(sizeof(struct hacky_sigframe))); + + sp_top_of_frame &= ~0xf; + esp = sp_top_of_frame - sizeof(struct hacky_sigframe); + + tst = VG_(get_ThreadState)(tid); + if (!extend(tst, esp, sp_top_of_frame - esp)) + return; + + vg_assert(VG_IS_16_ALIGNED(esp)); + + frame = (struct hacky_sigframe *) esp; + + /* clear it (very conservatively) (why so conservatively??) */ + VG_(memset)(&frame->lower_guardzone, 0, 512); + VG_(memset)(&frame->gst, 0, sizeof(VexGuestX86State)); + VG_(memset)(&frame->gshadow1, 0, sizeof(VexGuestX86State)); + VG_(memset)(&frame->gshadow2, 0, sizeof(VexGuestX86State)); + VG_(memset)(&frame->fake_siginfo, 0, sizeof(frame->fake_siginfo)); + VG_(memset)(&frame->fake_ucontext, 0, sizeof(frame->fake_ucontext)); + + /* save stuff in frame */ + frame->gst = tst->arch.vex; + frame->gshadow1 = tst->arch.vex_shadow1; + frame->gshadow2 = tst->arch.vex_shadow2; + frame->sigNo_private = sigNo; + frame->mask = tst->sig_mask; + frame->magicPI = 0x31415927; + + /* Minimally fill in the siginfo and ucontext. Note, utter + lameness prevails. Be underwhelmed, be very underwhelmed. */ + frame->fake_siginfo.si_signo = sigNo; + frame->fake_siginfo.si_code = siginfo->si_code; + + /* Set up stack pointer */ + vg_assert(esp == (Addr)&frame->returnAddr); + VG_(set_SP)(tid, esp); + VG_TRACK( post_reg_write, Vg_CoreSignal, tid, VG_O_STACK_PTR, sizeof(UInt)); + + /* Set up program counter */ + VG_(set_IP)(tid, (UInt)handler); + VG_TRACK( post_reg_write, Vg_CoreSignal, tid, VG_O_INSTR_PTR, sizeof(UInt)); + + /* Set up RA and args for the frame */ + VG_TRACK( pre_mem_write, Vg_CoreSignal, tid, "signal handler frame", + (Addr)frame, 4*sizeof(UInt) ); + frame->returnAddr = (UInt)&VG_(x86_darwin_SUBST_FOR_sigreturn); + frame->a1_signo = sigNo; + frame->a2_siginfo = (UInt)&frame->fake_siginfo; /* oh well */ + frame->a3_ucontext = (UInt)&frame->fake_ucontext; /* oh well */ + VG_TRACK( post_mem_write, Vg_CoreSignal, tid, + (Addr)frame, 4*sizeof(UInt) ); + VG_TRACK( post_mem_write, Vg_CoreSignal, tid, + (Addr)&frame->fake_siginfo, sizeof(frame->fake_siginfo)); + VG_TRACK( post_mem_write, Vg_CoreSignal, tid, + (Addr)&frame->fake_ucontext, sizeof(frame->fake_ucontext)); + + if (VG_(clo_trace_signals)) + VG_(message)(Vg_DebugMsg, + "sigframe_create (thread %d): next EIP=%#lx, next ESP=%#lx", + tid, (Addr)handler, (Addr)frame ); +} + + +/* Remove a signal frame from thread 'tid's stack, and restore the CPU + state from it. Note, isRT is irrelevant here. */ +void VG_(sigframe_destroy)( ThreadId tid, Bool isRT ) +{ + ThreadState *tst; + Addr esp; + Int sigNo; + struct hacky_sigframe* frame; + + vg_assert(VG_(is_valid_tid)(tid)); + tst = VG_(get_ThreadState)(tid); + + /* Check that the stack frame looks valid */ + esp = VG_(get_SP)(tid); + + /* why -4 ? because the signal handler's return will have popped + the return address of the stack; and the return address is the + lowest-addressed element of hacky_sigframe. */ + frame = (struct hacky_sigframe*)(esp - 4); + vg_assert(frame->magicPI == 0x31415927); + vg_assert(VG_IS_16_ALIGNED(frame)); + + /* restore the entire guest state, and shadows, from the + frame. Note, as per comments above, this is a kludge - should + restore it from saved ucontext. Oh well. */ + tst->arch.vex = frame->gst; + tst->arch.vex_shadow1 = frame->gshadow1; + tst->arch.vex_shadow2 = frame->gshadow2; + tst->sig_mask = frame->mask; + tst->tmp_sig_mask = frame->mask; + sigNo = frame->sigNo_private; + + if (VG_(clo_trace_signals)) + VG_(message)(Vg_DebugMsg, + "sigframe_destroy (thread %d): valid magic; next EIP=%#x", + tid, tst->arch.vex.guest_EIP); + + VG_TRACK( die_mem_stack_signal, + (Addr)frame, + sizeof(struct hacky_sigframe) - VG_STACK_REDZONE_SZB ); + + /* tell the tools */ + VG_TRACK( post_deliver_signal, tid, sigNo ); +} + +/*--------------------------------------------------------------------*/ +/*--- end sigframe-x86-darwin.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_signals.c b/coregrind/m_signals.c index c5b4b10db..43c9aecf9 100644 --- a/coregrind/m_signals.c +++ b/coregrind/m_signals.c @@ -423,6 +423,82 @@ typedef struct SigQueue { return VG_UCONTEXT_STACK_PTR(ucV); } +#elif defined(VGP_x86_darwin) + + static inline Addr VG_UCONTEXT_INSTR_PTR( void* ucV ) { + ucontext_t* uc = (ucontext_t*)ucV; + struct __darwin_mcontext32* mc = uc->uc_mcontext; + struct __darwin_i386_thread_state* ss = &mc->__ss; + return ss->__eip; + } + static inline Addr VG_UCONTEXT_STACK_PTR( void* ucV ) { + ucontext_t* uc = (ucontext_t*)ucV; + struct __darwin_mcontext32* mc = uc->uc_mcontext; + struct __darwin_i386_thread_state* ss = &mc->__ss; + return ss->__esp; + } + static inline SysRes VG_UCONTEXT_SYSCALL_SYSRES( void* ucV, + UWord scclass ) { + /* this is complicated by the problem that there are 3 different + kinds of syscalls, each with its own return convention. + NB: scclass is a host word, hence UWord is good for both + amd64-darwin and x86-darwin */ + ucontext_t* uc = (ucontext_t*)ucV; + struct __darwin_mcontext32* mc = uc->uc_mcontext; + struct __darwin_i386_thread_state* ss = &mc->__ss; + /* duplicates logic in m_syswrap.getSyscallStatusFromGuestState */ + UInt carry = 1 & ss->__eflags; + UInt err = 0; + UInt wLO = 0; + UInt wHI = 0; + switch (scclass) { + case VG_DARWIN_SYSCALL_CLASS_UNIX: + err = carry; + wLO = ss->__eax; + wHI = ss->__edx; + break; + case VG_DARWIN_SYSCALL_CLASS_MACH: + wLO = ss->__eax; + break; + case VG_DARWIN_SYSCALL_CLASS_MDEP: + wLO = ss->__eax; + break; + default: + vg_assert(0); + break; + } + return VG_(mk_SysRes_x86_darwin)( scclass, err ? True : False, + wHI, wLO ); + } + static inline Addr VG_UCONTEXT_LINK_REG( void* ucV ) { + return 0; /* No, really. We have no LRs today. */ + } + static inline Addr VG_UCONTEXT_FRAME_PTR( void* ucV ) { + ucontext_t* uc = (ucontext_t*)ucV; + struct __darwin_mcontext32* mc = uc->uc_mcontext; + struct __darwin_i386_thread_state* ss = &mc->__ss; + return ss->__ebp; + } + +#elif defined(VGP_amd64_darwin) + + static inline Addr VG_UCONTEXT_INSTR_PTR( void* ucV ) { + I_die_here; + } + static inline Addr VG_UCONTEXT_STACK_PTR( void* ucV ) { + I_die_here; + } + static inline SysRes VG_UCONTEXT_SYSCALL_SYSRES( void* ucV, + UWord scclass ) { + I_die_here; + } + static inline Addr VG_UCONTEXT_LINK_REG( void* ucV ) { + return 0; /* No, really. We have no LRs today. */ + } + static inline Addr VG_UCONTEXT_FRAME_PTR( void* ucV ) { + I_die_here; + } + #else # error Unknown platform #endif @@ -438,6 +514,9 @@ typedef struct SigQueue { #elif defined(VGO_aix5) # define VKI_SIGINFO_si_addr si_addr # define VKI_SIGINFO_si_pid si_pid +#elif defined(VGO_darwin) +# define VKI_SIGINFO_si_addr si_addr +# define VKI_SIGINFO_si_pid si_pid #else # error Unknown OS #endif @@ -722,6 +801,18 @@ extern void my_sigreturn(void); ".globl my_sigreturn\n" \ "my_sigreturn:\n" \ ".long 0\n" +#elif defined(VGP_x86_darwin) +# define _MY_SIGRETURN(name) \ + ".text\n" \ + "my_sigreturn:\n" \ + "movl $" VG_STRINGIFY(__NR_DARWIN_FAKE_SIGRETURN) ",%eax\n" \ + "int $0x80" +#elif defined(VGP_amd64_darwin) + // DDD: todo +# define _MY_SIGRETURN(name) \ + ".text\n" \ + "my_sigreturn:\n" \ + "ud2\n" #else # error Unknown platform #endif @@ -763,8 +854,9 @@ static void handle_SCSS_change ( Bool force_update ) ksa.ksa_handler = skss.skss_per_sig[sig].skss_handler; ksa.sa_flags = skss.skss_per_sig[sig].skss_flags; -# if !defined(VGP_ppc32_linux) && !defined(VGP_ppc32_aix5) \ - && !defined(VGP_ppc64_aix5) +# if !defined(VGP_ppc32_linux) && \ + !defined(VGP_ppc32_aix5) && !defined(VGP_ppc64_aix5) && \ + !defined(VGP_x86_darwin) && !defined(VGP_amd64_darwin) ksa.sa_restorer = my_sigreturn; # endif /* Re above ifdef (also the assertion below), PaulM says: @@ -784,8 +876,7 @@ static void handle_SCSS_change ( Bool force_update ) sig, ksa.ksa_handler, (UWord)ksa.sa_flags, _VKI_NSIG_WORDS > 1 ? (ULong)ksa.sa_mask.sig[1] : 0, - (ULong)ksa.sa_mask.sig[0] - ); + (ULong)ksa.sa_mask.sig[0]); res = VG_(sigaction)( sig, &ksa, &ksa_old ); vg_assert(res == 0); @@ -797,8 +888,9 @@ static void handle_SCSS_change ( Bool force_update ) == skss_old.skss_per_sig[sig].skss_handler); vg_assert(ksa_old.sa_flags == skss_old.skss_per_sig[sig].skss_flags); -# if !defined(VGP_ppc32_linux) && !defined(VGP_ppc32_aix5) \ - && !defined(VGP_ppc64_aix5) +# if !defined(VGP_ppc32_linux) && \ + !defined(VGP_ppc32_aix5) && !defined(VGP_ppc64_aix5) && \ + !defined(VGP_x86_darwin) && !defined(VGP_amd64_darwin) vg_assert(ksa_old.sa_restorer == my_sigreturn); # endif @@ -919,7 +1011,8 @@ SysRes VG_(do_sys_sigaction) ( Int signo, old_act->ksa_handler = scss.scss_per_sig[signo].scss_handler; old_act->sa_flags = scss.scss_per_sig[signo].scss_flags; old_act->sa_mask = scss.scss_per_sig[signo].scss_mask; -# if !defined(VGP_ppc32_aix5) && !defined(VGP_ppc64_aix5) +# if !defined(VGP_ppc32_aix5) && !defined(VGP_ppc64_aix5) && \ + !defined(VGP_x86_darwin) && !defined(VGP_amd64_darwin) old_act->sa_restorer = scss.scss_per_sig[signo].scss_restorer; # endif } @@ -931,11 +1024,15 @@ SysRes VG_(do_sys_sigaction) ( Int signo, scss.scss_per_sig[signo].scss_mask = new_act->sa_mask; scss.scss_per_sig[signo].scss_restorer = NULL; -# if !defined(VGP_ppc32_aix5) && !defined(VGP_ppc64_aix5) +# if !defined(VGP_ppc32_aix5) && !defined(VGP_ppc64_aix5) && \ + !defined(VGP_x86_darwin) && !defined(VGP_amd64_darwin) scss.scss_per_sig[signo].scss_restorer = new_act->sa_restorer; # endif scss.scss_per_sig[signo].scss_sa_tramp = NULL; +# if defined(VGP_x86_darwin) || defined(VGP_amd64_darwin) + scss.scss_per_sig[signo].scss_sa_tramp = new_act->sa_tramp; +# endif VG_(sigdelset)(&scss.scss_per_sig[signo].scss_mask, VKI_SIGKILL); VG_(sigdelset)(&scss.scss_per_sig[signo].scss_mask, VKI_SIGSTOP); @@ -1234,13 +1331,15 @@ static const Char *signame(Int sigNo) /* Hit ourselves with a signal using the default handler */ void VG_(kill_self)(Int sigNo) { + Int r; vki_sigset_t mask, origmask; vki_sigaction_toK_t sa, origsa2; vki_sigaction_fromK_t origsa; sa.ksa_handler = VKI_SIG_DFL; sa.sa_flags = 0; -# if !defined(VGP_ppc32_aix5) && !defined(VGP_ppc64_aix5) +# if !defined(VGP_ppc32_aix5) && !defined(VGP_ppc64_aix5) && \ + !defined(VGP_x86_darwin) && !defined(VGP_amd64_darwin) sa.sa_restorer = 0; # endif VG_(sigemptyset)(&sa.sa_mask); @@ -1251,7 +1350,9 @@ void VG_(kill_self)(Int sigNo) VG_(sigaddset)(&mask, sigNo); VG_(sigprocmask)(VKI_SIG_UNBLOCK, &mask, &origmask); - VG_(kill)(VG_(getpid)(), sigNo); + r = VG_(kill)(VG_(getpid)(), sigNo); + /* This sometimes fails with EPERM on Darwin. I don't know why. */ + /* vg_assert(r == 0); */ VG_(convert_sigaction_fromK_to_toK)( &origsa, &origsa2 ); VG_(sigaction)(sigNo, &origsa2, NULL); @@ -1262,8 +1363,9 @@ void VG_(kill_self)(Int sigNo) // kernel, eg.: seg faults, illegal opcodes. Some come from the user, eg.: // from kill() (SI_USER), or timer_settime() (SI_TIMER), or an async I/O // request (SI_ASYNCIO). There's lots of implementation-defined leeway in -// POSIX, but the user vs. kernal distinction is what we want here. -static Bool is_signal_from_kernel(int si_code) +// POSIX, but the user vs. kernal distinction is what we want here. We also +// pass in some other details that can help when si_code is unreliable. +static Bool is_signal_from_kernel(ThreadId tid, int signum, int si_code) { #if defined(VGO_linux) || defined(VGO_aix5) // On Linux, SI_USER is zero, negative values are from the user, positive @@ -1271,6 +1373,35 @@ static Bool is_signal_from_kernel(int si_code) // macros but we don't use them here because other platforms don't have // them. return ( si_code > VKI_SI_USER ? True : False ); +#elif defined(VGO_darwin) + // On Darwin 9.6.0, the si_code is completely unreliable. It should be the + // case that 0 means "user", and >0 means "kernel". But: + // - For SIGSEGV, it seems quite reliable. + // - For SIGBUS, it's always 2. + // - For SIGFPE, it's often 0, even for kernel ones (eg. + // div-by-integer-zero always gives zero). + // - For SIGILL, it's unclear. + // - For SIGTRAP, it's always 1. + // You can see the "NOTIMP" (not implemented) status of a number of the + // sub-cases in sys/signal.h. Hopefully future versions of Darwin will + // get this right. + + // If we're blocked waiting on a syscall, it must be a user signal, because + // the kernel won't generate sync signals within syscalls. + if (VG_(threads)[tid].status == VgTs_WaitSys) { + return False; + + // If it's a SIGSEGV, use the proper condition, since it's fairly reliable. + } else if (SIGSEGV == signum) { + return ( si_code > 0 ? True : False ); + + // If it's anything else, assume it's kernel-generated. Reason being that + // kernel-generated sync signals are more common, and it's probable that + // misdiagnosing a user signal as a kernel signal is better than the + // opposite. + } else { + return True; + } #else # error Unknown OS #endif @@ -1358,7 +1489,7 @@ static void default_action(const vki_siginfo_t *info, ThreadId tid) } if ( (VG_(clo_verbosity) > 1 || - (could_core && is_signal_from_kernel(info->si_code)) + (could_core && is_signal_from_kernel(tid, sigNo, info->si_code)) ) && !VG_(clo_xml) ) { VG_UMSG(""); @@ -1366,7 +1497,7 @@ static void default_action(const vki_siginfo_t *info, ThreadId tid) sigNo, signame(sigNo), core ? ": dumping core" : ""); /* Be helpful - decode some more details about this fault */ - if (is_signal_from_kernel(info->si_code)) { + if (is_signal_from_kernel(tid, sigNo, info->si_code)) { const Char *event = NULL; Bool haveaddr = True; @@ -1448,7 +1579,7 @@ static void default_action(const vki_siginfo_t *info, ThreadId tid) VG_(pp_ExeContext)( ec ); } if (sigNo == VKI_SIGSEGV - && info && is_signal_from_kernel(info->si_code) + && info && is_signal_from_kernel(tid, sigNo, info->si_code) && info->si_code == VKI_SEGV_MAPERR) { VG_UMSG(" If you believe this happened as a result of a stack" ); VG_UMSG(" overflow in your program's main thread (unlikely but"); @@ -1640,6 +1771,11 @@ void VG_(synth_sigtrap)(ThreadId tid) { vki_siginfo_t info; struct vki_ucontext uc; +# if defined(VGP_x86_darwin) + struct __darwin_mcontext32 mc; +# elif defined(VGP_amd64_darwin) + struct __darwin_mcontext64 mc; +# endif vg_assert(VG_(threads)[tid].status == VgTs_Runnable); @@ -1653,6 +1789,12 @@ void VG_(synth_sigtrap)(ThreadId tid) for a breakpoint trap... */ uc.uc_mcontext.err = 0; /* tjh: no error code for x86 breakpoint trap... */ +# elif defined(VGP_x86_darwin) || defined(VGP_amd64_darwin) + /* the same thing, but using Darwin field/struct names */ + VG_(memset)(&mc, 0, sizeof(mc)); + uc.uc_mcontext = &mc; + uc.uc_mcontext->__es.__trapno = 3; + uc.uc_mcontext->__es.__err = 0; # endif resume_scheduler(tid); @@ -1756,7 +1898,7 @@ static int sanitize_si_code(int si_code) mask them off) sign extends them when exporting to user space so we do the same thing here. */ return (Short)si_code; -#elif defined(VGO_aix5) +#elif defined(VGO_aix5) || defined(VGO_darwin) return si_code; #else # error Unknown OS @@ -1812,7 +1954,16 @@ void async_signalhandler ( Int sigNo, VG_(fixup_guest_state_after_syscall_interrupted) will detect that the thread was not in said window and ignore the SysRes. */ + /* To make matters more complex still, on Darwin we need to know + the "class" of the syscall under consideration in order to be + able to extract the a correct SysRes. The class will have been + saved just before the syscall, by VG_(client_syscall), into this + thread's tst->arch.vex.guest_SC_CLASS. Hence: */ +# if defined(VGO_darwin) + sres = VG_UCONTEXT_SYSCALL_SYSRES(uc, tst->arch.vex.guest_SC_CLASS); +# else sres = VG_UCONTEXT_SYSCALL_SYSRES(uc); +# endif /* (1) */ VG_(fixup_guest_state_after_syscall_interrupted)( @@ -2050,7 +2201,6 @@ static Bool extend_stack_if_appropriate(ThreadId tid, vki_siginfo_t* info) } } - static void sync_signalhandler_from_kernel ( ThreadId tid, Int sigNo, vki_siginfo_t *info, struct vki_ucontext *uc ) @@ -2140,7 +2290,7 @@ void sync_signalhandler ( Int sigNo, info->si_code = sanitize_si_code(info->si_code); - from_user = !is_signal_from_kernel(info->si_code); + from_user = !is_signal_from_kernel(tid, sigNo, info->si_code); if (VG_(clo_trace_signals)) { VG_DMSG("sync signal handler: " @@ -2220,7 +2370,8 @@ void pp_ksigaction ( vki_sigaction_toK_t* sa ) VG_(printf)("pp_ksigaction: handler %p, flags 0x%x, restorer %p\n", sa->ksa_handler, (UInt)sa->sa_flags, -# if !defined(VGP_ppc32_aix5) && !defined(VGP_ppc64_aix5) +# if !defined(VGP_ppc32_aix5) && !defined(VGP_ppc64_aix5) && \ + !defined(VGP_x86_darwin) && !defined(VGP_amd64_darwin) sa->sa_restorer # else (void*)0 @@ -2242,7 +2393,8 @@ void VG_(set_default_handler)(Int signo) sa.ksa_handler = VKI_SIG_DFL; sa.sa_flags = 0; -# if !defined(VGP_ppc32_aix5) && !defined(VGP_ppc64_aix5) +# if !defined(VGP_ppc32_aix5) && !defined(VGP_ppc64_aix5) && \ + !defined(VGP_x86_darwin) && !defined(VGP_amd64_darwin) sa.sa_restorer = 0; # endif VG_(sigemptyset)(&sa.sa_mask); @@ -2326,6 +2478,13 @@ void VG_(sigstartup_actions) ( void ) /* Get the old host action */ ret = VG_(sigaction)(i, NULL, &sa); +# if defined(VGP_x86_darwin) + /* apparently we may not even ask about the disposition of these + signals, let alone change them */ + if (ret != 0 && (i == VKI_SIGKILL || i == VKI_SIGSTOP)) + continue; +# endif + if (ret != 0) break; @@ -2337,7 +2496,8 @@ void VG_(sigstartup_actions) ( void ) tsa.ksa_handler = (void *)sync_signalhandler; tsa.sa_flags = VKI_SA_SIGINFO; -# if !defined(VGP_ppc32_aix5) && !defined(VGP_ppc64_aix5) +# if !defined(VGP_ppc32_aix5) && !defined(VGP_ppc64_aix5) && \ + !defined(VGP_x86_darwin) && !defined(VGP_amd64_darwin) tsa.sa_restorer = 0; # endif VG_(sigfillset)(&tsa.sa_mask); @@ -2364,11 +2524,18 @@ void VG_(sigstartup_actions) ( void ) scss.scss_per_sig[i].scss_mask = sa.sa_mask; scss.scss_per_sig[i].scss_restorer = NULL; -# if !defined(VGP_ppc32_aix5) && !defined(VGP_ppc64_aix5) +# if !defined(VGP_ppc32_aix5) && !defined(VGP_ppc64_aix5) && \ + !defined(VGP_x86_darwin) && !defined(VGP_amd64_darwin) scss.scss_per_sig[i].scss_restorer = sa.sa_restorer; # endif scss.scss_per_sig[i].scss_sa_tramp = NULL; +# if defined(VGP_x86_darwin) || defined(VGP_amd64_darwin) + scss.scss_per_sig[i].scss_sa_tramp = NULL; + /*sa.sa_tramp;*/ + /* We can't know what it was, because Darwin's sys_sigaction + doesn't tell us. */ +# endif } if (VG_(clo_trace_signals)) diff --git a/coregrind/m_stacktrace.c b/coregrind/m_stacktrace.c index edfaf731a..6e3c8d07e 100644 --- a/coregrind/m_stacktrace.c +++ b/coregrind/m_stacktrace.c @@ -101,6 +101,9 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known, /* Assertion broken before main() is reached in pthreaded programs; the * offending stack traces only have one item. --njn, 2002-aug-16 */ /* vg_assert(fp_min <= fp_max);*/ + // On Darwin, this kicks in for pthread-related stack traces, so they're + // only 1 entry long which is wrong. +#if !defined(VGO_darwin) if (fp_min + 512 >= fp_max) { /* If the stack limits look bogus, don't poke around ... but don't bomb out either. */ @@ -109,13 +112,14 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known, ips[0] = ip; return 1; } +#endif /* Otherwise unwind the stack in a platform-specific way. Trying to merge the x86, amd64, ppc32 and ppc64 logic into a single piece of code is just too confusing and difficult to performance-tune. */ -# if defined(VGP_x86_linux) +# if defined(VGP_x86_linux) || defined(VGP_x86_darwin) /*--------------------- x86 ---------------------*/ @@ -209,7 +213,7 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known, break; } -# elif defined(VGP_amd64_linux) +# elif defined(VGP_amd64_linux) || defined(VGP_amd64_darwin) /*--------------------- amd64 ---------------------*/ @@ -551,7 +555,6 @@ void VG_(get_and_pp_StackTrace) ( ThreadId tid, UInt max_n_ips ) VG_(pp_StackTrace)(ips, n_ips); } - void VG_(apply_StackTrace)( void(*action)(UInt n, Addr ip), StackTrace ips, UInt n_ips ) { diff --git a/coregrind/m_start-amd64-darwin.S b/coregrind/m_start-amd64-darwin.S new file mode 100644 index 000000000..e414a26ac --- /dev/null +++ b/coregrind/m_start-amd64-darwin.S @@ -0,0 +1,81 @@ + +/*--------------------------------------------------------------------*/ +/*--- Darwin amd64 bootstrap. m_start-amd64-darwin.S ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2007 Apple Inc. + Greg Parker gparker@apple.com + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics_asm.h" + + .text + .align 3,0x90 +Ldyld_stub_binding_helper: + pushq %r11 + leaq ___dso_handle(%rip), %r11 + pushq %r11 + jmpq *Ldyld_lazy_symbol_binding_entry_point(%rip) + + .dyld + .align 3 +Ldyld_lazy_symbol_binding_entry_point: + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad Ldyld_stub_binding_helper + .quad 0 + + + // Memory layout established by kernel: + // + // 0 + // executable_name + // 0 + // envp[n] + // ... + // envp[0] + // 0 + // argv[argc-1] + // ... + // sp+8-> argv[0] + // sp -> argc + + .text + .align 3,0x90 + .globl __start +__start: + movq %rsp, %rdi // save &argc + andq $-16, %rsp // align stack + pushq $0 // push NULL "return address" for backtraces + pushq $0 // push fake saved ebp and align stack + movq %rsp, %rbp // save frame pointer + call __start_in_C_darwin // __start_in_C_darwin(&argc) + + // should not reach here + int $3 + int $3 + diff --git a/coregrind/m_start-x86-darwin.S b/coregrind/m_start-x86-darwin.S new file mode 100644 index 000000000..57b49d0a7 --- /dev/null +++ b/coregrind/m_start-x86-darwin.S @@ -0,0 +1,80 @@ + +/*--------------------------------------------------------------------*/ +/*--- Darwin x86 bootstrap. m_start-x86-darwin.S ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2007 Apple Inc. + Greg Parker gparker@apple.com + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics_asm.h" + + .text + .align 2,0x90 +Ldyld_stub_binding_helper: + pushl $__mh_execute_header + jmpl *Ldyld_lazy_symbol_binding_entry_point + + .dyld + .align 2 +Ldyld_lazy_symbol_binding_entry_point: + .long 0 + .long 0 + .long 0 + .long 0 + .long 0 + .long Ldyld_stub_binding_helper + .long 0 + + + // Memory layout established by kernel: + // + // 0 + // executable_name + // 0 + // envp[n] + // ... + // envp[0] + // 0 + // argv[argc-1] + // ... + // sp+4-> argv[0] + // sp -> argc + + .text + .align 2,0x90 + .globl __start +__start: + movl %esp, %eax // save &argc + andl $-16, %esp // align stack + pushl $0 // push NULL "return address" for backtraces + pushl $0 // push fake saved ebp + movl %esp, %ebp // save frame pointer + pushl $0 // align stack + pushl %eax // start_in_C_darwin(&argc) + call __start_in_C_darwin + + // should not reach here + int $3 + int $3 diff --git a/coregrind/m_syscall.c b/coregrind/m_syscall.c index 0970e6b83..3a74a89b4 100644 --- a/coregrind/m_syscall.c +++ b/coregrind/m_syscall.c @@ -29,6 +29,7 @@ */ #include "pub_core_basics.h" +#include "pub_core_libcassert.h" #include "pub_core_vki.h" #include "pub_core_vkiscnums.h" #include "pub_core_syscall.h" @@ -155,6 +156,95 @@ SysRes VG_(mk_SysRes_Success) ( UWord res ) { } +#elif defined(VGO_darwin) + +/* Darwin: Some syscalls return a double-word result. */ +SysRes VG_(mk_SysRes_x86_darwin) ( UChar scclass, Bool isErr, + UInt wHI, UInt wLO ) +{ + SysRes res; + res._wHI = 0; + res._wLO = 0; + res._mode = 0; /* invalid */ + vg_assert(isErr == False || isErr == True); + vg_assert(sizeof(UWord) == sizeof(UInt)); + switch (scclass) { + case VG_DARWIN_SYSCALL_CLASS_UNIX: + res._wLO = wLO; + res._wHI = wHI; + res._mode = isErr ? SysRes_UNIX_ERR : SysRes_UNIX_OK; + break; + case VG_DARWIN_SYSCALL_CLASS_MACH: + vg_assert(!isErr); + vg_assert(wHI == 0); + res._wLO = wLO; + res._mode = SysRes_MACH; + break; + case VG_DARWIN_SYSCALL_CLASS_MDEP: + vg_assert(!isErr); + vg_assert(wHI == 0); + res._wLO = wLO; + res._mode = SysRes_MDEP; + break; + default: + vg_assert(0); + } + return res; +} + +SysRes VG_(mk_SysRes_amd64_darwin) ( UChar scclass, Bool isErr, + ULong wHI, ULong wLO ) +{ + SysRes res; + res._wHI = 0; + res._wLO = 0; + res._mode = 0; /* invalid */ + vg_assert(isErr == False || isErr == True); + vg_assert(sizeof(UWord) == sizeof(ULong)); + switch (scclass) { + case VG_DARWIN_SYSCALL_CLASS_UNIX: + res._wLO = wLO; + res._wHI = wHI; + res._mode = isErr ? SysRes_UNIX_ERR : SysRes_UNIX_OK; + break; + case VG_DARWIN_SYSCALL_CLASS_MACH: + vg_assert(!isErr); + vg_assert(wHI == 0); + res._wLO = wLO; + res._mode = SysRes_MACH; + break; + case VG_DARWIN_SYSCALL_CLASS_MDEP: + vg_assert(!isErr); + vg_assert(wHI == 0); + res._wLO = wLO; + res._mode = SysRes_MDEP; + break; + default: + vg_assert(0); + } + return res; +} + +/* Generic constructors. We assume (without checking if this makes + any sense, from the caller's point of view) that these are for the + UNIX style of syscall. */ +SysRes VG_(mk_SysRes_Error) ( UWord err ) { + SysRes r; + r._wHI = 0; + r._wLO = err; + r._mode = SysRes_UNIX_ERR; + return r; +} + +SysRes VG_(mk_SysRes_Success) ( UWord res ) { + SysRes r; + r._wHI = 0; + r._wLO = res; + r._mode = SysRes_UNIX_OK; + return r; +} + + #else # error "Unknown OS" #endif @@ -460,6 +550,138 @@ static void do_syscall_WRK ( UWord* res_r3, UWord* res_r4, *res_r4 = args[1]; } +#elif defined(VGP_x86_darwin) + +/* Incoming args (syscall number + up to 8 args) come in on the stack + + The kernel's syscall calling convention is: + * the syscall number goes in eax + * the args are passed to the syscall on the stack, + pushed onto the stack R->L (that is, the usual x86 + calling conventions, with the leftmost arg at the lowest + address) + Call instruction: + * UNIX: sysenter + * UNIX: int $0x80 + * MACH: int $0x81 + * MDEP: int $0x82 + Note that the call type can be determined from the syscall number; + there is no need to inspect the actual instruction. Although obviously + the instruction must match. + Return value: + * MACH,MDEP: the return value comes back in eax + * UNIX: the return value comes back in edx:eax (hi32:lo32) + Error: + * MACH,MDEP: no error is returned + * UNIX: the carry flag indicates success or failure + + nb here, sizeof(UWord) == sizeof(UInt) +*/ + +__private_extern__ ULong +do_syscall_unix_WRK ( UWord a1, UWord a2, UWord a3, /* 4(esp)..12(esp) */ + UWord a4, UWord a5, UWord a6, /* 16(esp)..24(esp) */ + UWord a7, UWord a8, /* 28(esp)..32(esp) */ + UWord syscall_no, /* 36(esp) */ + /*OUT*/UInt* errflag /* 40(esp) */ ); +// Unix syscall: 64-bit return in edx:eax, with LSB in eax +// error indicated by carry flag: clear=good, set=bad +asm(".private_extern _do_syscall_unix_WRK\n" + "_do_syscall_unix_WRK:\n" + " movl 40(%esp), %ecx \n" /* assume syscall success */ + " movl $0, (%ecx) \n" + " movl 36(%esp), %eax \n" + " int $0x80 \n" + " jnc 1f \n" /* jump if success */ + " movl 40(%esp), %ecx \n" /* syscall failed - set *errflag */ + " movl $1, (%ecx) \n" + " 1: ret \n" + ); + +__private_extern__ UInt +do_syscall_mach_WRK ( UWord a1, UWord a2, UWord a3, /* 4(esp)..12(esp) */ + UWord a4, UWord a5, UWord a6, /* 16(esp)..24(esp) */ + UWord a7, UWord a8, /* 28(esp)..32(esp) */ + UWord syscall_no /* 36(esp) */ ); +// Mach trap: 32-bit result in %eax, no error flag +asm(".private_extern _do_syscall_mach_WRK\n" + "_do_syscall_mach_WRK:\n" + " movl 36(%esp), %eax \n" + " int $0x81 \n" + " ret \n" + ); + +__private_extern__ UInt +do_syscall_mdep_WRK ( UWord a1, UWord a2, UWord a3, /* 4(esp)..12(esp) */ + UWord a4, UWord a5, UWord a6, /* 16(esp)..24(esp) */ + UWord a7, UWord a8, /* 28(esp)..32(esp) */ + UWord syscall_no /* 36(esp) */ ); +// mdep trap: 32-bit result in %eax, no error flag +asm( + ".private_extern _do_syscall_mdep_WRK\n" + "_do_syscall_mdep_WRK:\n" + " movl 36(%esp), %eax \n" + " int $0x82 \n" + " ret \n" + ); + + +#elif defined(VGP_amd64_darwin) + +/* Incoming args (syscall number + up to 8 args) come in registers and stack + + The kernel's syscall calling convention is: + * the syscall number goes in rax + * the args are passed to the syscall in registers and the stack + * the call instruction is 'syscall' + Return value: + * MACH,MDEP: the return value comes back in rax + * UNIX: the return value comes back in rdx:rax (hi64:lo64) + Error: + * MACH,MDEP: no error is returned + * UNIX: the carry flag indicates success or failure + + nb here, sizeof(UWord) == sizeof(ULong) +*/ + +__private_extern__ UWord +do_syscall_unix_WRK ( UWord a1, UWord a2, UWord a3, /* rdi, rsi, rdx */ + UWord a4, UWord a5, UWord a6, /* rcx, r8, r9 */ + UWord a7, UWord a8, /* 8(rsp), 16(rsp) */ + UWord syscall_no, /* 24(rsp) */ + /*OUT*/ULong* errflag, /* 32(rsp) */ + /*OUT*/ULong* res2 ); /* 40(rsp) */ +// Unix syscall: 128-bit return in rax:rdx, with LSB in rax +// error indicated by carry flag: clear=good, set=bad +asm(".private_extern _do_syscall_unix_WRK\n" + "_do_syscall_unix_WRK:\n" + " movq %rcx, %r10 \n" /* pass rcx in r10 instead */ + " movq 32(%rsp), %rax \n" /* assume syscall success */ + " movq $0, (%rax) \n" + " movq 24(%rsp), %rax \n" /* load syscall_no */ + " syscall \n" + " jnc 1f \n" /* jump if success */ + " movq 32(%rsp), %rcx \n" /* syscall failed - set *errflag */ + " movq $1, (%rcx) \n" + " 1: movq 40(%rsp), %rcx \n" /* save 2nd result word */ + " movq %rdx, (%rcx) \n" + " retq \n" /* return 1st result word */ + ); + +__private_extern__ UWord +do_syscall_mach_WRK ( UWord a1, UWord a2, UWord a3, /* rdi, rsi, rdx */ + UWord a4, UWord a5, UWord a6, /* rcx, r8, r9 */ + UWord a7, UWord a8, /* 8(rsp), 16(rsp) */ + UWord syscall_no ); /* 24(rsp) */ +// Mach trap: 64-bit result, no error flag +asm(".private_extern _do_syscall_mach_WRK\n" + "_do_syscall_mach_WRK:\n" + " movq %rcx, %r10 \n" /* pass rcx in r10 instead */ + " movq 24(%rsp), %rax \n" /* load syscall_no */ + " syscall \n" + " retq \n" + ); + #else # error Unknown platform #endif @@ -536,6 +758,53 @@ SysRes VG_(do_syscall) ( UWord sysno, UWord a1, UWord a2, UWord a3, } return VG_(mk_SysRes_ppc64_aix5)( res, err ); +# elif defined(VGP_x86_darwin) + UInt wLO = 0, wHI = 0, err = 0; + ULong u64; + UChar scclass = VG_DARWIN_SYSNO_CLASS(sysno); + switch (scclass) { + case VG_DARWIN_SYSCALL_CLASS_UNIX: + u64 = do_syscall_unix_WRK(a1,a2,a3,a4,a5,a6,a7,a8, + VG_DARWIN_SYSNO_NUM(sysno), &err); + wLO = (UInt)u64; + wHI = (UInt)(u64 >> 32); + break; + case VG_DARWIN_SYSCALL_CLASS_MACH: + wLO = do_syscall_mach_WRK(a1,a2,a3,a4,a5,a6,a7,a8, + VG_DARWIN_SYSNO_NUM(sysno)); + err = 0; + break; + case VG_DARWIN_SYSCALL_CLASS_MDEP: + wLO = do_syscall_mdep_WRK(a1,a2,a3,a4,a5,a6,a7,a8, + VG_DARWIN_SYSNO_NUM(sysno)); + err = 0; + break; + default: + vg_assert(0); + break; + } + return VG_(mk_SysRes_x86_darwin)( scclass, err ? True : False, wHI, wLO ); + +# elif defined(VGP_amd64_darwin) + ULong wLO = 0, wHI = 0, err = 0; + UChar scclass = VG_DARWIN_SYSNO_CLASS(sysno); + switch (scclass) { + case VG_DARWIN_SYSCALL_CLASS_UNIX: + wLO = do_syscall_unix_WRK(a1,a2,a3,a4,a5,a6,a7,a8, + VG_DARWIN_SYSNO_NUM(sysno), &err, &wHI); + break; + case VG_DARWIN_SYSCALL_CLASS_MACH: + case VG_DARWIN_SYSCALL_CLASS_MDEP: + wLO = do_syscall_mach_WRK(a1,a2,a3,a4,a5,a6,a7,a8, + VG_DARWIN_SYSNO_NUM(sysno)); + err = 0; + break; + default: + vg_assert(0); + break; + } + return VG_(mk_SysRes_amd64_darwin)( scclass, err ? True : False, wHI, wLO ); + #else # error Unknown platform #endif diff --git a/coregrind/m_syswrap/priv_syswrap-darwin.h b/coregrind/m_syswrap/priv_syswrap-darwin.h new file mode 100644 index 000000000..bc8570625 --- /dev/null +++ b/coregrind/m_syswrap/priv_syswrap-darwin.h @@ -0,0 +1,289 @@ + +/*--------------------------------------------------------------------*/ +/*--- Private syscalls header for Darwin. priv_syswrap-darwin.h ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2005-2009 Apple Inc. + Greg Parker gparker@apple.com + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#ifndef __PRIV_SYSWRAP_DARWIN_H +#define __PRIV_SYSWRAP_DARWIN_H + +/* requires #include "priv_types_n_macros.h" */ + +// syswrap-darwin.c +Addr allocstack ( ThreadId tid ); +void find_stack_segment ( ThreadId tid, Addr sp ); +void start_thread_NORETURN ( Word arg ); +void assign_port_name(mach_port_t port, const char *name); +void record_named_port(ThreadId tid, mach_port_t port, mach_port_right_t right, const char *name); + +extern const SyscallTableEntry ML_(mach_trap_table)[]; +extern const SyscallTableEntry ML_(syscall_table)[]; +extern const SyscallTableEntry ML_(mdep_trap_table)[]; + +extern const UInt ML_(syscall_table_size); +extern const UInt ML_(mach_trap_table_size); +extern const UInt ML_(mdep_trap_table_size); + +void VG_(show_open_ports)(void); + +// Unix syscalls +DECL_TEMPLATE(darwin, sys_semget); +DECL_TEMPLATE(darwin, sys_semop); +DECL_TEMPLATE(darwin, sys_semctl); +DECL_TEMPLATE(darwin, sys_sem_open); +DECL_TEMPLATE(darwin, sys_sem_close); +DECL_TEMPLATE(darwin, sys_sem_unlink); +DECL_TEMPLATE(darwin, sys_sem_post); +DECL_TEMPLATE(darwin, sys_sem_init); +DECL_TEMPLATE(darwin, sys_sem_destroy); +DECL_TEMPLATE(darwin, sys_sem_wait_nocancel); +DECL_TEMPLATE(darwin, sys_sem_trywait); +DECL_TEMPLATE(darwin, sys_bsdthread_create); +DECL_TEMPLATE(darwin, sys_bsdthread_terminate); +DECL_TEMPLATE(darwin, sys_kqueue); +DECL_TEMPLATE(darwin, sys_kevent); +DECL_TEMPLATE(darwin, sys_bsdthread_register); +DECL_TEMPLATE(darwin, sys_workq_open); +DECL_TEMPLATE(darwin, sys_workq_ops); +DECL_TEMPLATE(darwin, sys___mac_syscall); +DECL_TEMPLATE(darwin, sys_exit); +DECL_TEMPLATE(darwin, sys_sigaction); +DECL_TEMPLATE(darwin, sys___pthread_canceled); +DECL_TEMPLATE(darwin, sys___pthread_markcancel); +DECL_TEMPLATE(darwin, sys___pthread_sigmask); +DECL_TEMPLATE(darwin, sys___disable_threadsignal); +DECL_TEMPLATE(darwin, sys_kdebug_trace); +DECL_TEMPLATE(darwin, sys_seteuid); +DECL_TEMPLATE(darwin, sys_setegid); +DECL_TEMPLATE(darwin, sys_listxattr); +DECL_TEMPLATE(darwin, sys_flistxattr); +DECL_TEMPLATE(darwin, sys_shmget); +DECL_TEMPLATE(darwin, sys_shm_open); +DECL_TEMPLATE(darwin, sys_statx); +DECL_TEMPLATE(darwin, sys_fchmod_extended); +DECL_TEMPLATE(darwin, sys_chmod_extended); +DECL_TEMPLATE(darwin, sys_accessx); +DECL_TEMPLATE(darwin, sys_chflags); +DECL_TEMPLATE(darwin, sys_fchflags); +DECL_TEMPLATE(darwin, sys_stat64); +DECL_TEMPLATE(darwin, sys_lstat64); +DECL_TEMPLATE(darwin, sys_fstat64); +DECL_TEMPLATE(darwin, sys_getfsstat); +DECL_TEMPLATE(darwin, sys_getattrlist); +DECL_TEMPLATE(darwin, sys_setattrlist); +DECL_TEMPLATE(darwin, sys_getdirentriesattr); +DECL_TEMPLATE(darwin, sys_fsctl); +DECL_TEMPLATE(darwin, sys_socket); +DECL_TEMPLATE(darwin, sys_setsockopt); +DECL_TEMPLATE(darwin, sys_getsockopt); +DECL_TEMPLATE(darwin, sys_connect); +DECL_TEMPLATE(darwin, sys_accept); +DECL_TEMPLATE(darwin, sys_sendto); +DECL_TEMPLATE(darwin, sys_recvfrom); +DECL_TEMPLATE(darwin, sys_sendmsg); +DECL_TEMPLATE(darwin, sys_recvmsg); +DECL_TEMPLATE(darwin, sys_shutdown); +DECL_TEMPLATE(darwin, sys_bind); +DECL_TEMPLATE(darwin, sys_listen); +DECL_TEMPLATE(darwin, sys_getsockname); +DECL_TEMPLATE(darwin, sys_getpeername); +DECL_TEMPLATE(darwin, sys_socketpair); +DECL_TEMPLATE(darwin, sys_gethostuuid); +DECL_TEMPLATE(darwin, sys_pipe); +DECL_TEMPLATE(darwin, sys_getlogin); +DECL_TEMPLATE(darwin, sys_ptrace); +DECL_TEMPLATE(darwin, sys_issetugid); +DECL_TEMPLATE(darwin, sys_getdtablesize); +DECL_TEMPLATE(darwin, sys_lseek); +DECL_TEMPLATE(darwin, sys_getdirentries); +DECL_TEMPLATE(darwin, sys_getdirentries64); +DECL_TEMPLATE(darwin, sys_statfs64); +DECL_TEMPLATE(darwin, sys_fstatfs64); +DECL_TEMPLATE(darwin, sys_csops); +DECL_TEMPLATE(darwin, sys_auditon); +DECL_TEMPLATE(darwin, sys_pathconf); +DECL_TEMPLATE(darwin, sys_fpathconf); +DECL_TEMPLATE(darwin, sys_shared_region_map_file_np); +DECL_TEMPLATE(darwin, sys_mmap); +DECL_TEMPLATE(darwin, sys_sysctl); +DECL_TEMPLATE(darwin, sys_sigpending); +DECL_TEMPLATE(darwin, sys_sigprocmask); +DECL_TEMPLATE(darwin, sys_sigsuspend); +DECL_TEMPLATE(darwin, sys_watchevent); +DECL_TEMPLATE(darwin, sys_waitevent); +DECL_TEMPLATE(darwin, sys_modwatch); +DECL_TEMPLATE(darwin, sys_getxattr); +DECL_TEMPLATE(darwin, sys_fgetxattr); +DECL_TEMPLATE(darwin, sys_setxattr); +DECL_TEMPLATE(darwin, sys_fsetxattr); +DECL_TEMPLATE(darwin, sys_initgroups); +DECL_TEMPLATE(darwin, sys_posix_spawn); +DECL_TEMPLATE(darwin, sys_settid); +DECL_TEMPLATE(darwin, sys_sendfile); +DECL_TEMPLATE(darwin, sys_fcntl); +DECL_TEMPLATE(darwin, sys_fcntl64); +DECL_TEMPLATE(darwin, sys_ioctl); +DECL_TEMPLATE(darwin, sys_futimes); +DECL_TEMPLATE(darwin, sys_FAKE_SIGRETURN); +DECL_TEMPLATE(darwin, sys_sigreturn); + +// Mach message helpers +DECL_TEMPLATE(darwin, host_info); +DECL_TEMPLATE(darwin, host_page_size); +DECL_TEMPLATE(darwin, host_get_io_master); +DECL_TEMPLATE(darwin, host_get_clock_service); +DECL_TEMPLATE(darwin, host_request_notification); +DECL_TEMPLATE(darwin, mach_port_type); +DECL_TEMPLATE(darwin, mach_port_extract_member); +DECL_TEMPLATE(darwin, mach_port_allocate); +DECL_TEMPLATE(darwin, mach_port_deallocate); +DECL_TEMPLATE(darwin, mach_port_get_refs); +DECL_TEMPLATE(darwin, mach_port_mod_refs); +DECL_TEMPLATE(darwin, mach_port_get_set_status); +DECL_TEMPLATE(darwin, mach_port_destroy); +DECL_TEMPLATE(darwin, mach_port_request_notification); +DECL_TEMPLATE(darwin, mach_port_insert_right); +DECL_TEMPLATE(darwin, mach_port_get_attributes); +DECL_TEMPLATE(darwin, mach_port_set_attributes); +DECL_TEMPLATE(darwin, mach_port_insert_member); +DECL_TEMPLATE(darwin, task_get_special_port); +DECL_TEMPLATE(darwin, semaphore_create); +DECL_TEMPLATE(darwin, semaphore_destroy); +DECL_TEMPLATE(darwin, mach_ports_lookup); +DECL_TEMPLATE(darwin, task_threads); +DECL_TEMPLATE(darwin, task_suspend); +DECL_TEMPLATE(darwin, task_resume); +DECL_TEMPLATE(darwin, vm_allocate); +DECL_TEMPLATE(darwin, vm_deallocate); +DECL_TEMPLATE(darwin, vm_protect); +DECL_TEMPLATE(darwin, vm_inherit); +DECL_TEMPLATE(darwin, vm_read); +DECL_TEMPLATE(darwin, mach_vm_read); +DECL_TEMPLATE(darwin, vm_copy); +DECL_TEMPLATE(darwin, vm_read_overwrite); +DECL_TEMPLATE(darwin, vm_map); +DECL_TEMPLATE(darwin, vm_remap); +DECL_TEMPLATE(darwin, mach_make_memory_entry_64); +DECL_TEMPLATE(darwin, vm_purgable_control); +DECL_TEMPLATE(darwin, mach_vm_purgable_control); +DECL_TEMPLATE(darwin, mach_vm_allocate); +DECL_TEMPLATE(darwin, mach_vm_deallocate); +DECL_TEMPLATE(darwin, mach_vm_protect); +DECL_TEMPLATE(darwin, mach_vm_copy); +DECL_TEMPLATE(darwin, mach_vm_inherit); +DECL_TEMPLATE(darwin, mach_vm_map); +DECL_TEMPLATE(darwin, mach_vm_region_recurse); +DECL_TEMPLATE(darwin, thread_terminate); +DECL_TEMPLATE(darwin, thread_create); +DECL_TEMPLATE(darwin, thread_create_running); +DECL_TEMPLATE(darwin, thread_suspend); +DECL_TEMPLATE(darwin, thread_get_state); +DECL_TEMPLATE(darwin, thread_policy); +DECL_TEMPLATE(darwin, thread_info); +DECL_TEMPLATE(darwin, bootstrap_register); +DECL_TEMPLATE(darwin, bootstrap_look_up); +DECL_TEMPLATE(darwin, mach_msg_receive); +DECL_TEMPLATE(darwin, mach_msg_bootstrap); +DECL_TEMPLATE(darwin, mach_msg_host); +DECL_TEMPLATE(darwin, mach_msg_task); +DECL_TEMPLATE(darwin, mach_msg_thread); + +// Mach traps +DECL_TEMPLATE(darwin, mach_msg_unhandled); +DECL_TEMPLATE(darwin, mach_msg); +DECL_TEMPLATE(darwin, mach_reply_port); +DECL_TEMPLATE(darwin, mach_thread_self); +DECL_TEMPLATE(darwin, mach_host_self); +DECL_TEMPLATE(darwin, mach_task_self); +DECL_TEMPLATE(darwin, syscall_thread_switch); +DECL_TEMPLATE(darwin, semaphore_signal); +DECL_TEMPLATE(darwin, semaphore_signal_all); +DECL_TEMPLATE(darwin, semaphore_signal_thread); +DECL_TEMPLATE(darwin, semaphore_wait); +DECL_TEMPLATE(darwin, semaphore_wait_signal); +DECL_TEMPLATE(darwin, semaphore_timedwait); +DECL_TEMPLATE(darwin, semaphore_timedwait_signal); +DECL_TEMPLATE(darwin, sys___semwait_signal); +DECL_TEMPLATE(darwin, task_for_pid); +DECL_TEMPLATE(darwin, pid_for_task); +DECL_TEMPLATE(darwin, mach_timebase_info); +DECL_TEMPLATE(darwin, mach_wait_until); +DECL_TEMPLATE(darwin, mk_timer_create); +DECL_TEMPLATE(darwin, mk_timer_destroy); +DECL_TEMPLATE(darwin, mk_timer_arm); +DECL_TEMPLATE(darwin, mk_timer_cancel); +DECL_TEMPLATE(darwin, iokit_user_client_trap); +DECL_TEMPLATE(darwin, swtch); +DECL_TEMPLATE(darwin, swtch_pri); + +// Machine-dependent traps +DECL_TEMPLATE(darwin, pthread_set_self); + +// syswrap--darwin.c +#include +extern +void thread_state_from_vex(thread_state_t mach_generic, + thread_state_flavor_t flavor, + mach_msg_type_number_t count, + VexGuestArchState *vex_generic); +extern +void thread_state_to_vex(const thread_state_t mach_generic, + thread_state_flavor_t flavor, + mach_msg_type_number_t count, + VexGuestArchState *vex_generic); +extern +ThreadState *build_thread(const thread_state_t state, + thread_state_flavor_t flavor, + mach_msg_type_number_t count); +extern +void hijack_thread_state(thread_state_t mach_generic, + thread_state_flavor_t flavor, + mach_msg_type_number_t count, + ThreadState *tst); +extern +__attribute__((noreturn)) +void call_on_new_stack_0_1 ( Addr stack, + Addr retaddr, + void (*f)(Word), + Word arg1 ); + +extern void pthread_hijack_asm(void); +extern void pthread_hijack(Addr self, Addr kport, Addr func, Addr func_arg, + Addr stacksize, Addr flags, Addr sp); +extern void wqthread_hijack_asm(void); +extern void wqthread_hijack(Addr self, Addr kport, Addr stackaddr, Addr workitem, Int reuse, Addr sp); + +extern Addr pthread_starter; +extern Addr wqthread_starter; +extern SizeT pthread_structsize; + + +#endif + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_syswrap/priv_syswrap-main.h b/coregrind/m_syswrap/priv_syswrap-main.h index 7c4fc5242..e4b87b3ec 100644 --- a/coregrind/m_syswrap/priv_syswrap-main.h +++ b/coregrind/m_syswrap/priv_syswrap-main.h @@ -35,6 +35,12 @@ extern void ML_(fixup_guest_state_to_restart_syscall) ( ThreadArchState* arch ); +#if defined(VGO_darwin) +/* Longjmp to scheduler after client calls workq_ops(WQOPS_THREAD_RETURN)*/ +extern +void ML_(wqthread_continue_NORETURN)(ThreadId tid); +#endif + #endif // __PRIV_SYSWRAP_MAIN_H /*--------------------------------------------------------------------*/ diff --git a/coregrind/m_syswrap/priv_types_n_macros.h b/coregrind/m_syswrap/priv_types_n_macros.h index 45536b896..c481b8fc3 100644 --- a/coregrind/m_syswrap/priv_types_n_macros.h +++ b/coregrind/m_syswrap/priv_types_n_macros.h @@ -49,8 +49,8 @@ /* Arguments for a syscall. */ typedef - struct { - UWord sysno; + struct SyscallArgs { + Word sysno; UWord arg1; UWord arg2; UWord arg3; @@ -64,7 +64,7 @@ typedef /* Current status of a syscall being done on behalf of the client. */ typedef - struct { + struct SyscallStatus { enum { /* call is complete, result is in 'res' */ SsComplete=1, @@ -80,6 +80,12 @@ typedef /* Guest state layout info for syscall args. */ typedef struct { + // Note that, depending on the platform, arguments may be found in + // registers or on the stack. (See the comment at the top of + // syswrap-main.c for per-platform details.) For register arguments + // (which have o_arg field names) the o_arg value is the offset from + // the vex register state. For stack arguments (which have s_arg + // field names), the s_arg value is the offset from the stack pointer. Int o_sysno; # if defined(VGP_x86_linux) || defined(VGP_amd64_linux) \ || defined(VGP_ppc32_linux) || defined(VGP_ppc64_linux) @@ -100,6 +106,24 @@ typedef Int o_arg6; Int o_arg7; Int o_arg8; +# elif defined(VGP_x86_darwin) + Int s_arg1; + Int s_arg2; + Int s_arg3; + Int s_arg4; + Int s_arg5; + Int s_arg6; + Int s_arg7; + Int s_arg8; +# elif defined(VGP_amd64_darwin) + Int o_arg1; + Int o_arg2; + Int o_arg3; + Int o_arg4; + Int o_arg5; + Int o_arg6; + Int s_arg7; + Int s_arg8; # else # error "Unknown platform" # endif @@ -146,7 +170,7 @@ typedef */ -#if defined(VGO_linux) +#if defined(VGO_linux) || defined(VGO_darwin) /* On Linux, finding the wrapper is easy: just look up in fixed, platform-specific tables. These are defined in the relevant platform-specific files -- syswrap-arch-os.c */ @@ -245,8 +269,15 @@ SyscallTableEntry* ML_(get_ppc64_aix5_syscall_entry) ( UInt sysno ); vgSysWrap_##auxstr##_##name##_after /* Add a generic wrapper to a syscall table. */ -#define GENX_(sysno, name) WRAPPER_ENTRY_X_(generic, sysno, name) -#define GENXY(sysno, name) WRAPPER_ENTRY_XY(generic, sysno, name) +#if defined(VGO_linux) || defined(VGO_aix5) +# define GENX_(sysno, name) WRAPPER_ENTRY_X_(generic, sysno, name) +# define GENXY(sysno, name) WRAPPER_ENTRY_XY(generic, sysno, name) +#elif defined(VGO_darwin) +# define GENX_(sysno, name) WRAPPER_ENTRY_X_(generic, VG_DARWIN_SYSNO_INDEX(sysno), name) +# define GENXY(sysno, name) WRAPPER_ENTRY_XY(generic, VG_DARWIN_SYSNO_INDEX(sysno), name) +#else +# error Unknown OS +#endif /* Add a Linux-specific, arch-independent wrapper to a syscall table. */ @@ -343,15 +374,57 @@ static inline UWord getERR ( SyscallStatus* st ) { /* Macros used to tell tools about uses of scalar arguments. Note, these assume little-endianness. These can only be used in pre-wrappers, and they refer to the layout parameter passed in. */ -/* PRRAn == "pre-register-read-argument" - PRRSN == "pre-register-read-syscall" +/* PRRSN == "pre-register-read-sysno" + PRRAn == "pre-register-read-argument" + PSRAn == "pre-stack-read-argument" + PRAn == "pre-read-argument" */ +#if defined(VGO_linux) + /* Up to 6 parameters, all in registers. */ +# define PRA1(s,t,a) PRRAn(1,s,t,a) +# define PRA2(s,t,a) PRRAn(2,s,t,a) +# define PRA3(s,t,a) PRRAn(3,s,t,a) +# define PRA4(s,t,a) PRRAn(4,s,t,a) +# define PRA5(s,t,a) PRRAn(5,s,t,a) +# define PRA6(s,t,a) PRRAn(6,s,t,a) + +#elif defined(VGO_aix5) +# error Need to fill this in for AIX5 + +#elif defined(VGP_x86_darwin) + /* Up to 8 parameters, all on the stack. */ +# define PRA1(s,t,a) PSRAn(1,s,t,a) +# define PRA2(s,t,a) PSRAn(2,s,t,a) +# define PRA3(s,t,a) PSRAn(3,s,t,a) +# define PRA4(s,t,a) PSRAn(4,s,t,a) +# define PRA5(s,t,a) PSRAn(5,s,t,a) +# define PRA6(s,t,a) PSRAn(6,s,t,a) +# define PRA7(s,t,a) PSRAn(7,s,t,a) +# define PRA8(s,t,a) PSRAn(8,s,t,a) + +#elif defined(VGP_amd64_darwin) + /* Up to 8 parameters, 6 in registers, 2 on the stack. */ +# define PRA1(s,t,a) PRRAn(1,s,t,a) +# define PRA2(s,t,a) PRRAn(2,s,t,a) +# define PRA3(s,t,a) PRRAn(3,s,t,a) +# define PRA4(s,t,a) PRRAn(4,s,t,a) +# define PRA5(s,t,a) PRRAn(5,s,t,a) +# define PRA6(s,t,a) PRRAn(6,s,t,a) +# define PRA7(s,t,a) PSRAn(7,s,t,a) +# define PRA8(s,t,a) PSRAn(8,s,t,a) + +#else +# error Unknown platform +#endif + + /* Tell the tool that the syscall number is being read. */ #define PRRSN \ VG_(tdict).track_pre_reg_read(Vg_CoreSysCall, tid, "(syscallno)", \ layout->o_sysno, sizeof(UWord)); +/* REGISTER PARAMETERS */ /* PRRAn: Tell the tool that the register holding the n-th syscall argument is being read, at type 't' which must be at most the size @@ -402,6 +475,55 @@ static inline UWord getERR ( SyscallStatus* st ) { #endif +/* STACK PARAMETERS */ + +/* PSRAn: Tell the tool that the memory holding the n-th syscall + argument is being read, at type 't' which must be at most the size + of a register but can be smaller. In the latter case we need to be + careful about endianness. */ + +/* little-endian: the part of the guest state being read is + let here = offset_of_reg + in [here .. here + sizeof(t) - 1] + since the least significant parts of the guest register are stored + in memory at the lowest address. +*/ +#define PSRAn_LE(n,s,t,a) \ + do { \ + Addr here = layout->s_arg##n + VG_(get_SP)(tid); \ + vg_assert(sizeof(t) <= sizeof(UWord)); \ + VG_(tdict).track_pre_mem_read( \ + Vg_CoreSysCallArgInMem, tid, s"("#a")", \ + here, sizeof(t) \ + ); \ + } while (0) + +/* big-endian: the part of the guest state being read is + let next = offset_of_reg + sizeof(reg) + in [next - sizeof(t) .. next - 1] + since the least significant parts of the guest register are stored + in memory at the highest address. +*/ +#define PSRAn_BE(n,s,t,a) \ + do { \ + Addr next = layout->o_arg##n + sizeof(UWord) + \ + VG_(threads)[tid].arch.vex.VG_STACK_PTR; \ + vg_assert(sizeof(t) <= sizeof(UWord)); \ + VG_(tdict).track_pre_mem_read( \ + Vg_CoreSysCallArgInMem, tid, s"("#a")", \ + next-sizeof(t), sizeof(t) \ + ); \ + } while (0) + +#if defined(VG_BIGENDIAN) +# define PSRAn(n,s,t,a) PSRAn_BE(n,s,t,a) +#elif defined(VG_LITTLEENDIAN) +# define PSRAn(n,s,t,a) PSRAn_LE(n,s,t,a) +#else +# error "Unknown endianness" +#endif + + #define PRE_REG_READ0(tr, s) \ if (VG_(tdict).track_pre_reg_read) { \ PRRSN; \ @@ -409,35 +531,50 @@ static inline UWord getERR ( SyscallStatus* st ) { #define PRE_REG_READ1(tr, s, t1, a1) \ if (VG_(tdict).track_pre_reg_read) { \ PRRSN; \ - PRRAn(1,s,t1,a1); \ + PRA1(s,t1,a1); \ } #define PRE_REG_READ2(tr, s, t1, a1, t2, a2) \ if (VG_(tdict).track_pre_reg_read) { \ PRRSN; \ - PRRAn(1,s,t1,a1); PRRAn(2,s,t2,a2); \ + PRA1(s,t1,a1); PRA2(s,t2,a2); \ } #define PRE_REG_READ3(tr, s, t1, a1, t2, a2, t3, a3) \ if (VG_(tdict).track_pre_reg_read) { \ PRRSN; \ - PRRAn(1,s,t1,a1); PRRAn(2,s,t2,a2); PRRAn(3,s,t3,a3); \ + PRA1(s,t1,a1); PRA2(s,t2,a2); PRA3(s,t3,a3); \ } #define PRE_REG_READ4(tr, s, t1, a1, t2, a2, t3, a3, t4, a4) \ if (VG_(tdict).track_pre_reg_read) { \ PRRSN; \ - PRRAn(1,s,t1,a1); PRRAn(2,s,t2,a2); PRRAn(3,s,t3,a3); \ - PRRAn(4,s,t4,a4); \ + PRA1(s,t1,a1); PRA2(s,t2,a2); PRA3(s,t3,a3); \ + PRA4(s,t4,a4); \ } #define PRE_REG_READ5(tr, s, t1, a1, t2, a2, t3, a3, t4, a4, t5, a5) \ if (VG_(tdict).track_pre_reg_read) { \ PRRSN; \ - PRRAn(1,s,t1,a1); PRRAn(2,s,t2,a2); PRRAn(3,s,t3,a3); \ - PRRAn(4,s,t4,a4); PRRAn(5,s,t5,a5); \ + PRA1(s,t1,a1); PRA2(s,t2,a2); PRA3(s,t3,a3); \ + PRA4(s,t4,a4); PRA5(s,t5,a5); \ } #define PRE_REG_READ6(tr, s, t1, a1, t2, a2, t3, a3, t4, a4, t5, a5, t6, a6) \ if (VG_(tdict).track_pre_reg_read) { \ PRRSN; \ - PRRAn(1,s,t1,a1); PRRAn(2,s,t2,a2); PRRAn(3,s,t3,a3); \ - PRRAn(4,s,t4,a4); PRRAn(5,s,t5,a5); PRRAn(6,s,t6,a6); \ + PRA1(s,t1,a1); PRA2(s,t2,a2); PRA3(s,t3,a3); \ + PRA4(s,t4,a4); PRA5(s,t5,a5); PRA6(s,t6,a6); \ + } +#define PRE_REG_READ7(tr, s, t1, a1, t2, a2, t3, a3, t4, a4, t5, a5, t6, a6, t7, a7) \ + if (VG_(tdict).track_pre_reg_read) { \ + PRRSN; \ + PRA1(s,t1,a1); PRA2(s,t2,a2); PRA3(s,t3,a3); \ + PRA4(s,t4,a4); PRA5(s,t5,a5); PRA6(s,t6,a6); \ + PRA7(s,t7,a7); \ + } + +#define PRE_REG_READ8(tr, s, t1, a1, t2, a2, t3, a3, t4, a4, t5, a5, t6, a6, t7, a7, t8, a8) \ + if (VG_(tdict).track_pre_reg_read) { \ + PRRSN; \ + PRA1(s,t1,a1); PRA2(s,t2,a2); PRA3(s,t3,a3); \ + PRA4(s,t4,a4); PRA5(s,t5,a5); PRA6(s,t6,a6); \ + PRA7(s,t7,a7); PRA8(s,t8,a8); \ } #define PRE_MEM_READ(zzname, zzaddr, zzlen) \ diff --git a/coregrind/m_syswrap/syscall-amd64-darwin.S b/coregrind/m_syswrap/syscall-amd64-darwin.S new file mode 100644 index 000000000..099a94e58 --- /dev/null +++ b/coregrind/m_syswrap/syscall-amd64-darwin.S @@ -0,0 +1,253 @@ + +/*--------------------------------------------------------------------*/ +/*--- Support for doing system calls. syscall-amd64-darwin.S ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2007 Julian Seward + jseward@acm.org + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics_asm.h" +#include "pub_core_vkiscnums.h" +#include "libvex_guest_offsets.h" + + +/*----------------------------------------------------------------*/ +/* + Perform a syscall for the client. This will run a syscall + with the client's specific per-thread signal mask. + + The structure of this function is such that, if the syscall is + interrupted by a signal, we can determine exactly what + execution state we were in with respect to the execution of + the syscall by examining the value of %eip in the signal + handler. This means that we can always do the appropriate + thing to precisely emulate the kernel's signal/syscall + interactions. + + The syscall number is taken from the argument, even though it + should also be in guest_state->guest_RAX. The syscall result + is written back to guest_state->guest_RAX on completion. + + Returns 0 if the syscall was successfully called (even if the + syscall itself failed), or a -ve error code if one of the + sigprocmasks failed (there's no way to determine which one + failed). + + VG_(fixup_guest_state_after_syscall_interrupted) does the + thread state fixup in the case where we were interrupted by a + signal. + + Prototype: + + Int ML_(do_syscall_for_client_WRK( + Int syscallno, // rdi + void* guest_state, // rsi + const vki_sigset_t *sysmask, // rdx + const vki_sigset_t *postmask, // rcx + Int sigsetSzB) // r8 + + Note that sigsetSzB is totally ignored (and irrelevant). +*/ + +/* from vki_arch.h */ +#define VKI_SIG_SETMASK 3 + +/* DO_SYSCALL MACH|MDEP|UNIX */ +#define MACH 1 +#define MDEP 2 +#define UNIX 3 + +.macro DO_SYSCALL + /* save callee-saved regs */ + pushq %rbp + movq %rsp, %rbp + // stack is now aligned + pushq %rdi // -8(%rbp) syscallno + pushq %rsi // -16(%rbp) guest_state + pushq %rdx // -24(%rbp) sysmask + pushq %rcx // -32(%rbp) postmask + pushq %r8 // -40(%rbp) sigsetSzB + // stack is now aligned + +L_$0_1: /* Even though we can't take a signal until the sigprocmask completes, + start the range early. + If rip is in the range [1,2), the syscall hasn't been started yet */ + + /* Set the signal mask which should be current during the syscall. */ + /* GrP fixme signals + DDD: JRS fixme: use __NR___pthread_sigmask, not __NR_rt_sigprocmask + movq $__NR_rt_sigprocmask, %rax // syscall # + movq $VKI_SIG_SETMASK, %rdi // how + movq -24(%rbp), %rsi // sysmask + movq -32(%rbp), %rdx // postmask + movq -40(%rbp), %r10 // sigsetSzB in r10 not rcx + DDD: fixme return address + syscall + + jnc 7f // sigprocmask failed + */ + + /* OK, that worked. Now do the syscall proper. */ + + /* 6 register parameters */ + movq -16(%rbp), %r11 /* r11 = VexGuestAMD64State * */ + movq OFFSET_amd64_RDI(%r11), %rdi + movq OFFSET_amd64_RSI(%r11), %rsi + movq OFFSET_amd64_RDX(%r11), %rdx + movq OFFSET_amd64_RCX(%r11), %r10 /* rcx is passed in r10 instead */ + movq OFFSET_amd64_R8(%r11), %r8 + movq OFFSET_amd64_R9(%r11), %r9 + /* 2 stack parameters plus return address (ignored by syscall) */ + movq OFFSET_amd64_RSP(%r11), %r11 /* r11 = simulated RSP */ + movq 16(%r11), %rax + pushq %rax + movq 8(%r11), %rax + pushq %rax + /* stack is currently aligned - return address misaligns */ + movq 0(%r11), %rax + pushq %rax + /* syscallno */ + movq -8(%rbp), %rax + + /* If rip==2, then the syscall was either just about + to start, or was interrupted and the kernel was + restarting it. */ +L_$0_2: syscall +L_$0_3: /* In the range [3, 4), the syscall result is in %rax, + but hasn't been committed to RAX. */ + + /* stack contents: 3 words for syscall above, plus our prologue */ + setc 0(%rsp) /* stash returned carry flag */ + + movq -16(%rbp), %r11 /* r11 = VexGuestAMD64State * */ + movq %rax, OFFSET_amd64_RAX(%r11) /* save back to RAX */ + movq %rdx, OFFSET_amd64_RDX(%r11) /* save back to RDX */ + +.if $0 == UNIX + /* save carry flag to VEX */ + xor %rax, %rax + movb 0(%rsp), %al + movq %rax, %rdi /* arg1 = new flag */ + movq %r11, %rsi /* arg2 = vex state */ + addq $$24, %rsp /* remove syscall parameters */ + call _LibVEX_GuestAMD64_put_rflag_c +.else + addq $$24, %rsp /* remove syscall parameters*/ +.endif + +L_$0_4: /* Re-block signals. If eip is in [4,5), then the syscall + is complete and we needn't worry about it. */ + /* GrP fixme signals + DDD: JRS fixme: use __NR___pthread_sigmask, not __NR_rt_sigprocmask + PUSH_di_si_dx_cx_8 + + movq $__NR_rt_sigprocmask, %rax // syscall # + movq $VKI_SIG_SETMASK, %rdi // how + movq %rcx, %rsi // postmask + xorq %rdx, %rdx // NULL + movq %r8, %r10 // sigsetSzB + DDD: fixme return address + syscall + + POP_di_si_dx_cx_8 + + jnc 7f // sigprocmask failed + */ +L_$0_5: /* now safe from signals */ + movq $$0, %rax /* SUCCESS */ + movq %rbp, %rsp + popq %rbp + ret + +/* GrP fixme signals +L_$0_7: // failure: return 0x8000 | error code + DDD: fixme return value + movq %rbp, %rsp + popq %rbp + ret +*/ + +.endmacro + + +.globl ML_(do_syscall_for_client_unix_WRK) +ML_(do_syscall_for_client_unix_WRK): + DO_SYSCALL UNIX + +.globl ML_(do_syscall_for_client_mach_WRK) +ML_(do_syscall_for_client_mach_WRK): + DO_SYSCALL MACH + +.globl ML_(do_syscall_for_client_mdep_WRK) +ML_(do_syscall_for_client_mdep_WRK): + DO_SYSCALL MDEP + +.data +/* export the ranges so that + VG_(fixup_guest_state_after_syscall_interrupted) can do the + right thing */ + +/* eg MK_L_SCLASS_N(UNIX,99) produces L_3_99 + since UNIX is #defined to 3 at the top of this file */ +#define FOO(scclass,labelno) L_##scclass##_##labelno +#define MK_L_SCCLASS_N(scclass,labelno) FOO(scclass,labelno) + +.globl ML_(blksys_setup_MACH) +.globl ML_(blksys_restart_MACH) +.globl ML_(blksys_complete_MACH) +.globl ML_(blksys_committed_MACH) +.globl ML_(blksys_finished_MACH) +ML_(blksys_setup_MACH): .quad MK_L_SCCLASS_N(MACH,1) +ML_(blksys_restart_MACH): .quad MK_L_SCCLASS_N(MACH,2) +ML_(blksys_complete_MACH): .quad MK_L_SCCLASS_N(MACH,3) +ML_(blksys_committed_MACH): .quad MK_L_SCCLASS_N(MACH,4) +ML_(blksys_finished_MACH): .quad MK_L_SCCLASS_N(MACH,5) + +.globl ML_(blksys_setup_MDEP) +.globl ML_(blksys_restart_MDEP) +.globl ML_(blksys_complete_MDEP) +.globl ML_(blksys_committed_MDEP) +.globl ML_(blksys_finished_MDEP) +ML_(blksys_setup_MDEP): .quad MK_L_SCCLASS_N(MDEP,1) +ML_(blksys_restart_MDEP): .quad MK_L_SCCLASS_N(MDEP,2) +ML_(blksys_complete_MDEP): .quad MK_L_SCCLASS_N(MDEP,3) +ML_(blksys_committed_MDEP): .quad MK_L_SCCLASS_N(MDEP,4) +ML_(blksys_finished_MDEP): .quad MK_L_SCCLASS_N(MDEP,5) + +.globl ML_(blksys_setup_UNIX) +.globl ML_(blksys_restart_UNIX) +.globl ML_(blksys_complete_UNIX) +.globl ML_(blksys_committed_UNIX) +.globl ML_(blksys_finished_UNIX) +ML_(blksys_setup_UNIX): .quad MK_L_SCCLASS_N(UNIX,1) +ML_(blksys_restart_UNIX): .quad MK_L_SCCLASS_N(UNIX,2) +ML_(blksys_complete_UNIX): .quad MK_L_SCCLASS_N(UNIX,3) +ML_(blksys_committed_UNIX): .quad MK_L_SCCLASS_N(UNIX,4) +ML_(blksys_finished_UNIX): .quad MK_L_SCCLASS_N(UNIX,5) + + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_syswrap/syscall-x86-darwin.S b/coregrind/m_syswrap/syscall-x86-darwin.S new file mode 100644 index 000000000..41cc27e68 --- /dev/null +++ b/coregrind/m_syswrap/syscall-x86-darwin.S @@ -0,0 +1,252 @@ + +/*--------------------------------------------------------------------*/ +/*--- Support for doing system calls. syscall-x86-darwin.S ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2007 Julian Seward + jseward@acm.org + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics_asm.h" +#include "pub_core_vkiscnums.h" +#include "libvex_guest_offsets.h" + + +/*----------------------------------------------------------------*/ +/* + Perform a syscall for the client. This will run a syscall + with the client's specific per-thread signal mask. + + The structure of this function is such that, if the syscall is + interrupted by a signal, we can determine exactly what + execution state we were in with respect to the execution of + the syscall by examining the value of %eip in the signal + handler. This means that we can always do the appropriate + thing to precisely emulate the kernel's signal/syscall + interactions. + + The syscall number is taken from the argument, even though it + should also be in regs->m_eax. The syscall result is written + back to regs->m_eax on completion. + + Returns 0 if the syscall was successfully called (even if the + syscall itself failed), or a -ve error code if one of the + sigprocmasks failed (there's no way to determine which one + failed). + + VG_(fixup_guest_state_after_syscall_interrupted) does the + thread state fixup in the case where we were interrupted by a + signal. + + Prototype: + + Int ML_(do_syscall_for_client_WRK)( + Int syscallno, // ebp+8 + void* guest_state, // ebp+12 + const vki_sigset_t *sysmask, // ebp+16 + const vki_sigset_t *postmask, // ebp+20 + Int sigsetSzB) // ebp+24 + + Note that sigsetSzB is totally ignored (and irrelevant). +*/ + +/* from vki-darwin.h, checked at startup by m_vki.c */ +#define VKI_SIG_SETMASK 3 + +/* DO_SYSCALL MACH|MDEP|UNIX */ +#define MACH 1 +#define MDEP 2 +#define UNIX 3 + +.macro DO_SYSCALL + /* establish stack frame */ + push %ebp + mov %esp, %ebp + subl $$8, %esp /* 16-byte align stack */ + +L_$0_1: /* Even though we can't take a signal until the + __pthread_sigmask completes, start the range early. + If eip is in the range [1,2), the syscall hasn't been started yet */ + + /* Set the signal mask which should be current during the syscall. */ + /* Set up for __pthread_sigmask(SIG_SETMASK, sysmask, postmask) */ + pushl 20(%ebp) + pushl 16(%ebp) + pushl $$VKI_SIG_SETMASK + pushl $$0xcafebabe /* totally fake return address */ + movl $$__NR___pthread_sigmask, %eax + int $$0x80 /* should be sysenter? */ + jc L_$0_7 /* __pthread_sigmask failed */ + addl $$16,%esp + + /* Copy syscall parameters to the stack - assume no more than 8 + * plus the return address */ + /* do_syscall8 */ + /* stack is currently aligned assuming 8 parameters */ + movl 12(%ebp), %edx + movl OFFSET_x86_ESP(%edx), %edx /* edx = simulated ESP */ + movl 28+4(%edx), %eax + pushl %eax + movl 24+4(%edx), %eax + pushl %eax + movl 20+4(%edx), %eax + pushl %eax + movl 16+4(%edx), %eax + pushl %eax + movl 12+4(%edx), %eax + pushl %eax + movl 8+4(%edx), %eax + pushl %eax + movl 4+4(%edx), %eax + pushl %eax + movl 0+4(%edx), %eax + pushl %eax + /* return address */ + movl 0(%edx), %eax + pushl %eax + + /* Put syscall number in eax */ + movl 8(%ebp), %eax + + /* If eip==2, then the syscall was either just about to start, + or was interrupted and the kernel was restarting it. */ +L_$0_2: +.if $0 == UNIX + int $$0x80 /* UNIX (GrP fixme should be sysenter?) */ +.elseif $0 == MACH + int $$0x81 +.elseif $0 == MDEP + int $$0x82 +.else + error$0 x +.endif + +L_$0_3: /* In the range [3, 4), the syscall result is in %eax and %edx and C, + but hasn't been committed to the thread state. */ + setc 0(%esp) /* stash returned carry flag */ + movl 12(%ebp), %ecx + movl %eax, OFFSET_x86_EAX(%ecx) /* save EAX to vex */ + movl %edx, OFFSET_x86_EDX(%ecx) /* save EDX to vex */ +.if $0 == UNIX + /* UNIX: save carry flag to vex */ + subl $$12, %esp + movl %ecx, 4(%esp) + movl $$0, 0(%esp) + movb 12(%esp), %al + movb %al, 0(%esp) + call _LibVEX_GuestX86_put_eflag_c + addl $$12, %esp +.endif + +L_$0_4: /* Re-block signals. If eip is in [4,5), then the syscall is + complete and we needn't worry about it. */ + /* Set up for __pthread_sigmask(SIG_SETMASK, postmask, NULL) */ + pushl $$0 + pushl 20(%ebp) + pushl $$VKI_SIG_SETMASK + pushl $$0xcafef00d /* totally fake return address */ + movl $$__NR___pthread_sigmask, %eax + int $$0x80 /* should be sysenter? */ + jc L_$0_7 /* __pthread_sigmask failed */ + addl $$16,%esp + +L_$0_5: /* now safe from signals */ + movl $$0, %eax /* SUCCESS */ + movl %ebp, %esp + popl %ebp + ret + +L_$0_7: /* failure: return 0x8000 | error code */ + /* Note that we enter here with %esp being 16 too low + (4 extra words on the stack). But because we're nuking + the stack frame now, that doesn't matter. */ + andl $$0x7FFF, %eax + orl $$0x8000, %eax + movl %ebp, %esp + popl %ebp + ret + +.endmacro + + +.globl ML_(do_syscall_for_client_unix_WRK) +ML_(do_syscall_for_client_unix_WRK): + DO_SYSCALL UNIX + +.globl ML_(do_syscall_for_client_mach_WRK) +ML_(do_syscall_for_client_mach_WRK): + DO_SYSCALL MACH + +.globl ML_(do_syscall_for_client_mdep_WRK) +ML_(do_syscall_for_client_mdep_WRK): + DO_SYSCALL MDEP + +.data +/* export the ranges so that + VG_(fixup_guest_state_after_syscall_interrupted) can do the + right thing */ + +/* eg MK_L_SCLASS_N(UNIX,99) produces L_3_99 + since UNIX is #defined to 3 at the top of this file */ +#define FOO(scclass,labelno) L_##scclass##_##labelno +#define MK_L_SCCLASS_N(scclass,labelno) FOO(scclass,labelno) + +.globl ML_(blksys_setup_MACH) +.globl ML_(blksys_restart_MACH) +.globl ML_(blksys_complete_MACH) +.globl ML_(blksys_committed_MACH) +.globl ML_(blksys_finished_MACH) +ML_(blksys_setup_MACH): .long MK_L_SCCLASS_N(MACH,1) +ML_(blksys_restart_MACH): .long MK_L_SCCLASS_N(MACH,2) +ML_(blksys_complete_MACH): .long MK_L_SCCLASS_N(MACH,3) +ML_(blksys_committed_MACH): .long MK_L_SCCLASS_N(MACH,4) +ML_(blksys_finished_MACH): .long MK_L_SCCLASS_N(MACH,5) + +.globl ML_(blksys_setup_MDEP) +.globl ML_(blksys_restart_MDEP) +.globl ML_(blksys_complete_MDEP) +.globl ML_(blksys_committed_MDEP) +.globl ML_(blksys_finished_MDEP) +ML_(blksys_setup_MDEP): .long MK_L_SCCLASS_N(MDEP,1) +ML_(blksys_restart_MDEP): .long MK_L_SCCLASS_N(MDEP,2) +ML_(blksys_complete_MDEP): .long MK_L_SCCLASS_N(MDEP,3) +ML_(blksys_committed_MDEP): .long MK_L_SCCLASS_N(MDEP,4) +ML_(blksys_finished_MDEP): .long MK_L_SCCLASS_N(MDEP,5) + +.globl ML_(blksys_setup_UNIX) +.globl ML_(blksys_restart_UNIX) +.globl ML_(blksys_complete_UNIX) +.globl ML_(blksys_committed_UNIX) +.globl ML_(blksys_finished_UNIX) +ML_(blksys_setup_UNIX): .long MK_L_SCCLASS_N(UNIX,1) +ML_(blksys_restart_UNIX): .long MK_L_SCCLASS_N(UNIX,2) +ML_(blksys_complete_UNIX): .long MK_L_SCCLASS_N(UNIX,3) +ML_(blksys_committed_UNIX): .long MK_L_SCCLASS_N(UNIX,4) +ML_(blksys_finished_UNIX): .long MK_L_SCCLASS_N(UNIX,5) + + + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_syswrap/syswrap-amd64-darwin.c b/coregrind/m_syswrap/syswrap-amd64-darwin.c new file mode 100644 index 000000000..714a75585 --- /dev/null +++ b/coregrind/m_syswrap/syswrap-amd64-darwin.c @@ -0,0 +1,457 @@ + +/*--------------------------------------------------------------------*/ +/*--- Darwin-specific syscalls, etc. syswrap-amd64-darwin.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2005-2009 Apple Inc. + Greg Parker gparker@apple.com + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics.h" +#include "pub_core_vki.h" +#include "pub_core_threadstate.h" +#include "pub_core_aspacemgr.h" +#include "pub_core_xarray.h" +#include "pub_core_clientstate.h" +#include "pub_core_debuglog.h" +#include "pub_core_debuginfo.h" // VG_(di_notify_*) +#include "pub_core_transtab.h" // VG_(discard_translations) +#include "pub_core_libcbase.h" +#include "pub_core_libcassert.h" +#include "pub_core_libcfile.h" +#include "pub_core_libcprint.h" +#include "pub_core_libcproc.h" +#include "pub_core_libcsignal.h" +#include "pub_core_mallocfree.h" +#include "pub_core_options.h" +#include "pub_core_scheduler.h" +#include "pub_core_sigframe.h" // For VG_(sigframe_destroy)() +#include "pub_core_signals.h" +#include "pub_core_syscall.h" +#include "pub_core_syswrap.h" +#include "pub_core_tooliface.h" + +#include "priv_types_n_macros.h" +#include "priv_syswrap-generic.h" /* for decls of generic wrappers */ +#include "priv_syswrap-darwin.h" /* for decls of darwin-ish wrappers */ +#include "priv_syswrap-main.h" + + +#include + +static void x86_thread_state64_from_vex(x86_thread_state64_t *mach, + VexGuestAMD64State *vex) +{ + mach->__rax = vex->guest_RAX; + mach->__rbx = vex->guest_RBX; + mach->__rcx = vex->guest_RCX; + mach->__rdx = vex->guest_RDX; + mach->__rdi = vex->guest_RDI; + mach->__rsi = vex->guest_RSI; + mach->__rbp = vex->guest_RBP; + mach->__rsp = vex->guest_RSP; + mach->__rflags = LibVEX_GuestAMD64_get_rflags(vex); + mach->__rip = vex->guest_RIP; + mach->__r8 = vex->guest_R8; + mach->__r9 = vex->guest_R9; + mach->__r10 = vex->guest_R10; + mach->__r11 = vex->guest_R11; + mach->__r12 = vex->guest_R12; + mach->__r13 = vex->guest_R13; + mach->__r14 = vex->guest_R14; + mach->__r15 = vex->guest_R15; + /* GrP fixme + mach->__cs = vex->guest_CS; + mach->__fs = vex->guest_FS; + mach->__gs = vex->guest_GS; + */ +} + + +static void x86_float_state64_from_vex(x86_float_state64_t *mach, + VexGuestAMD64State *vex) +{ + // DDD: #warning GrP fixme fp state + + VG_(memcpy)(&mach->__fpu_xmm0, &vex->guest_XMM0, 16 * sizeof(mach->__fpu_xmm0)); +} + + +void thread_state_from_vex(thread_state_t mach_generic, + thread_state_flavor_t flavor, + mach_msg_type_number_t count, + VexGuestArchState *vex_generic) +{ + VexGuestAMD64State *vex = (VexGuestAMD64State *)vex_generic; + + switch (flavor) { + case x86_THREAD_STATE64: + vg_assert(count == x86_THREAD_STATE64_COUNT); + x86_thread_state64_from_vex((x86_thread_state64_t *)mach_generic, vex); + break; + + case x86_FLOAT_STATE64: + vg_assert(count == x86_FLOAT_STATE64_COUNT); + x86_float_state64_from_vex((x86_float_state64_t *)mach_generic, vex); + break; + + default: + vg_assert(0); + } +} + + +static void x86_thread_state64_to_vex(const x86_thread_state64_t *mach, + VexGuestAMD64State *vex) +{ + LibVEX_GuestAMD64_initialise(vex); + vex->guest_RAX = mach->__rax; + vex->guest_RBX = mach->__rbx; + vex->guest_RCX = mach->__rcx; + vex->guest_RDX = mach->__rdx; + vex->guest_RDI = mach->__rdi; + vex->guest_RSI = mach->__rsi; + vex->guest_RBP = mach->__rbp; + vex->guest_RSP = mach->__rsp; + // DDD: #warning GrP fixme eflags + vex->guest_RIP = mach->__rip; + vex->guest_R8 = mach->__r8; + vex->guest_R9 = mach->__r9; + vex->guest_R10 = mach->__r10; + vex->guest_R11 = mach->__r11; + vex->guest_R12 = mach->__r12; + vex->guest_R13 = mach->__r13; + vex->guest_R14 = mach->__r14; + vex->guest_R15 = mach->__r15; + /* GrP fixme + vex->guest_CS = mach->__cs; + vex->guest_FS = mach->__fs; + vex->guest_GS = mach->__gs; + */ +} + +static void x86_float_state64_to_vex(const x86_float_state64_t *mach, + VexGuestAMD64State *vex) +{ + // DDD: #warning GrP fixme fp state + + VG_(memcpy)(&vex->guest_XMM0, &mach->__fpu_xmm0, 16 * sizeof(mach->__fpu_xmm0)); +} + + +void thread_state_to_vex(const thread_state_t mach_generic, + thread_state_flavor_t flavor, + mach_msg_type_number_t count, + VexGuestArchState *vex_generic) +{ + VexGuestAMD64State *vex = (VexGuestAMD64State *)vex_generic; + + switch(flavor) { + case x86_THREAD_STATE64: + vg_assert(count == x86_THREAD_STATE64_COUNT); + x86_thread_state64_to_vex((const x86_thread_state64_t*)mach_generic,vex); + break; + case x86_FLOAT_STATE64: + vg_assert(count == x86_FLOAT_STATE64_COUNT); + x86_float_state64_to_vex((const x86_float_state64_t*)mach_generic,vex); + break; + + default: + vg_assert(0); + break; + } +} + + +ThreadState *build_thread(const thread_state_t state, + thread_state_flavor_t flavor, + mach_msg_type_number_t count) +{ + ThreadId tid = VG_(alloc_ThreadState)(); + ThreadState *tst = VG_(get_ThreadState)(tid); + + vg_assert(flavor == x86_THREAD_STATE64); + vg_assert(count == x86_THREAD_STATE64_COUNT); + + // Initialize machine registers + + thread_state_to_vex(state, flavor, count, &tst->arch.vex); + + I_die_here; + // GrP fixme signals, sig_mask, tmp_sig_mask, os_state.parent + + find_stack_segment(tid, tst->arch.vex.guest_RSP); + + return tst; +} + + +// Edit the thread state to send to the real kernel. +// The real thread will run start_thread_NORETURN(tst) +// on a separate non-client stack. +void hijack_thread_state(thread_state_t mach_generic, + thread_state_flavor_t flavor, + mach_msg_type_number_t count, + ThreadState *tst) +{ + x86_thread_state64_t *mach = (x86_thread_state64_t *)mach_generic; + char *stack; + + vg_assert(flavor == x86_THREAD_STATE64); + vg_assert(count == x86_THREAD_STATE64_COUNT); + + stack = (char *)allocstack(tst->tid); + stack -= 64+320; // make room for top frame + memset(stack, 0, 64+320); // ...and clear it + *(uintptr_t *)stack = 0; // push fake return address + + mach->__rdi = (uintptr_t)tst; // arg1 = tst + mach->__rip = (uintptr_t)&start_thread_NORETURN; + mach->__rsp = (uintptr_t)stack; +} + + +/* Call f(arg1), but first switch stacks, using 'stack' as the new + stack, and use 'retaddr' as f's return-to address. Also, clear all + the integer registers before entering f.*/ +__attribute__((noreturn)) +void call_on_new_stack_0_1 ( Addr stack, + Addr retaddr, + void (*f)(Word), + Word arg1 ); +// %rdi == stack (must be 16-byte aligned) +// %rsi == retaddr +// %rdx == f +// %rcx == arg1 +asm( +".globl _call_on_new_stack_0_1\n" +"_call_on_new_stack_0_1:\n" +" movq %rsp, %rbp\n" // remember old stack pointer +" movq %rdi, %rsp\n" // set new stack +" movq %rcx, %rdi\n" // set arg1 +" pushq %rsi\n" // retaddr to new stack +" pushq %rdx\n" // f to new stack +" movq $0, %rax\n" // zero all other GP regs +" movq $0, %rbx\n" +" movq $0, %rcx\n" +" movq $0, %rdx\n" +" movq $0, %rsi\n" +" movq $0, %rbp\n" +" movq $0, %r8\n" +" movq $0, %r9\n" +" movq $0, %r10\n" +" movq $0, %r11\n" +" movq $0, %r12\n" +" movq $0, %r13\n" +" movq $0, %r14\n" +" movq $0, %r15\n" +" ret\n" // jump to f +" ud2\n" // should never get here +); + +asm( +".globl _pthread_hijack_asm\n" +"_pthread_hijack_asm:\n" +" movq %rsp,%rbp\n" +" push $0\n" // alignment pad +" push %rbp\n" // original sp + // other values stay where they are in registers +" push $0\n" // fake return address +" jmp _pthread_hijack\n" + ); + + + +void pthread_hijack(Addr self, Addr kport, Addr func, Addr func_arg, + Addr stacksize, Addr flags, Addr sp) +{ + ThreadState *tst = (ThreadState *)func_arg; + VexGuestAMD64State *vex = &tst->arch.vex; + + // VG_(printf)("pthread_hijack pthread %p, machthread %p, func %p, arg %p, stack %p, flags %p, stack %p\n", self, kport, func, func_arg, stacksize, flags, sp); + + // Wait for parent thread's permission. + // The parent thread holds V's lock on our behalf. + semaphore_wait(tst->os_state.child_go); + + // Set thread's registers + // Do this FIRST because some code below tries to collect a backtrace, + // which requires valid register data. + LibVEX_GuestAMD64_initialise(vex); + vex->guest_RIP = pthread_starter; + vex->guest_RDI = self; + vex->guest_RSI = kport; + vex->guest_RDX = func; + vex->guest_RCX = tst->os_state.func_arg; + vex->guest_R8 = stacksize; + vex->guest_R9 = flags; + vex->guest_RSP = sp; + + // Record thread's stack and Mach port and pthread struct + tst->os_state.pthread = self; + tst->os_state.lwpid = kport; + record_named_port(tst->tid, kport, MACH_PORT_RIGHT_SEND, "thread-%p"); + + if ((flags & 0x01000000) == 0) { + // kernel allocated stack - needs mapping + Addr stack = VG_PGROUNDUP(sp) - stacksize; + tst->client_stack_highest_word = stack+stacksize; + tst->client_stack_szB = stacksize; + + // pthread structure + ML_(notify_core_and_tool_of_mmap)( + stack+stacksize, pthread_structsize, + VKI_PROT_READ|VKI_PROT_WRITE, VKI_MAP_PRIVATE, -1, 0); + // stack contents + ML_(notify_core_and_tool_of_mmap)( + stack, stacksize, + VKI_PROT_READ|VKI_PROT_WRITE, VKI_MAP_PRIVATE, -1, 0); + // guard page + ML_(notify_core_and_tool_of_mmap)( + stack-VKI_PAGE_SIZE, VKI_PAGE_SIZE, 0, VKI_MAP_PRIVATE, -1, 0); + } else { + // client allocated stack + find_stack_segment(tst->tid, sp); + } + VG_(am_do_sync_check)("after", "pthread_hijack", 0); + + // Tell parent thread's POST(sys_bsdthread_create) that we're done + // initializing registers and mapping memory. + semaphore_signal(tst->os_state.child_done); + // LOCK IS GONE BELOW THIS POINT + + // Go! + call_on_new_stack_0_1(tst->os_state.valgrind_stack_init_SP, 0, + start_thread_NORETURN, (Word)tst); + + /*NOTREACHED*/ + vg_assert(0); +} + + + +asm( +".globl _wqthread_hijack_asm\n" +"_wqthread_hijack_asm:\n" +" movq %rsp,%r9\n" // original sp + // other values stay where they are in registers +" push $0\n" // fake return address +" jmp _wqthread_hijack\n" + ); + + +/* + wqthread note: The kernel may create or destroy pthreads in the + wqthread pool at any time with no userspace interaction, + and wqthread_start may be entered at any time with no userspace + interaction. + To handle this in valgrind, we create and destroy a valgrind + thread for every work item. +*/ +void wqthread_hijack(Addr self, Addr kport, Addr stackaddr, Addr workitem, + Int reuse, Addr sp) +{ + ThreadState *tst; + VexGuestAMD64State *vex; + Addr stack; + SizeT stacksize; + + if (reuse) { + // This thread already exists; we're merely re-entering + // after leaving via workq_ops(WQOPS_THREAD_RETURN). + // Don't allocate any V thread resources. + // Do reset thread registers. + ThreadId tid = VG_(lwpid_to_vgtid)(kport); + vg_assert(VG_(is_valid_tid)(tid)); + vg_assert(mach_thread_self() == kport); + + tst = VG_(get_ThreadState)(tid); + vex = &tst->arch.vex; + vg_assert(tst->os_state.pthread == self); + } + else { + // This is a new thread. + tst = VG_(get_ThreadState)(VG_(alloc_ThreadState)()); + vex = &tst->arch.vex; + allocstack(tst->tid); + LibVEX_GuestAMD64_initialise(vex); + } + + // Set thread's registers + // Do this FIRST because some code below tries to collect a backtrace, + // which requires valid register data. + vex->guest_RIP = wqthread_starter; + vex->guest_RDI = self; + vex->guest_RSI = kport; + vex->guest_RDX = stackaddr; + vex->guest_RCX = workitem; + vex->guest_R8 = reuse; + vex->guest_R9 = 0; + vex->guest_RSP = sp; + + stacksize = 512*1024; // wq stacks are always DEFAULT_STACK_SIZE + stack = VG_PGROUNDUP(sp) - stacksize; + + if (reuse) { + // Continue V's thread back in the scheduler. + // The client thread is of course in another location entirely. + ML_(wqthread_continue_NORETURN)(tst->tid); + } + else { + + // Record thread's stack and Mach port and pthread struct + tst->os_state.pthread = self; + tst->os_state.lwpid = kport; + record_named_port(tst->tid, kport, MACH_PORT_RIGHT_SEND, "wqthread-%p"); + + // kernel allocated stack - needs mapping + tst->client_stack_highest_word = stack+stacksize; + tst->client_stack_szB = stacksize; + + // GrP fixme scheduler lock?! + + // pthread structure + ML_(notify_core_and_tool_of_mmap)( + stack+stacksize, pthread_structsize, + VKI_PROT_READ|VKI_PROT_WRITE, VKI_MAP_PRIVATE, -1, 0); + // stack contents + // GrP fixme uninitialized! + ML_(notify_core_and_tool_of_mmap)( + stack, stacksize, + VKI_PROT_READ|VKI_PROT_WRITE, VKI_MAP_PRIVATE, -1, 0); + // guard page + // GrP fixme ban_mem_stack! + ML_(notify_core_and_tool_of_mmap)( + stack-VKI_PAGE_SIZE, VKI_PAGE_SIZE, 0, VKI_MAP_PRIVATE, -1, 0); + + VG_(am_do_sync_check)("after", "wqthread_hijack", 0); + + // Go! + call_on_new_stack_0_1(tst->os_state.valgrind_stack_init_SP, 0, + start_thread_NORETURN, (Word)tst); + } + + /*NOTREACHED*/ + vg_assert(0); +} diff --git a/coregrind/m_syswrap/syswrap-darwin.c b/coregrind/m_syswrap/syswrap-darwin.c new file mode 100644 index 000000000..49b8a9a6b --- /dev/null +++ b/coregrind/m_syswrap/syswrap-darwin.c @@ -0,0 +1,7549 @@ + +/*--------------------------------------------------------------------*/ +/*--- Darwin-specific syscalls, etc. syswrap-darwin.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2005-2009 Apple Inc. + Greg Parker gparker@apple.com + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics.h" +#include "pub_core_vki.h" +#include "pub_core_vkiscnums.h" +#include "pub_core_threadstate.h" +#include "pub_core_aspacemgr.h" +#include "pub_core_xarray.h" +#include "pub_core_clientstate.h" +#include "pub_core_debuglog.h" +#include "pub_core_debuginfo.h" // VG_(di_notify_*) +#include "pub_core_transtab.h" // VG_(discard_translations) +#include "pub_core_libcbase.h" +#include "pub_core_libcassert.h" +#include "pub_core_libcfile.h" +#include "pub_core_libcprint.h" +#include "pub_core_libcproc.h" +#include "pub_core_libcsignal.h" +#include "pub_core_machine.h" // VG_(get_SP) +#include "pub_core_mallocfree.h" +#include "pub_core_options.h" +#include "pub_core_scheduler.h" +#include "pub_core_sigframe.h" // For VG_(sigframe_destroy)() +#include "pub_core_signals.h" +#include "pub_core_syscall.h" +#include "pub_core_syswrap.h" +#include "pub_core_tooliface.h" + +#include "priv_types_n_macros.h" +#include "priv_syswrap-generic.h" /* for decls of generic wrappers */ +#include "priv_syswrap-darwin.h" /* for decls of darwin-ish wrappers */ +#include "priv_syswrap-main.h" + +/* --- !!! --- EXTERNAL HEADERS start --- !!! --- */ +#include +#include +#include +#include /* struct kauth_filesec */ +/* --- !!! --- EXTERNAL HEADERS end --- !!! --- */ + +#define msgh_request_port msgh_remote_port +#define msgh_reply_port msgh_local_port +#define BOOTSTRAP_MAX_NAME_LEN 128 +typedef char name_t[BOOTSTRAP_MAX_NAME_LEN]; + +typedef uint64_t mig_addr_t; + + +// Saved ports +static mach_port_t vg_host_port = 0; +static mach_port_t vg_task_port = 0; +static mach_port_t vg_bootstrap_port = 0; + +// Run a thread from beginning to end and return the thread's +// scheduler-return-code. +static VgSchedReturnCode thread_wrapper(Word /*ThreadId*/ tidW) +{ + VgSchedReturnCode ret; + ThreadId tid = (ThreadId)tidW; + ThreadState* tst = VG_(get_ThreadState)(tid); + + VG_(debugLog)(1, "syswrap-darwin", + "thread_wrapper(tid=%lld): entry\n", + (ULong)tidW); + + vg_assert(tst->status == VgTs_Init); + + /* make sure we get the CPU lock before doing anything significant */ + VG_(acquire_BigLock)(tid, "thread_wrapper"); + + if (0) + VG_(printf)("thread tid %d started: stack = %p\n", + tid, &tid); + + VG_TRACK(pre_thread_first_insn, tid); + + tst->os_state.lwpid = VG_(gettid)(); + tst->os_state.threadgroup = VG_(getpid)(); + + /* Thread created with all signals blocked; scheduler will set the + appropriate mask */ + + ret = VG_(scheduler)(tid); + + vg_assert(VG_(is_exiting)(tid)); + + vg_assert(tst->status == VgTs_Runnable); + vg_assert(VG_(is_running_thread)(tid)); + + VG_(debugLog)(1, "syswrap-darwin", + "thread_wrapper(tid=%lld): done\n", + (ULong)tidW); + + /* Return to caller, still holding the lock. */ + return ret; +} + + + +/* Allocate a stack for this thread, if it doesn't already have one. + Returns the initial stack pointer value to use, or 0 if allocation + failed. */ + +Addr allocstack ( ThreadId tid ) +{ + ThreadState* tst = VG_(get_ThreadState)(tid); + VgStack* stack; + Addr initial_SP; + + /* Either the stack_base and stack_init_SP are both zero (in which + case a stack hasn't been allocated) or they are both non-zero, + in which case it has. */ + + if (tst->os_state.valgrind_stack_base == 0) + vg_assert(tst->os_state.valgrind_stack_init_SP == 0); + + if (tst->os_state.valgrind_stack_base != 0) + vg_assert(tst->os_state.valgrind_stack_init_SP != 0); + + /* If no stack is present, allocate one. */ + + if (tst->os_state.valgrind_stack_base == 0) { + stack = VG_(am_alloc_VgStack)( &initial_SP ); + if (stack) { + tst->os_state.valgrind_stack_base = (Addr)stack; + tst->os_state.valgrind_stack_init_SP = initial_SP; + } + } + + VG_(debugLog)( 2, "syswrap-darwin", "stack for tid %d at %p; init_SP=%p\n", + tid, + (void*)tst->os_state.valgrind_stack_base, + (void*)tst->os_state.valgrind_stack_init_SP ); + + vg_assert(VG_IS_32_ALIGNED(tst->os_state.valgrind_stack_init_SP)); + + return tst->os_state.valgrind_stack_init_SP; +} + + +void find_stack_segment(ThreadId tid, Addr sp) +{ + /* We don't really know where the client stack is, because it's + allocated by the client. The best we can do is look at the + memory mappings and try to derive some useful information. We + assume that esp starts near its highest possible value, and can + only go down to the start of the mmaped segment. */ + ThreadState *tst = VG_(get_ThreadState)(tid); + const NSegment *seg = VG_(am_find_nsegment)(sp); + if (seg && seg->kind != SkResvn) { + tst->client_stack_highest_word = (Addr)VG_PGROUNDUP(sp); + tst->client_stack_szB = tst->client_stack_highest_word - seg->start; + + if (1) + VG_(printf)("tid %d: guessed client stack range %#lx-%#lx\n", + tid, seg->start, VG_PGROUNDUP(sp)); + } else { + VG_(printf)("couldn't find user stack\n"); + VG_(message)(Vg_UserMsg, "!? New thread %d starts with SP(%#lx) unmapped\n", + tid, sp); + tst->client_stack_szB = 0; + } +} + + +/* Run a thread all the way to the end, then do appropriate exit actions + (this is the last-one-out-turn-off-the-lights bit). +*/ +static void run_a_thread_NORETURN ( Word tidW ) +{ + Int c; + VgSchedReturnCode src; + ThreadId tid = (ThreadId)tidW; + + VG_(debugLog)(1, "syswrap-darwin", + "run_a_thread_NORETURN(tid=%lld): pre-thread_wrapper\n", + (ULong)tidW); + + /* Run the thread all the way through. */ + src = thread_wrapper(tid); + + VG_(debugLog)(1, "syswrap-darwin", + "run_a_thread_NORETURN(tid=%lld): post-thread_wrapper\n", + (ULong)tidW); + + c = VG_(count_living_threads)(); + vg_assert(c >= 1); /* stay sane */ + + // Tell the tool this thread is exiting + VG_TRACK( pre_thread_ll_exit, tid ); + + if (c == 1) { + + VG_(debugLog)(1, "syswrap-darwin", + "run_a_thread_NORETURN(tid=%lld): " + "last one standing\n", + (ULong)tidW); + + /* We are the last one standing. Keep hold of the lock and + carry on to show final tool results, then exit the entire system. + Use the continuation pointer set at startup in m_main. */ + ( * VG_(address_of_m_main_shutdown_actions_NORETURN) ) (tid, src); + + } else { + + ThreadState *tst; + mach_msg_header_t msg; + + VG_(debugLog)(1, "syswrap-darwin", + "run_a_thread_NORETURN(tid=%lld): " + "not last one standing\n", + (ULong)tidW); + + /* OK, thread is dead, but others still exist. Just exit. */ + tst = VG_(get_ThreadState)(tid); + + /* This releases the run lock */ + VG_(exit_thread)(tid); + vg_assert(tst->status == VgTs_Zombie); + + /* tid is now invalid. */ + + // GrP fixme exit race + msg.msgh_bits = MACH_MSGH_BITS(17, MACH_MSG_TYPE_MAKE_SEND_ONCE); + msg.msgh_request_port = VG_(gettid)(); + msg.msgh_reply_port = 0; + msg.msgh_id = 3600; // thread_terminate + + tst->status = VgTs_Empty; + // GrP fixme race here! new thread may claim this V thread stack + // before we get out here! + // GrP fixme use bsdthread_terminate for safe cleanup? + mach_msg(&msg, MACH_SEND_MSG|MACH_MSG_OPTION_NONE, + sizeof(msg), 0, 0, MACH_MSG_TIMEOUT_NONE, 0); + + // DDD: This is reached sometimes on none/tests/manythreads, maybe + // because of the race above. + VG_(core_panic)("Thread exit failed?\n"); + } + + /*NOTREACHED*/ + vg_assert(0); +} + + +/* Allocate a stack for the main thread, and run it all the way to the + end. Although we already have a working VgStack + (VG_(interim_stack)) it's better to allocate a new one, so that + overflow detection works uniformly for all threads. +*/ +void VG_(main_thread_wrapper_NORETURN)(ThreadId tid) +{ + Addr sp; + VG_(debugLog)(1, "syswrap-darwin", + "entering VG_(main_thread_wrapper_NORETURN)\n"); + + sp = allocstack(tid); + + /* If we can't even allocate the first thread's stack, we're hosed. + Give up. */ + vg_assert2(sp != 0, "Cannot allocate main thread's stack."); + + /* shouldn't be any other threads around yet */ + vg_assert( VG_(count_living_threads)() == 1 ); + + call_on_new_stack_0_1( + (Addr)sp, /* stack */ + 0, /*bogus return address*/ + run_a_thread_NORETURN, /* fn to call */ + (Word)tid /* arg to give it */ + ); + + /*NOTREACHED*/ + vg_assert(0); +} + + +void start_thread_NORETURN ( Word arg ) +{ + ThreadState* tst = (ThreadState*)arg; + ThreadId tid = tst->tid; + + run_a_thread_NORETURN ( (Word)tid ); + /*NOTREACHED*/ + vg_assert(0); +} + + +void VG_(cleanup_thread) ( ThreadArchState* arch ) +{ +} + + +/* --------------------------------------------------------------------- + Mach port tracking (based on syswrap-generic's fd tracker) + ------------------------------------------------------------------ */ + +/* One of these is allocated for each open port. */ +typedef struct OpenPort +{ + mach_port_t port; + mach_port_type_t type; /* right type(s) */ + Int send_count; /* number of send rights */ + Char *name; /* bootstrap name or NULL */ + ExeContext *where; /* first allocation only */ + struct OpenPort *next, *prev; +} OpenPort; + +// strlen("0x12345678") +#define PORT_STRLEN (2+2*sizeof(mach_port_t)) + +/* List of allocated ports. */ +static OpenPort *allocated_ports; + +/* Count of open ports. */ +static Int allocated_port_count = 0; + + +__attribute__((unused)) +static Bool port_exists(mach_port_t port) +{ + OpenPort *i; + + /* Check to see if this port is already open. */ + i = allocated_ports; + while (i) { + if (i->port == port) { + return True; + } + i = i->next; + } + + return False; +} + +static OpenPort *info_for_port(mach_port_t port) +{ + OpenPort *i; + if (!port) return NULL; + + i = allocated_ports; + while (i) { + if (i->port == port) { + return i; + } + i = i->next; + } + + return NULL; +} + + +// Give a port a name, without changing its refcount +// GrP fixme don't override name if it already has a specific one +__private_extern__ void assign_port_name(mach_port_t port, const char *name) +{ + OpenPort *i; + if (!port) return; + vg_assert(name); + + i = info_for_port(port); + vg_assert(i); + + if (i->name) VG_(arena_free)(VG_AR_CORE, i->name); + i->name = + VG_(arena_malloc)(VG_AR_CORE, "syswrap-darwin.mach-port-name", + VG_(strlen)(name) + PORT_STRLEN + 1); + VG_(sprintf)(i->name, name, port); +} + + +// Return the name of the given port or "UNKNOWN 0x1234" if not known. +static const char *name_for_port(mach_port_t port) +{ + static char buf[8 + PORT_STRLEN + 1]; + OpenPort *i; + + // hack + if (port == VG_(gettid)()) return "mach_thread_self()"; + if (port == 0) return "NULL"; + + i = allocated_ports; + while (i) { + if (i->port == port) { + return i->name; + } + i = i->next; + } + + VG_(sprintf)(buf, "NONPORT-%#x", port); + return buf; +} + +/* Note the fact that a port was just deallocated. */ + +static +void record_port_mod_refs(mach_port_t port, mach_port_type_t right, Int delta) +{ + OpenPort *i = allocated_ports; + if (!port) return; + + while(i) { + if(i->port == port) { + vg_assert(right != MACH_PORT_TYPE_DEAD_NAME); + if (right & MACH_PORT_TYPE_SEND) { + // send rights are refcounted + if (delta == INT_MIN) delta = -i->send_count; // INT_MIN == destroy + i->send_count += delta; + if (i->send_count > 0) i->type |= MACH_PORT_TYPE_SEND; + else i->type &= ~MACH_PORT_TYPE_SEND; + } + right = right & ~MACH_PORT_TYPE_SEND; + if (right) { + // other rights are not refcounted + if (delta > 0) { + i->type |= right; + } else if (delta < 0) { + i->type &= ~right; + } + } + + if (i->type != 0) return; + + // Port has no rights left. Kill it. + // VG_(printf)("deleting port %p %s", i->port, i->name); + if(i->prev) + i->prev->next = i->next; + else + allocated_ports = i->next; + if(i->next) + i->next->prev = i->prev; + if(i->name) + VG_(arena_free) (VG_AR_CORE, i->name); + VG_(arena_free) (VG_AR_CORE, i); + allocated_port_count--; + return; + } + i = i->next; + } + + VG_(printf)("UNKNOWN Mach port modified (port %#x delta %d)\n", port, delta); +} + +static +void record_port_insert_rights(mach_port_t port, mach_msg_type_name_t type) +{ + switch (type) { + case MACH_MSG_TYPE_PORT_NAME: + // this task has no rights for the name + break; + case MACH_MSG_TYPE_PORT_RECEIVE: + // this task gets receive rights + record_port_mod_refs(port, MACH_PORT_TYPE_RECEIVE, 1); + break; + case MACH_MSG_TYPE_PORT_SEND: + // this task gets a send right + record_port_mod_refs(port, MACH_PORT_TYPE_SEND, 1); + break; + case MACH_MSG_TYPE_PORT_SEND_ONCE: + // this task gets send-once rights + record_port_mod_refs(port, MACH_PORT_TYPE_SEND_ONCE, 1); + break; + default: + vg_assert(0); + break; + } +} + +static +void record_port_dealloc(mach_port_t port) +{ + // deletes 1 send or send-once right (port can't have both) + record_port_mod_refs(port, MACH_PORT_TYPE_SEND_RIGHTS, -1); +} + +static +void record_port_destroy(mach_port_t port) +{ + // deletes all rights to port + record_port_mod_refs(port, MACH_PORT_TYPE_ALL_RIGHTS, INT_MIN); +} + + +/* Note the fact that a Mach port was just allocated or transferred. + If the port is already known, increment its reference count. */ +void record_named_port(ThreadId tid, mach_port_t port, + mach_port_right_t right, const char *name) +{ + OpenPort *i; + if (!port) return; + + /* Check to see if this port is already open. */ + i = allocated_ports; + while (i) { + if (i->port == port) { + if (right != -1) record_port_mod_refs(port, MACH_PORT_TYPE(right), 1); + return; + } + i = i->next; + } + + /* Not already one: allocate an OpenPort */ + if (i == NULL) { + i = VG_(arena_malloc)(VG_AR_CORE, "syswrap-darwin.mach-port", + sizeof(OpenPort)); + + i->prev = NULL; + i->next = allocated_ports; + if(allocated_ports) allocated_ports->prev = i; + allocated_ports = i; + allocated_port_count++; + + i->port = port; + i->where = (tid == -1) ? NULL : VG_(record_ExeContext)(tid, 0); + i->name = NULL; + if (right != -1) { + i->type = MACH_PORT_TYPE(right); + i->send_count = (right == MACH_PORT_RIGHT_SEND) ? 1 : 0; + } else { + i->type = 0; + i->send_count = 0; + } + + assign_port_name(port, name); + } +} + + +// Record opening of a nameless port. +static void record_unnamed_port(ThreadId tid, mach_port_t port, mach_port_right_t right) +{ + record_named_port(tid, port, right, "unnamed-%p"); +} + + +/* Dump summary of open Mach ports, like VG_(show_open_fds) */ +void VG_(show_open_ports)(void) +{ + OpenPort *i; + + VG_(message)(Vg_UserMsg, + "MACH PORTS: %d open at exit.", allocated_port_count); + + for (i = allocated_ports; i; i = i->next) { + if (i->name) { + VG_(message)(Vg_UserMsg, "Open Mach port 0x%x: %s", i->port, i->name); + } else { + VG_(message)(Vg_UserMsg, "Open Mach port 0x%x", i->port); + } + + if (i->where) { + VG_(pp_ExeContext)(i->where); + VG_(message)(Vg_UserMsg, ""); + } + } + + VG_(message)(Vg_UserMsg, ""); +} + + +/* --------------------------------------------------------------------- + sync_mappings + ------------------------------------------------------------------ */ + +static void sync_mappings(const HChar *when, const HChar *where, Int num) +{ + // I haven't seen more than 1 segment be added or removed in a single calls + // to sync_mappings(). So 20 seems generous. The upper bound is the + // number of segments currently in use. --njn + #define CSS_SIZE 20 + ChangedSeg css[CSS_SIZE]; + Int css_used; + Int i; + + VG_(get_changed_segments)(when, where, css, CSS_SIZE, &css_used); + + // Now add/remove them. + for (i = 0; i < css_used; i++) { + ChangedSeg* cs = &css[i]; + Char* action; + if (cs->is_added) { + ML_(notify_core_and_tool_of_mmap)( + cs->start, cs->end - cs->start + 1, + cs->prot, VKI_MAP_PRIVATE, 0, cs->offset); + // should this call VG_(di_notify_mmap) also? + action = "added"; + + } else { + ML_(notify_core_and_tool_of_munmap)( + cs->start, cs->end - cs->start + 1); + action = "removed"; + } + if (VG_(clo_trace_syscalls)) { + VG_(debugLog)(0, "aspacem", + "\n%s region 0x%010lx..0x%010lx at %s (%s)\n", + action, cs->start, cs->end + 1, where, when); + } + } +} + +/* --------------------------------------------------------------------- + wrappers + ------------------------------------------------------------------ */ + +#define PRE(name) DEFN_PRE_TEMPLATE(darwin, name) +#define POST(name) DEFN_POST_TEMPLATE(darwin, name) + +#define PRE_FN(name) vgSysWrap_darwin_##name##_before +#define POST_FN(name) vgSysWrap_darwin_##name##_after + +#define CALL_PRE(name) PRE_FN(name)(tid, layout, arrghs, status, flags) +#define CALL_POST(name) POST_FN(name)(tid, arrghs, status) + +#if VG_WORDSIZE == 4 +// Combine two 32-bit values into a 64-bit value +// Always use with low-numbered arg first (e.g. LOHI64(ARG1,ARG2) ) +# if defined(VGA_x86) +# define LOHI64(lo,hi) ( (lo) | ((ULong)(hi) << 32) ) +# else +# error unknown architecture +# endif +#endif + +// Retrieve the current Mach thread +#define MACH_THREAD ((Addr)VG_(get_ThreadState)(tid)->os_state.lwpid) + +// Set the POST handler for a mach_msg derivative +#define AFTER VG_(get_ThreadState)(tid)->os_state.post_mach_trap_fn + +// Set or get values saved from Mach messages +#define MACH_ARG(x) VG_(get_ThreadState)(tid)->os_state.mach_args.x +#define MACH_REMOTE VG_(get_ThreadState)(tid)->os_state.remote_port +#define MACH_MSGH_ID VG_(get_ThreadState)(tid)->os_state.msgh_id + +/* --------------------------------------------------------------------- + darwin ioctl wrapper + ------------------------------------------------------------------ */ + +PRE(sys_ioctl) +{ + *flags |= SfMayBlock; + PRINT("sys_ioctl ( %ld, 0x%lx, %#lx )",ARG1,ARG2,ARG3); + PRE_REG_READ3(long, "ioctl", + unsigned int, fd, unsigned int, request, unsigned long, arg); + + switch (ARG2 /* request */) { + case VKI_TIOCGWINSZ: + PRE_MEM_WRITE( "ioctl(TIOCGWINSZ)", ARG3, sizeof(struct vki_winsize) ); + break; + case VKI_TIOCSWINSZ: + PRE_MEM_READ( "ioctl(TIOCSWINSZ)", ARG3, sizeof(struct vki_winsize) ); + break; + case VKI_TIOCMBIS: + PRE_MEM_READ( "ioctl(TIOCMBIS)", ARG3, sizeof(unsigned int) ); + break; + case VKI_TIOCMBIC: + PRE_MEM_READ( "ioctl(TIOCMBIC)", ARG3, sizeof(unsigned int) ); + break; + case VKI_TIOCMSET: + PRE_MEM_READ( "ioctl(TIOCMSET)", ARG3, sizeof(unsigned int) ); + break; + case VKI_TIOCMGET: + PRE_MEM_WRITE( "ioctl(TIOCMGET)", ARG3, sizeof(unsigned int) ); + break; + case VKI_TIOCGPGRP: + /* Get process group ID for foreground processing group. */ + PRE_MEM_WRITE( "ioctl(TIOCGPGRP)", ARG3, sizeof(vki_pid_t) ); + break; + case VKI_TIOCSPGRP: + /* Set a process group ID? */ + PRE_MEM_WRITE( "ioctl(TIOCGPGRP)", ARG3, sizeof(vki_pid_t) ); + break; + case VKI_TIOCSCTTY: + /* Just takes an int value. */ + break; + case VKI_FIONBIO: + PRE_MEM_READ( "ioctl(FIONBIO)", ARG3, sizeof(int) ); + break; + case VKI_FIOASYNC: + PRE_MEM_READ( "ioctl(FIOASYNC)", ARG3, sizeof(int) ); + break; + case VKI_FIONREAD: /* identical to SIOCINQ */ + PRE_MEM_WRITE( "ioctl(FIONREAD)", ARG3, sizeof(int) ); + break; + + + /* These all use struct ifreq AFAIK */ + /* GrP fixme is sizeof(struct vki_if_req) correct if it's using a sockaddr? */ + case VKI_SIOCGIFFLAGS: /* get flags */ + PRE_MEM_RASCIIZ( "ioctl(SIOCGIFFLAGS)", + (Addr)((struct vki_ifreq *)ARG3)->vki_ifr_name ); + PRE_MEM_WRITE( "ioctl(SIOCGIFFLAGS)", ARG3, sizeof(struct vki_ifreq)); + break; + case VKI_SIOCGIFMTU: /* get MTU size */ + PRE_MEM_RASCIIZ( "ioctl(SIOCGIFMTU)", + (Addr)((struct vki_ifreq *)ARG3)->vki_ifr_name ); + PRE_MEM_WRITE( "ioctl(SIOCGIFMTU)", ARG3, sizeof(struct vki_ifreq)); + break; + case VKI_SIOCGIFADDR: /* get PA address */ + PRE_MEM_RASCIIZ( "ioctl(SIOCGIFADDR)", + (Addr)((struct vki_ifreq *)ARG3)->vki_ifr_name ); + PRE_MEM_WRITE( "ioctl(SIOCGIFADDR)", ARG3, sizeof(struct vki_ifreq)); + break; + case VKI_SIOCGIFNETMASK: /* get network PA mask */ + PRE_MEM_RASCIIZ( "ioctl(SIOCGIFNETMASK)", + (Addr)((struct vki_ifreq *)ARG3)->vki_ifr_name ); + PRE_MEM_WRITE( "ioctl(SIOCGIFNETMASK)", ARG3, sizeof(struct vki_ifreq)); + break; + case VKI_SIOCGIFMETRIC: /* get metric */ + PRE_MEM_RASCIIZ( "ioctl(SIOCGIFMETRIC)", + (Addr)((struct vki_ifreq *)ARG3)->vki_ifr_name ); + PRE_MEM_WRITE( "ioctl(SIOCGIFMETRIC)", ARG3, sizeof(struct vki_ifreq)); + break; + case VKI_SIOCGIFDSTADDR: /* get remote PA address */ + PRE_MEM_RASCIIZ( "ioctl(SIOCGIFDSTADDR)", + (Addr)((struct vki_ifreq *)ARG3)->vki_ifr_name ); + PRE_MEM_WRITE( "ioctl(SIOCGIFDSTADDR)", ARG3, sizeof(struct vki_ifreq)); + break; + case VKI_SIOCGIFBRDADDR: /* get broadcast PA address */ + PRE_MEM_RASCIIZ( "ioctl(SIOCGIFBRDADDR)", + (Addr)((struct vki_ifreq *)ARG3)->vki_ifr_name ); + PRE_MEM_WRITE( "ioctl(SIOCGIFBRDADDR)", ARG3, sizeof(struct vki_ifreq)); + break; + case VKI_SIOCGIFCONF: /* get iface list */ + /* WAS: + PRE_MEM_WRITE( "ioctl(SIOCGIFCONF)", ARG3, sizeof(struct ifconf)); + KERNEL_DO_SYSCALL(tid,RES); + if (!VG_(is_kerror)(RES) && RES == 0) + POST_MEM_WRITE(ARG3, sizeof(struct ifconf)); + */ + PRE_MEM_READ( "ioctl(SIOCGIFCONF)", + (Addr)&((struct vki_ifconf *)ARG3)->ifc_len, + sizeof(((struct vki_ifconf *)ARG3)->ifc_len)); + PRE_MEM_READ( "ioctl(SIOCGIFCONF)", + (Addr)&((struct vki_ifconf *)ARG3)->vki_ifc_buf, + sizeof(((struct vki_ifconf *)ARG3)->vki_ifc_buf)); + if ( ARG3 ) { + // TODO len must be readable and writable + // buf pointer only needs to be readable + struct vki_ifconf *ifc = (struct vki_ifconf *) ARG3; + PRE_MEM_WRITE( "ioctl(SIOCGIFCONF).ifc_buf", + (Addr)(ifc->vki_ifc_buf), ifc->ifc_len ); + } + break; + + case VKI_SIOCSIFFLAGS: /* set flags */ + PRE_MEM_RASCIIZ( "ioctl(SIOCSIFFLAGS)", + (Addr)((struct vki_ifreq *)ARG3)->vki_ifr_name ); + PRE_MEM_READ( "ioctl(SIOCSIFFLAGS)", + (Addr)&((struct vki_ifreq *)ARG3)->vki_ifr_flags, + sizeof(((struct vki_ifreq *)ARG3)->vki_ifr_flags) ); + break; + case VKI_SIOCSIFADDR: /* set PA address */ + case VKI_SIOCSIFDSTADDR: /* set remote PA address */ + case VKI_SIOCSIFBRDADDR: /* set broadcast PA address */ + case VKI_SIOCSIFNETMASK: /* set network PA mask */ + PRE_MEM_RASCIIZ( "ioctl(SIOCSIF*ADDR)", + (Addr)((struct vki_ifreq *)ARG3)->vki_ifr_name ); + PRE_MEM_READ( "ioctl(SIOCSIF*ADDR)", + (Addr)&((struct vki_ifreq *)ARG3)->ifr_addr, + sizeof(((struct vki_ifreq *)ARG3)->ifr_addr) ); + break; + case VKI_SIOCSIFMETRIC: /* set metric */ + PRE_MEM_RASCIIZ( "ioctl(SIOCSIFMETRIC)", + (Addr)((struct vki_ifreq *)ARG3)->vki_ifr_name ); + PRE_MEM_READ( "ioctl(SIOCSIFMETRIC)", + (Addr)&((struct vki_ifreq *)ARG3)->vki_ifr_metric, + sizeof(((struct vki_ifreq *)ARG3)->vki_ifr_metric) ); + break; + case VKI_SIOCSIFMTU: /* set MTU size */ + PRE_MEM_RASCIIZ( "ioctl(SIOCSIFMTU)", + (Addr)((struct vki_ifreq *)ARG3)->vki_ifr_name ); + PRE_MEM_READ( "ioctl(SIOCSIFMTU)", + (Addr)&((struct vki_ifreq *)ARG3)->vki_ifr_mtu, + sizeof(((struct vki_ifreq *)ARG3)->vki_ifr_mtu) ); + break; + /* Routing table calls. */ +#ifdef VKI_SIOCADDRT + case VKI_SIOCADDRT: /* add routing table entry */ + case VKI_SIOCDELRT: /* delete routing table entry */ + PRE_MEM_READ( "ioctl(SIOCADDRT/DELRT)", ARG3, + sizeof(struct vki_rtentry)); + break; +#endif + + case VKI_SIOCGPGRP: + PRE_MEM_WRITE( "ioctl(SIOCGPGRP)", ARG3, sizeof(int) ); + break; + case VKI_SIOCSPGRP: + PRE_MEM_READ( "ioctl(SIOCSPGRP)", ARG3, sizeof(int) ); + //tst->sys_flags &= ~SfMayBlock; + break; + + case VKI_FIODTYPE: + PRE_MEM_WRITE( "ioctl(FIONREAD)", ARG3, sizeof(int) ); + break; + + case VKI_DTRACEHIOC_REMOVE: + case VKI_DTRACEHIOC_ADDDOF: + break; + + // ttycom.h + case VKI_TIOCGETA: + PRE_MEM_WRITE( "ioctl(TIOCGETA)", ARG3, sizeof(struct vki_termios) ); + break; + case VKI_TIOCSETA: + PRE_MEM_READ( "ioctl(TIOCSETA)", ARG3, sizeof(struct vki_termios) ); + break; + case VKI_TIOCGETD: + PRE_MEM_WRITE( "ioctl(TIOCGETD)", ARG3, sizeof(int) ); + break; + case VKI_TIOCSETD: + PRE_MEM_READ( "ioctl(TIOCSETD)", ARG3, sizeof(int) ); + break; + case VKI_TIOCPTYGNAME: + PRE_MEM_WRITE( "ioctl(TIOCPTYGNAME)", ARG3, 128 ); + break; + case VKI_TIOCPTYGRANT: + case VKI_TIOCPTYUNLK: + break; + + default: + ML_(PRE_unknown_ioctl)(tid, ARG2, ARG3); + break; + } +} + + +POST(sys_ioctl) +{ + vg_assert(SUCCESS); + switch (ARG2 /* request */) { + case VKI_TIOCGWINSZ: + POST_MEM_WRITE( ARG3, sizeof(struct vki_winsize) ); + break; + case VKI_TIOCSWINSZ: + case VKI_TIOCMBIS: + case VKI_TIOCMBIC: + case VKI_TIOCMSET: + break; + case VKI_TIOCMGET: + POST_MEM_WRITE( ARG3, sizeof(unsigned int) ); + break; + case VKI_TIOCGPGRP: + /* Get process group ID for foreground processing group. */ + POST_MEM_WRITE( ARG3, sizeof(vki_pid_t) ); + break; + case VKI_TIOCSPGRP: + /* Set a process group ID? */ + POST_MEM_WRITE( ARG3, sizeof(vki_pid_t) ); + break; + case VKI_TIOCSCTTY: + break; + case VKI_FIONBIO: + break; + case VKI_FIOASYNC: + break; + case VKI_FIONREAD: /* identical to SIOCINQ */ + POST_MEM_WRITE( ARG3, sizeof(int) ); + break; + + /* These all use struct ifreq AFAIK */ + case VKI_SIOCGIFFLAGS: /* get flags */ + POST_MEM_WRITE( (Addr)&((struct vki_ifreq *)ARG3)->vki_ifr_flags, + sizeof(((struct vki_ifreq *)ARG3)->vki_ifr_flags) ); + break; + case VKI_SIOCGIFMTU: /* get MTU size */ + POST_MEM_WRITE( (Addr)&((struct vki_ifreq *)ARG3)->vki_ifr_mtu, + sizeof(((struct vki_ifreq *)ARG3)->vki_ifr_mtu) ); + break; + case VKI_SIOCGIFADDR: /* get PA address */ + case VKI_SIOCGIFDSTADDR: /* get remote PA address */ + case VKI_SIOCGIFBRDADDR: /* get broadcast PA address */ + case VKI_SIOCGIFNETMASK: /* get network PA mask */ + POST_MEM_WRITE( + (Addr)&((struct vki_ifreq *)ARG3)->ifr_addr, + sizeof(((struct vki_ifreq *)ARG3)->ifr_addr) ); + break; + case VKI_SIOCGIFMETRIC: /* get metric */ + POST_MEM_WRITE( + (Addr)&((struct vki_ifreq *)ARG3)->vki_ifr_metric, + sizeof(((struct vki_ifreq *)ARG3)->vki_ifr_metric) ); + break; + case VKI_SIOCGIFCONF: /* get iface list */ + /* WAS: + PRE_MEM_WRITE("ioctl(SIOCGIFCONF)", ARG3, sizeof(struct ifconf)); + KERNEL_DO_SYSCALL(tid,RES); + if (!VG_(is_kerror)(RES) && RES == 0) + POST_MEM_WRITE(ARG3, sizeof(struct ifconf)); + */ + if (RES == 0 && ARG3 ) { + struct vki_ifconf *ifc = (struct vki_ifconf *) ARG3; + if (ifc->vki_ifc_buf != NULL) + POST_MEM_WRITE( (Addr)(ifc->vki_ifc_buf), ifc->ifc_len ); + } + break; + + case VKI_SIOCSIFFLAGS: /* set flags */ + case VKI_SIOCSIFDSTADDR: /* set remote PA address */ + case VKI_SIOCSIFBRDADDR: /* set broadcast PA address */ + case VKI_SIOCSIFNETMASK: /* set network PA mask */ + case VKI_SIOCSIFMETRIC: /* set metric */ + case VKI_SIOCSIFADDR: /* set PA address */ + case VKI_SIOCSIFMTU: /* set MTU size */ + break; + +#ifdef VKI_SIOCADDRT + /* Routing table calls. */ + case VKI_SIOCADDRT: /* add routing table entry */ + case VKI_SIOCDELRT: /* delete routing table entry */ + break; +#endif + + case VKI_SIOCGPGRP: + POST_MEM_WRITE(ARG3, sizeof(int)); + break; + case VKI_SIOCSPGRP: + break; + + case VKI_FIODTYPE: + POST_MEM_WRITE( ARG3, sizeof(int) ); + break; + + case VKI_DTRACEHIOC_REMOVE: + case VKI_DTRACEHIOC_ADDDOF: + break; + + // ttycom.h + case VKI_TIOCGETA: + POST_MEM_WRITE( ARG3, sizeof(struct vki_termios)); + break; + case VKI_TIOCSETA: + break; + case VKI_TIOCGETD: + POST_MEM_WRITE( ARG3, sizeof(int) ); + break; + case VKI_TIOCSETD: + break; + case VKI_TIOCPTYGNAME: + POST_MEM_WRITE( ARG3, 128); + break; + case VKI_TIOCPTYGRANT: + case VKI_TIOCPTYUNLK: + break; + + default: + break; + } +} + + +/* --------------------------------------------------------------------- + darwin fcntl wrapper + ------------------------------------------------------------------ */ +static const char *name_for_fcntl(UWord cmd) { +#define F(n) case VKI_##n: return #n + switch (cmd) { + F(F_CHKCLEAN); + F(F_RDAHEAD); + F(F_NOCACHE); + F(F_FULLFSYNC); + F(F_FREEZE_FS); + F(F_THAW_FS); + F(F_GLOBAL_NOCACHE); + F(F_PREALLOCATE); + F(F_SETSIZE); + F(F_RDADVISE); + F(F_READBOOTSTRAP); + F(F_WRITEBOOTSTRAP); + F(F_LOG2PHYS); + F(F_GETPATH); + F(F_PATHPKG_CHECK); + F(F_ADDSIGS); + default: + return "UNKNOWN"; + } +#undef F +} + +PRE(sys_fcntl) +{ + switch (ARG2) { + // These ones ignore ARG3. + case VKI_F_GETFD: + case VKI_F_GETFL: + case VKI_F_GETOWN: + PRINT("sys_fcntl ( %ld, %ld )", ARG1,ARG2); + PRE_REG_READ2(long, "fcntl", unsigned int, fd, unsigned int, cmd); + break; + + // These ones use ARG3 as "arg". + case VKI_F_DUPFD: + case VKI_F_SETFD: + case VKI_F_SETFL: + case VKI_F_SETOWN: + PRINT("sys_fcntl[ARG3=='arg'] ( %ld, %ld, %ld )", ARG1,ARG2,ARG3); + PRE_REG_READ3(long, "fcntl", + unsigned int, fd, unsigned int, cmd, unsigned long, arg); + break; + + // These ones use ARG3 as "lock". + case VKI_F_GETLK: + case VKI_F_SETLK: + case VKI_F_SETLKW: + PRINT("sys_fcntl[ARG3=='lock'] ( %ld, %ld, %#lx )", ARG1,ARG2,ARG3); + PRE_REG_READ3(long, "fcntl", + unsigned int, fd, unsigned int, cmd, + struct flock64 *, lock); + // GrP fixme mem read sizeof(flock64) + if (ARG2 == VKI_F_SETLKW) + *flags |= SfMayBlock; + break; + + // none + case VKI_F_CHKCLEAN: + case VKI_F_RDAHEAD: + case VKI_F_NOCACHE: + case VKI_F_FULLFSYNC: + case VKI_F_FREEZE_FS: + case VKI_F_THAW_FS: + case VKI_F_GLOBAL_NOCACHE: + PRINT("sys_fcntl ( %ld, %s )", ARG1, name_for_fcntl(ARG1)); + PRE_REG_READ2(long, "fcntl", unsigned int, fd, unsigned int, cmd); + break; + + // struct fstore + case VKI_F_PREALLOCATE: + PRINT("sys_fcntl ( %ld, %s, %#lx )", ARG1, name_for_fcntl(ARG2), ARG3); + PRE_REG_READ3(long, "fcntl", + unsigned int, fd, unsigned int, cmd, + struct fstore *, fstore); + { + struct vki_fstore *fstore = (struct vki_fstore *)ARG3; + PRE_FIELD_READ( "fcntl(F_PREALLOCATE, fstore->fst_flags)", + fstore->fst_flags ); + PRE_FIELD_READ( "fcntl(F_PREALLOCATE, fstore->fst_flags)", + fstore->fst_posmode ); + PRE_FIELD_READ( "fcntl(F_PREALLOCATE, fstore->fst_flags)", + fstore->fst_offset ); + PRE_FIELD_READ( "fcntl(F_PREALLOCATE, fstore->fst_flags)", + fstore->fst_length ); + PRE_FIELD_WRITE( "fcntl(F_PREALLOCATE, fstore->fst_bytesalloc)", + fstore->fst_bytesalloc); + } + break; + + // off_t + case VKI_F_SETSIZE: + PRINT("sys_fcntl ( %ld, %s, %#lx )", ARG1, name_for_fcntl(ARG2), ARG3); + PRE_REG_READ3(long, "fcntl", + unsigned int, fd, unsigned int, cmd, + vki_off_t *, offset); + break; + + // struct radvisory + case VKI_F_RDADVISE: + PRINT("sys_fcntl ( %ld, %s, %#lx )", ARG1, name_for_fcntl(ARG2), ARG3); + PRE_REG_READ3(long, "fcntl", + unsigned int, fd, unsigned int, cmd, + struct vki_radvisory *, radvisory); + { + struct vki_radvisory *radvisory = (struct vki_radvisory *)ARG3; + PRE_FIELD_READ( "fcntl(F_PREALLOCATE, radvisory->ra_offset)", + radvisory->ra_offset ); + PRE_FIELD_READ( "fcntl(F_PREALLOCATE, radvisory->ra_count)", + radvisory->ra_count ); + } + break; + + // struct fbootstraptransfer + case VKI_F_READBOOTSTRAP: + case VKI_F_WRITEBOOTSTRAP: + PRINT("sys_fcntl ( %ld, %s, %#lx )", ARG1, name_for_fcntl(ARG2), ARG3); + PRE_REG_READ3(long, "fcntl", + unsigned int, fd, unsigned int, cmd, + struct fbootstraptransfer *, bootstrap); + PRE_MEM_READ( "fcntl(F_READ/WRITEBOOTSTRAP, bootstrap)", + ARG3, sizeof(struct vki_fbootstraptransfer) ); + break; + + // struct log2phys (out) + case VKI_F_LOG2PHYS: + PRINT("sys_fcntl ( %ld, %s, %#lx )", ARG1, name_for_fcntl(ARG2), ARG3); + PRE_REG_READ3(long, "fcntl", + unsigned int, fd, unsigned int, cmd, + struct log2phys *, l2p); + PRE_MEM_WRITE( "fcntl(F_LOG2PHYS, l2p)", + ARG3, sizeof(struct vki_log2phys) ); + break; + + // char[maxpathlen] (out) + case VKI_F_GETPATH: + PRINT("sys_fcntl ( %ld, %s, %#lx )", ARG1, name_for_fcntl(ARG2), ARG3); + PRE_REG_READ3(long, "fcntl", + unsigned int, fd, unsigned int, cmd, + char *, pathbuf); + PRE_MEM_WRITE( "fcntl(F_GETPATH, pathbuf)", + ARG3, VKI_MAXPATHLEN ); + break; + + // char[maxpathlen] (in) + case VKI_F_PATHPKG_CHECK: + PRINT("sys_fcntl ( %ld, %s, %#lx '%s')", ARG1, name_for_fcntl(ARG2), ARG3, + (char *)ARG3); + PRE_REG_READ3(long, "fcntl", + unsigned int, fd, unsigned int, cmd, + char *, pathbuf); + PRE_MEM_RASCIIZ( "fcntl(F_PATHPKG_CHECK, pathbuf)", ARG3); + break; + + case VKI_F_ADDSIGS: /* Add detached signatures (for code signing) */ + PRINT("sys_fcntl ( %ld, %s )", ARG1, name_for_fcntl(ARG2)); + PRE_REG_READ3(long, "fcntl", + unsigned int, fd, unsigned int, cmd, + vki_fsignatures_t *, sigs); + + { + vki_fsignatures_t *fsigs = (vki_fsignatures_t*)ARG3; + PRE_FIELD_READ( "fcntl(F_ADDSIGS, fsigs->fs_blob_start)", + fsigs->fs_blob_start); + PRE_FIELD_READ( "fcntl(F_ADDSIGS, fsigs->fs_blob_size)", + fsigs->fs_blob_size); + + if (fsigs->fs_blob_start) + PRE_MEM_READ( "fcntl(F_ADDSIGS, fsigs->fs_blob_start)", + (Addr)fsigs->fs_blob_start, fsigs->fs_blob_size); + } + break; + + default: + PRINT("sys_fcntl ( %ld, %ld [??] )", ARG1, ARG2); + VG_(printf)("UNKNOWN fcntl %ld!", ARG2); + break; + } +} + +POST(sys_fcntl) +{ + vg_assert(SUCCESS); + switch (ARG2) { + case VKI_F_DUPFD: + if (!ML_(fd_allowed)(RES, "fcntl(DUPFD)", tid, True)) { + VG_(close)(RES); + SET_STATUS_Failure( VKI_EMFILE ); + } else { + if (VG_(clo_track_fds)) + ML_(record_fd_open_named)(tid, RES); + } + break; + + case VKI_F_GETFD: + case VKI_F_GETFL: + case VKI_F_GETOWN: + case VKI_F_SETFD: + case VKI_F_SETFL: + case VKI_F_SETOWN: + case VKI_F_GETLK: + case VKI_F_SETLK: + case VKI_F_SETLKW: + break; + + case VKI_F_PREALLOCATE: + { + struct vki_fstore *fstore = (struct vki_fstore *)ARG3; + POST_FIELD_WRITE( fstore->fst_bytesalloc ); + } + break; + + case VKI_F_LOG2PHYS: + POST_MEM_WRITE( ARG3, sizeof(struct vki_log2phys) ); + break; + + case VKI_F_GETPATH: + POST_MEM_WRITE( ARG3, 1+VG_(strlen)((char *)ARG3) ); + PRINT("\"%s\"", (char*)ARG3); + break; + + default: + // DDD: ugh, missing lots of cases here, not nice + break; + } +} + +// XXX: wrapper only suitable for 32-bit systems +PRE(sys_fcntl64) +{ + switch (ARG2) { + // These ones ignore ARG3. + case VKI_F_GETFD: + case VKI_F_GETFL: + case VKI_F_GETOWN: + PRINT("sys_fcntl64 ( %ld, %ld )", ARG1,ARG2); + PRE_REG_READ2(long, "fcntl64", unsigned int, fd, unsigned int, cmd); + break; + + // These ones use ARG3 as "arg". + case VKI_F_DUPFD: + case VKI_F_SETFD: + case VKI_F_SETFL: + case VKI_F_SETOWN: + PRINT("sys_fcntl64[ARG3=='arg'] ( %ld, %ld, %ld )", ARG1,ARG2,ARG3); + PRE_REG_READ3(long, "fcntl64", + unsigned int, fd, unsigned int, cmd, unsigned long, arg); + break; + + // These ones use ARG3 as "lock". + case VKI_F_GETLK: + case VKI_F_SETLK: + case VKI_F_SETLKW: + PRINT("sys_fcntl64[ARG3=='lock'] ( %ld, %ld, %#lx )", ARG1,ARG2,ARG3); + PRE_REG_READ3(long, "fcntl64", + unsigned int, fd, unsigned int, cmd, + struct flock64 *, lock); + if (ARG2 == VKI_F_SETLKW) + *flags |= SfMayBlock; + break; + + default: + I_die_here; // DDD: do something better here + break; + } +} + +POST(sys_fcntl64) +{ + vg_assert(SUCCESS); + switch (ARG2) { + case VKI_F_DUPFD: + if (!ML_(fd_allowed)(RES, "fcntl64(DUPFD)", tid, True)) { + VG_(close)(RES); + SET_STATUS_Failure( VKI_EMFILE ); + } else { + if (VG_(clo_track_fds)) + ML_(record_fd_open_named)(tid, RES); + } + break; + + case VKI_F_GETFD: + case VKI_F_GETFL: + case VKI_F_GETOWN: + case VKI_F_SETFD: + case VKI_F_SETFL: + case VKI_F_SETOWN: + case VKI_F_GETLK: + case VKI_F_SETLK: + case VKI_F_SETLKW: + break; + + default: + I_die_here; // DDD: do something better here + break; + } +} + +/* --------------------------------------------------------------------- + unix syscalls + ------------------------------------------------------------------ */ + +PRE(sys_futimes) +{ + PRINT("sys_futimes ( %ld, %#lx )", ARG1,ARG2); + PRE_REG_READ2(long, "futimes", int, fd, struct timeval *, tvp); + if (ARG2 != 0) { + PRE_timeval_READ( "futimes(tvp[0])", ARG2 ); + PRE_timeval_READ( "futimes(tvp[1])", ARG2+sizeof(struct vki_timeval) ); + } +} + +PRE(sys_semget) +{ + PRINT("sys_semget ( %ld, %ld, %ld )",ARG1,ARG2,ARG3); + PRE_REG_READ3(long, "semget", vki_key_t, key, int, nsems, int, semflg); +} + +PRE(sys_semop) +{ + *flags |= SfMayBlock; + PRINT("sys_semop ( %ld, %#lx, %lu )",ARG1,ARG2,ARG3); + PRE_REG_READ3(long, "semop", + int, semid, struct sembuf *, sops, vki_size_t, nsoops); + ML_(generic_PRE_sys_semop)(tid, ARG1,ARG2,ARG3); +} + +PRE(sys_semctl) +{ + switch (ARG3) { + case VKI_IPC_STAT: + case VKI_IPC_SET: + PRINT("sys_semctl ( %ld, %ld, %ld, %#lx )",ARG1,ARG2,ARG3,ARG4); + PRE_REG_READ4(long, "semctl", + int, semid, int, semnum, int, cmd, struct semid_ds *, arg); + break; + case VKI_GETALL: + case VKI_SETALL: + PRINT("sys_semctl ( %ld, %ld, %ld, %#lx )",ARG1,ARG2,ARG3,ARG4); + PRE_REG_READ4(long, "semctl", + int, semid, int, semnum, int, cmd, unsigned short *, arg); + break; + case VKI_SETVAL: + PRINT("sys_semctl ( %ld, %ld, %ld, %#lx )",ARG1,ARG2,ARG3,ARG4); + PRE_REG_READ4(long, "semctl", + int, semid, int, semnum, int, cmd, int, arg); + break; + default: + PRINT("sys_semctl ( %ld, %ld, %ld )",ARG1,ARG2,ARG3); + PRE_REG_READ3(long, "semctl", + int, semid, int, semnum, int, cmd); + break; + } + ML_(generic_PRE_sys_semctl)(tid, ARG1,ARG2,ARG3,ARG4); +} +POST(sys_semctl) +{ + ML_(generic_POST_sys_semctl)(tid, RES,ARG1,ARG2,ARG3,ARG4); +} + +PRE(sys_sem_open) +{ + if (ARG2 & VKI_O_CREAT) { + // 4-arg version + PRINT("sys_sem_open ( %#lx(%s), %ld, %ld, %ld )", + ARG1,(char*)ARG1,ARG2,ARG3,ARG4); + PRE_REG_READ4(vki_sem_t *, "sem_open", + const char *, name, int, oflag, vki_mode_t, mode, + unsigned int, value); + } else { + // 2-arg version + PRINT("sys_sem_open ( %#lx(%s), %ld )",ARG1,(char*)ARG1,ARG2); + PRE_REG_READ2(vki_sem_t *, "sem_open", + const char *, name, int, oflag); + } + PRE_MEM_RASCIIZ( "sem_open(name)", ARG1 ); + + /* Otherwise handle normally */ + *flags |= SfMayBlock; +} + +PRE(sys_sem_close) +{ + PRINT("sem_close( %#lx )", ARG1); + PRE_REG_READ1(int, "sem_close", vki_sem_t *, sem); +} + +PRE(sys_sem_unlink) +{ + PRINT("sem_unlink( %#lx(%s) )", ARG1,(char*)ARG1); + PRE_REG_READ1(int, "sem_unlink", const char *, name); + PRE_MEM_RASCIIZ( "sem_unlink(name)", ARG1 ); +} + +PRE(sys_sem_post) +{ + PRINT("sem_post( %#lx )", ARG1); + PRE_REG_READ1(int, "sem_post", vki_sem_t *, sem); + *flags |= SfMayBlock; +} + +PRE(sys_sem_destroy) +{ + PRINT("sem_destroy( %#lx )", ARG1); + PRE_REG_READ1(int, "sem_destroy", vki_sem_t *, sem); + PRE_MEM_READ("sem_destroy(sem)", ARG1, sizeof(vki_sem_t)); +} + +PRE(sys_sem_init) +{ + PRINT("sem_init( %#lx, %ld, %ld )", ARG1, ARG2, ARG3); + PRE_REG_READ3(int, "sem_init", vki_sem_t *, sem, + int, pshared, unsigned int, value); + PRE_MEM_WRITE("sem_init(sem)", ARG1, sizeof(vki_sem_t)); +} + +POST(sys_sem_init) +{ + POST_MEM_WRITE(ARG1, sizeof(vki_sem_t)); +} + +PRE(sys_sem_wait_nocancel) +{ + PRINT("sem_wait_nocancel( %#lx )", ARG1); + PRE_REG_READ1(int, "sem_wait_nocancel", vki_sem_t *, sem); + *flags |= SfMayBlock; +} + +PRE(sys_sem_trywait) +{ + PRINT("sem_trywait( %#lx )", ARG1); + PRE_REG_READ1(int, "sem_trywait", vki_sem_t *, sem); + *flags |= SfMayBlock; +} + +PRE(sys_kqueue) +{ + PRINT("kqueue()"); +} + +POST(sys_kqueue) +{ + if (!ML_(fd_allowed)(RES, "kqueue", tid, True)) { + VG_(close)(RES); + SET_STATUS_Failure( VKI_EMFILE ); + } else { + if (VG_(clo_track_fds)) { + ML_(record_fd_open_with_given_name)(tid, RES, NULL); + } + } +} + +PRE(sys_kevent) +{ + PRINT("kevent( %ld, %#lx, %ld, %#lx, %ld, %#lx )", + ARG1, ARG2, ARG3, ARG4, ARG5, ARG6); + PRE_REG_READ6(int,"kevent", int,kq, + const struct vki_kevent *,changelist, int,nchanges, + struct vki_kevent *,eventlist, int,nevents, + const struct vki_timespec *,timeout); + + if (ARG3) PRE_MEM_READ ("kevent(changelist)", + ARG2, ARG3 * sizeof(struct vki_kevent)); + if (ARG5) PRE_MEM_WRITE("kevent(eventlist)", + ARG4, ARG5 * sizeof(struct vki_kevent)); + if (ARG6) PRE_MEM_READ ("kevent(timeout)", + ARG6, sizeof(struct vki_timespec)); + + *flags |= SfMayBlock; +} + +POST(sys_kevent) +{ + PRINT("kevent ret %ld dst %#lx (%zu)", RES, ARG4, sizeof(struct vki_kevent)); + if (RES > 0) POST_MEM_WRITE(ARG4, RES * sizeof(struct vki_kevent)); +} + + +Addr pthread_starter = 0; +Addr wqthread_starter = 0; +SizeT pthread_structsize = 0; + +PRE(sys_bsdthread_register) +{ + PRINT("bsdthread_register( %#lx, %#lx, %lu )", ARG1, ARG2, ARG3); + PRE_REG_READ3(int,"__bsdthread_register", void *,"threadstart", + void *,"wqthread", size_t,"pthsize"); + + pthread_starter = ARG1; + wqthread_starter = ARG2; + pthread_structsize = ARG3; + ARG1 = (Word)&pthread_hijack_asm; + ARG2 = (Word)&wqthread_hijack_asm; +} + +PRE(sys_workq_open) +{ + PRINT("workq_open()"); + PRE_REG_READ0(int, "workq_open"); + + // This creates lots of threads and thread stacks under the covers, + // but we ignore them all until some work item starts running on it. +} + +static const char *workqop_name(int op) +{ + switch (op) { + case VKI_WQOPS_QUEUE_ADD: return "QUEUE_ADD"; + case VKI_WQOPS_QUEUE_REMOVE: return "QUEUE_REMOVE"; + case VKI_WQOPS_THREAD_RETURN: return "THREAD_RETURN"; + default: return "?"; + } +} + + +PRE(sys_workq_ops) +{ + PRINT("workq_ops( %ld(%s), %#lx, %ld )", ARG1, workqop_name(ARG1), ARG2, + ARG3); + PRE_REG_READ3(int,"workq_ops", int,"options", void *,"item", + int,"priority"); + + switch (ARG1) { + case VKI_WQOPS_QUEUE_ADD: + case VKI_WQOPS_QUEUE_REMOVE: + // GrP fixme need anything here? + // GrP fixme may block? + break; + + case VKI_WQOPS_THREAD_RETURN: { + // The interesting case. The kernel will do one of two things: + // 1. Return normally. We continue; libc proceeds to stop the thread. + // V does nothing special here. + // 2. Jump to wqthread_hijack. This wipes the stack and runs a + // new work item, and never returns from workq_ops. + // V handles this by longjmp() from wqthread_hijack back to the + // scheduler, which continues at the new client SP/IP/state. + // This works something like V's signal handling. + // To the tool, this looks like workq_ops() sometimes returns + // to a strange address. + ThreadState *tst = VG_(get_ThreadState)(tid); + tst->os_state.wq_jmpbuf_valid = True; + *flags |= SfMayBlock; // GrP fixme true? + break; + } + + default: + VG_(printf)("UNKNOWN workq_ops option %ld\n", ARG1); + break; + } +} + +POST(sys_workq_ops) +{ + ThreadState *tst = VG_(get_ThreadState)(tid); + tst->os_state.wq_jmpbuf_valid = False; +} + + + +PRE(sys___mac_syscall) +{ + PRINT("__mac_syscall( %#lx, %ld, %#lx )", ARG1, ARG2, ARG3); + PRE_REG_READ3(int,"__mac_syscall", char *,"policy", + int,"call", void *,"arg"); + + // GrP fixme check call's arg? + // GrP fixme check policy? +} + + +/* Not syswrap-generic's sys_exit, which exits only one thread. + More like syswrap-generic's sys_exit_group. */ +PRE(sys_exit) +{ + ThreadId t; + ThreadState* tst; + + PRINT("darwin exit( %ld )", ARG1); + PRE_REG_READ1(void, "exit", int, status); + + tst = VG_(get_ThreadState)(tid); + + /* A little complex; find all the threads with the same threadgroup + as this one (including this one), and mark them to exit */ + for (t = 1; t < VG_N_THREADS; t++) { + if ( /* not alive */ + VG_(threads)[t].status == VgTs_Empty + /* GrP fixme zombie? */ + ) + continue; + + VG_(threads)[t].exitreason = VgSrc_ExitProcess; + VG_(threads)[t].os_state.exitcode = ARG1; + + if (t != tid) + VG_(get_thread_out_of_syscall)(t); /* unblock it, if blocked */ + } + + /* We have to claim the syscall already succeeded. */ + SET_STATUS_Success(0); +} + + +PRE(sys_sigaction) +{ + PRINT("sys_sigaction ( %ld, %#lx, %#lx )", ARG1,ARG2,ARG3); + PRE_REG_READ3(long, "sigaction", + int, signum, vki_sigaction_toK_t *, act, + vki_sigaction_fromK_t *, oldact); + + if (ARG2 != 0) { + vki_sigaction_toK_t *sa = (vki_sigaction_toK_t *)ARG2; + PRE_MEM_READ( "sigaction(act->sa_handler)", + (Addr)&sa->ksa_handler, sizeof(sa->ksa_handler)); + PRE_MEM_READ( "sigaction(act->sa_mask)", + (Addr)&sa->sa_mask, sizeof(sa->sa_mask)); + PRE_MEM_READ( "sigaction(act->sa_flags)", + (Addr)&sa->sa_flags, sizeof(sa->sa_flags)); + } + if (ARG3 != 0) + PRE_MEM_WRITE( "sigaction(oldact)", + ARG3, sizeof(vki_sigaction_fromK_t)); + + SET_STATUS_from_SysRes( + VG_(do_sys_sigaction)(ARG1, (const vki_sigaction_toK_t *)ARG2, + (vki_sigaction_fromK_t *)ARG3) + ); +} +POST(sys_sigaction) +{ + vg_assert(SUCCESS); + if (RES == 0 && ARG3 != 0) + POST_MEM_WRITE( ARG3, sizeof(vki_sigaction_fromK_t)); +} + + +PRE(sys___pthread_sigmask) +{ + // GrP fixme + // JRS: arguments are identical to sigprocmask + // (how, sigset_t*, sigset_t*). Perhaps behave identically? + static Bool warned; + if (!warned) { + VG_(printf)("UNKNOWN __pthread_sigmask is unsupported. " + "This warning will not be repeated.\n"); + warned = True; + } + SET_STATUS_Success( 0 ); +} + + +PRE(sys___pthread_canceled) +{ + *flags |= SfMayBlock; /* might kill this thread??? */ + /* I don't think so -- I think it just changes the cancellation + state. But taking no chances. */ + PRINT("__pthread_canceled ( %ld )", ARG1); + PRE_REG_READ1(long, "__pthread_canceled", void*, arg1); +} + + +PRE(sys___pthread_markcancel) +{ + *flags |= SfMayBlock; /* might kill this thread??? */ + PRINT("__pthread_markcancel ( %#lx )", ARG1); + PRE_REG_READ1(long, "__pthread_markcancel", void*, arg1); + /* Just let it go through. No idea if this is correct. */ +} + + +PRE(sys___disable_threadsignal) +{ + vki_sigset_t set; + PRINT("__disable_threadsignal(%ld, %ld, %ld)", ARG1, ARG2, ARG3); + /* I don't think this really looks at its arguments. So don't + bother to check them. */ + + VG_(sigfillset)( &set ); + SET_STATUS_from_SysRes( + VG_(do_sys_sigprocmask) ( tid, VKI_SIG_BLOCK, &set, NULL ) + ); + + /* We don't expect that blocking all signals for this thread could + cause any more to be delivered (how could it?), but just in case + .. */ + if (SUCCESS) + *flags |= SfPollAfter; +} + + +PRE(sys_kdebug_trace) +{ + PRINT("kdebug_trace(%ld, %ld, %ld, %ld, %ld, %ld)", + ARG1, ARG2, ARG3, ARG4, ARG5, ARG6); + PRE_REG_READ6(long, "kdebug_trace", + int,"code", int,"arg1", int,"arg2", + int,"arg3", int,"arg4", int,"arg5"); + // GrP fixme anything else? +} + + +PRE(sys_seteuid) +{ + PRINT("seteuid(%ld)", ARG1); + PRE_REG_READ1(long, "seteuid", vki_uid_t, "uid"); +} + + +PRE(sys_setegid) +{ + PRINT("setegid(%ld)", ARG1); + PRE_REG_READ1(long, "setegid", vki_uid_t, "uid"); +} + +PRE(sys_settid) +{ + PRINT("settid(%ld, %ld)", ARG1, ARG2); + PRE_REG_READ2(long, "settid", vki_uid_t, "uid", vki_gid_t, "gid"); +} + +/* XXX need to check whether we need POST operations for + * waitevent, watchevent, modwatch -- jpeach + */ +PRE(sys_watchevent) +{ + PRINT("watchevent(%#lx, %#lx)", ARG1, ARG2); + PRE_REG_READ2(long, "watchevent", + vki_eventreq *, "event", unsigned int, "eventmask"); + + PRE_MEM_READ("watchevent(event)", ARG1, sizeof(vki_eventreq)); + PRE_MEM_READ("watchevent(eventmask)", ARG2, sizeof(unsigned int)); + *flags |= SfMayBlock; +} + +#define WAITEVENT_FAST_POLL ((Addr)(struct timeval *)-1) +PRE(sys_waitevent) +{ + PRINT("waitevent(%#lx, %#lx)", ARG1, ARG2); + PRE_REG_READ2(long, "waitevent", + vki_eventreq *, "event", struct timeval *, "timeout"); + PRE_MEM_WRITE("waitevent(event)", ARG1, sizeof(vki_eventreq)); + + if (ARG2 && ARG2 != WAITEVENT_FAST_POLL) { + PRE_timeval_READ("waitevent(timeout)", ARG2); + } + + /* XXX ((timeval*)-1) is valid for ARG2 -- jpeach */ + *flags |= SfMayBlock; +} + +POST(sys_waitevent) +{ + POST_MEM_WRITE(ARG1, sizeof(vki_eventreq)); +} + +PRE(sys_modwatch) +{ + PRINT("modwatch(%#lx, %#lx)", ARG1, ARG2); + PRE_REG_READ2(long, "modwatch", + vki_eventreq *, "event", unsigned int, "eventmask"); + + PRE_MEM_READ("modwatch(event)", ARG1, sizeof(vki_eventreq)); + PRE_MEM_READ("modwatch(eventmask)", ARG2, sizeof(unsigned int)); +} + +PRE(sys_getxattr) +{ + PRINT("getxattr(%#lx(%s), %#lx(%s), %#lx, %lu, %lu, %ld)", + ARG1, (char *)ARG1, ARG2, (char *)ARG2, ARG3, ARG4, ARG5, ARG6); + + PRE_REG_READ6(vki_ssize_t, "getxattr", + const char *, path, char *, name, void *, value, + vki_size_t, size, uint32_t, position, int, options); + PRE_MEM_RASCIIZ("getxattr(path)", ARG1); + PRE_MEM_RASCIIZ("getxattr(name)", ARG2); + PRE_MEM_WRITE( "getxattr(value)", ARG3, ARG4); +} + +POST(sys_getxattr) +{ + vg_assert((vki_ssize_t)RES >= 0); + POST_MEM_WRITE(ARG3, (vki_ssize_t)RES); +} + +PRE(sys_fgetxattr) +{ + PRINT("fgetxattr(%ld, %#lx(%s), %#lx, %lu, %lu, %ld)", + ARG1, ARG2, (char *)ARG2, ARG3, ARG4, ARG5, ARG6); + + PRE_REG_READ6(vki_ssize_t, "fgetxattr", + int, fd, char *, name, void *, value, + vki_size_t, size, uint32_t, position, int, options); + PRE_MEM_RASCIIZ("getxattr(name)", ARG2); + PRE_MEM_WRITE( "getxattr(value)", ARG3, ARG4); +} + +POST(sys_fgetxattr) +{ + vg_assert((vki_ssize_t)RES >= 0); + POST_MEM_WRITE(ARG3, (vki_ssize_t)RES); +} + +PRE(sys_setxattr) +{ + PRINT("setxattr ( %#lx(%s), %#lx(%s), %#lx, %lu, %lu, %ld )", + ARG1, (char *)ARG1, ARG2, (char*)ARG2, ARG3, ARG4, ARG5, ARG6 ); + PRE_REG_READ6(int, "setxattr", + const char *,"path", char *,"name", void *,"value", + vki_size_t,"size", uint32_t,"position", int,"options" ); + + PRE_MEM_RASCIIZ( "setxattr(path)", ARG1 ); + PRE_MEM_RASCIIZ( "setxattr(name)", ARG2 ); + PRE_MEM_READ( "setxattr(value)", ARG3, ARG4 ); +} + + +PRE(sys_fsetxattr) +{ + PRINT( "fsetxattr ( %ld, %#lx(%s), %#lx, %lu, %lu, %ld )", + ARG1, ARG2, (char*)ARG2, ARG3, ARG4, ARG5, ARG6 ); + PRE_REG_READ6(int, "fsetxattr", + int,"fd", char *,"name", void *,"value", + vki_size_t,"size", uint32_t,"position", int,"options" ); + + PRE_MEM_RASCIIZ( "fsetxattr(name)", ARG2 ); + PRE_MEM_READ( "fsetxattr(value)", ARG3, ARG4 ); +} + + +PRE(sys_listxattr) +{ + PRINT( "listxattr ( %#lx(%s), %#lx, %lu, %ld )", + ARG1, (char *)ARG1, ARG2, ARG3, ARG4 ); + PRE_REG_READ4 (long, "listxattr", + const char *,"path", char *,"namebuf", + vki_size_t,"size", int,"options" ); + + PRE_MEM_RASCIIZ( "listxattr(path)", ARG1 ); + PRE_MEM_WRITE( "listxattr(namebuf)", ARG2, ARG3 ); + *flags |= SfMayBlock; +} +POST(sys_listxattr) +{ + vg_assert(SUCCESS); + vg_assert((vki_ssize_t)RES >= 0); + POST_MEM_WRITE( ARG2, (vki_ssize_t)RES ); +} + + +PRE(sys_flistxattr) +{ + PRINT( "flistxattr ( %ld, %#lx, %lu, %ld )", + ARG1, ARG2, ARG3, ARG4 ); + PRE_REG_READ4 (long, "flistxattr", + int, "fd", char *,"namebuf", + vki_size_t,"size", int,"options" ); + PRE_MEM_WRITE( "flistxattr(namebuf)", ARG2, ARG3 ); + *flags |= SfMayBlock; +} +POST(sys_flistxattr) +{ + vg_assert(SUCCESS); + vg_assert((vki_ssize_t)RES >= 0); + POST_MEM_WRITE( ARG2, (vki_ssize_t)RES ); +} + +PRE(sys_shmget) +{ + PRINT("sys_shmget ( %ld, %ld, %ld )",ARG1,ARG2,ARG3); + PRE_REG_READ3(long, "shmget", vki_key_t, key, vki_size_t, size, int, shmflg); +} + +PRE(sys_shm_open) +{ + PRINT("shm_open(%#lx(%s), %ld, %ld)", ARG1, (char *)ARG1, ARG2, ARG3); + PRE_REG_READ3(long, "shm_open", + const char *,"name", int,"flags", vki_mode_t,"mode"); + + PRE_MEM_RASCIIZ( "shm_open(filename)", ARG1 ); + + *flags |= SfMayBlock; +} + +POST(sys_shm_open) +{ + vg_assert(SUCCESS); + if (!ML_(fd_allowed)(RES, "shm_open", tid, True)) { + VG_(close)(RES); + SET_STATUS_Failure( VKI_EMFILE ); + } else { + if (VG_(clo_track_fds)) + ML_(record_fd_open_with_given_name)(tid, RES, (Char*)ARG1); + } +} + + +PRE(sys_statx) +{ + PRINT("statx( %#lx(%s), %#lx, %#lx, %#lx )", + ARG1, (char *)ARG1, ARG2, ARG3, ARG4); + PRE_REG_READ4(int, "statx", char *, file_name, struct stat *, buf, + void *, fsacl, vki_size_t *, fsacl_size); + PRE_MEM_RASCIIZ( "statx(file_name)", ARG1 ); + PRE_MEM_READ( "statx(fsacl_size)", ARG4, sizeof(vki_size_t) ); + PRE_MEM_WRITE( "statx(buf)", ARG2, sizeof(struct vki_stat) ); + PRE_MEM_WRITE( "statx(fsacl_size)", ARG4, sizeof(vki_size_t) ); + PRE_MEM_WRITE( "statx(fsacl)", ARG3, *(vki_size_t *)ARG4 ); +} +POST(sys_statx) +{ + POST_MEM_WRITE( ARG2, sizeof(struct vki_stat) ); + POST_MEM_WRITE( ARG4, sizeof(vki_size_t) ); + POST_MEM_WRITE( ARG3, *(vki_size_t *)ARG4 ); +} + + +PRE(sys_fchmod_extended) +{ + /* Note: this is not really correct. Handling of + sys_chmod_extended is broken in the same way. */ + PRINT("sys_fchmod_extended ( %ld, %ld, %ld, %ld, %#lx )", + ARG1, ARG2, ARG3, ARG4, ARG5); + PRE_REG_READ5(long, "fchmod", + unsigned int, fildes, + uid_t, uid, + gid_t, gid, + vki_mode_t, mode, + void* /*really,user_addr_t*/, xsecurity); + /* relative to the xnu sources (kauth_copyinfilesec), this + is just way wrong. */ + PRE_MEM_READ( "fchmod_extended(xsecurity)", ARG5, + sizeof(struct kauth_filesec) ); +} + +PRE(sys_chmod_extended) +{ + /* Note: this is not really correct. Handling of + sys_fchmod_extended is broken in the same way. */ + PRINT("sys_chmod_extended ( %#lx(%s), %ld, %ld, %ld, %#lx )", + ARG1, ARG1 ? (HChar*)ARG1 : "(null)", ARG2, ARG3, ARG4, ARG5); + PRE_REG_READ5(long, "chmod", + unsigned int, fildes, + uid_t, uid, + gid_t, gid, + vki_mode_t, mode, + void* /*really,user_addr_t*/, xsecurity); + PRE_MEM_RASCIIZ("chmod_extended(path)", ARG1); + /* relative to the xnu sources (kauth_copyinfilesec), this + is just way wrong. */ + PRE_MEM_READ( "chmod_extended(xsecurity)", ARG5, + sizeof(struct kauth_filesec) ); +} + + +PRE(sys_accessx) +{ + // GrP fixme difficult +} + +POST(sys_accessx) +{ + // GrP fixme +} + +PRE(sys_chflags) +{ + PRINT("sys_chflags ( %#lx(%s), %lu )", ARG1, (char *)ARG1, ARG2); + PRE_REG_READ2(int, "chflags", const char *,path, unsigned int,flags); + PRE_MEM_RASCIIZ("chflags(path)", ARG1); + + // GrP fixme sanity-check flags value? +} + +PRE(sys_fchflags) +{ + PRINT("sys_fchflags ( %ld, %lu )", ARG1, ARG2); + PRE_REG_READ2(int, "fchflags", int,fd, unsigned int,flags); + + // GrP fixme sanity-check flags value? +} + +POST(sys_stat64) +{ + POST_MEM_WRITE( ARG2, sizeof(struct vki_stat64) ); +} + +PRE(sys_stat64) +{ + PRINT("sys_stat64 ( %#lx(%s), %#lx )", ARG1, (char *)ARG1, ARG2); + PRE_REG_READ2(long, "stat", const char *,path, struct stat64 *,buf); + PRE_MEM_RASCIIZ("stat64(path)", ARG1); + PRE_MEM_WRITE( "stat64(buf)", ARG2, sizeof(struct vki_stat64) ); +} + + +POST(sys_lstat64) +{ + POST_MEM_WRITE( ARG2, sizeof(struct vki_stat64) ); +} + +PRE(sys_lstat64) +{ + PRINT("sys_lstat64 ( %#lx(%s), %#lx )", ARG1, (char *)ARG1, ARG2); + PRE_REG_READ2(long, "stat", const char *,path, struct stat64 *,buf); + PRE_MEM_RASCIIZ("lstat64(path)", ARG1); + PRE_MEM_WRITE( "lstat64(buf)", ARG2, sizeof(struct vki_stat64) ); +} + + +POST(sys_fstat64) +{ + POST_MEM_WRITE( ARG2, sizeof(struct vki_stat64) ); +} + +PRE(sys_fstat64) +{ + PRINT("sys_fstat64 ( %ld, %#lx )", ARG1,ARG2); + PRE_REG_READ2(long, "fstat", unsigned int, fd, struct stat64 *, buf); + PRE_MEM_WRITE( "fstat64(buf)", ARG2, sizeof(struct vki_stat64) ); +} + + +PRE(sys_getfsstat) +{ + PRINT("getfsstat(%#lx, %ld, %ld)", ARG1, ARG2, ARG3); + PRE_REG_READ3(int, "getfsstat", struct vki_statfs *, buf, + int, bufsize, int, flags); + if (ARG1) { + // ARG2 is a BYTE SIZE + PRE_MEM_WRITE("getfsstat", ARG1, ARG2); + } +} + +POST(sys_getfsstat) +{ + if (ARG1) { + // RES is a STRUCT COUNT + POST_MEM_WRITE(ARG1, RES * sizeof(struct vki_statfs)); + } +} + + +static void scan_attrlist(ThreadId tid, struct vki_attrlist *attrList, + void *attrBuf, SizeT attrBufSize, + void (*fn)(ThreadId, void *attrData, SizeT size) + ) +{ + typedef struct { + uint32_t attrBit; + int32_t attrSize; + } attrspec; + static const attrspec commonattr[] = { + // This order is important. + { ATTR_CMN_NAME, -1 }, + { ATTR_CMN_DEVID, sizeof(dev_t) }, + { ATTR_CMN_FSID, sizeof(fsid_t) }, + { ATTR_CMN_OBJTYPE, sizeof(fsobj_type_t) }, + { ATTR_CMN_OBJTAG, sizeof(fsobj_tag_t) }, + { ATTR_CMN_OBJID, sizeof(fsobj_id_t) }, + { ATTR_CMN_OBJPERMANENTID, sizeof(fsobj_id_t) }, + { ATTR_CMN_PAROBJID, sizeof(fsobj_id_t) }, + { ATTR_CMN_SCRIPT, sizeof(text_encoding_t) }, + { ATTR_CMN_CRTIME, sizeof(struct timespec) }, + { ATTR_CMN_MODTIME, sizeof(struct timespec) }, + { ATTR_CMN_CHGTIME, sizeof(struct timespec) }, + { ATTR_CMN_ACCTIME, sizeof(struct timespec) }, + { ATTR_CMN_BKUPTIME, sizeof(struct timespec) }, + { ATTR_CMN_FNDRINFO, 32 /*FileInfo+ExtendedFileInfo, or FolderInfo+ExtendedFolderInfo*/ }, + { ATTR_CMN_OWNERID, sizeof(uid_t) }, + { ATTR_CMN_GRPID, sizeof(gid_t) }, + { ATTR_CMN_ACCESSMASK, sizeof(uint32_t) }, + { ATTR_CMN_NAMEDATTRCOUNT, sizeof(uint32_t) }, + { ATTR_CMN_NAMEDATTRLIST, -1 }, + { ATTR_CMN_FLAGS, sizeof(uint32_t) }, + { ATTR_CMN_USERACCESS, sizeof(uint32_t) }, + { ATTR_CMN_FILEID, sizeof(uint64_t) }, + { ATTR_CMN_PARENTID, sizeof(uint64_t) }, + { 0, 0 } + }; + static const attrspec volattr[] = { + // This order is important. + { ATTR_VOL_INFO, 0 }, + { ATTR_VOL_FSTYPE, sizeof(uint32_t) }, + { ATTR_VOL_SIGNATURE, sizeof(uint32_t) }, + { ATTR_VOL_SIZE, sizeof(off_t) }, + { ATTR_VOL_SPACEFREE, sizeof(off_t) }, + { ATTR_VOL_SPACEAVAIL, sizeof(off_t) }, + { ATTR_VOL_MINALLOCATION, sizeof(off_t) }, + { ATTR_VOL_ALLOCATIONCLUMP, sizeof(off_t) }, + { ATTR_VOL_IOBLOCKSIZE, sizeof(uint32_t) }, + { ATTR_VOL_OBJCOUNT, sizeof(uint32_t) }, + { ATTR_VOL_FILECOUNT, sizeof(uint32_t) }, + { ATTR_VOL_DIRCOUNT, sizeof(uint32_t) }, + { ATTR_VOL_MAXOBJCOUNT, sizeof(uint32_t) }, + { ATTR_VOL_MOUNTPOINT, -1 }, + { ATTR_VOL_NAME, -1 }, + { ATTR_VOL_MOUNTFLAGS, sizeof(uint32_t) }, + { ATTR_VOL_MOUNTEDDEVICE, -1 }, + { ATTR_VOL_ENCODINGSUSED, sizeof(uint64_t) }, + { ATTR_VOL_CAPABILITIES, sizeof(vol_capabilities_attr_t) }, + { ATTR_VOL_ATTRIBUTES, sizeof(vol_attributes_attr_t) }, + { 0, 0 } + }; + static const attrspec dirattr[] = { + // This order is important. + { ATTR_DIR_LINKCOUNT, sizeof(uint32_t) }, + { ATTR_DIR_ENTRYCOUNT, sizeof(uint32_t) }, + { ATTR_DIR_MOUNTSTATUS, sizeof(uint32_t) }, + { 0, 0 } + }; + static const attrspec fileattr[] = { + // This order is important. + { ATTR_FILE_LINKCOUNT, sizeof(uint32_t) }, + { ATTR_FILE_TOTALSIZE, sizeof(off_t) }, + { ATTR_FILE_ALLOCSIZE, sizeof(off_t) }, + { ATTR_FILE_IOBLOCKSIZE, sizeof(uint32_t) }, + { ATTR_FILE_CLUMPSIZE, sizeof(uint32_t) }, + { ATTR_FILE_DEVTYPE, sizeof(uint32_t) }, + { ATTR_FILE_FILETYPE, sizeof(uint32_t) }, + { ATTR_FILE_FORKCOUNT, sizeof(uint32_t) }, + { ATTR_FILE_FORKLIST, -1 }, + { ATTR_FILE_DATALENGTH, sizeof(off_t) }, + { ATTR_FILE_DATAALLOCSIZE, sizeof(off_t) }, + { ATTR_FILE_DATAEXTENTS, sizeof(extentrecord) }, + { ATTR_FILE_RSRCLENGTH, sizeof(off_t) }, + { ATTR_FILE_RSRCALLOCSIZE, sizeof(off_t) }, + { ATTR_FILE_RSRCEXTENTS, sizeof(extentrecord) }, + { 0, 0 } + }; + static const attrspec forkattr[] = { + // This order is important. + { ATTR_FORK_TOTALSIZE, sizeof(off_t) }, + { ATTR_FORK_ALLOCSIZE, sizeof(off_t) }, + { 0, 0 } + }; + + static const attrspec *attrdefs[5] = { + commonattr, volattr, dirattr, fileattr, forkattr + }; + attrgroup_t a[5]; + uint8_t *d, *dend; + int g, i; + + vg_assert(attrList->bitmapcount == 5); + VG_(memcpy)(a, &attrList->commonattr, sizeof(a)); + d = attrBuf; + dend = d + attrBufSize; + + for (g = 0; g < 5; g++) { + for (i = 0; attrdefs[g][i].attrBit; i++) { + uint32_t bit = attrdefs[g][i].attrBit; + int32_t size = attrdefs[g][i].attrSize; + + if (a[g] & bit) { + a[g] &= ~bit; // clear bit for error check later + if (size == -1) { + attrreference_t *ref = (attrreference_t *)d; + size = MIN(sizeof(attrreference_t), dend - d); + fn(tid, d, size); + if (size >= sizeof(attrreference_t) && + d + ref->attr_dataoffset < dend) + { + fn(tid, d + ref->attr_dataoffset, + MIN(ref->attr_length, dend - (d + ref->attr_dataoffset))); + } + d += size; + } + else { + size = MIN(size, dend - d); + fn(tid, d, size); + d += size; + } + + if ((uintptr_t)d % 4) d += 4 - ((uintptr_t)d % 4); + if (d > dend) d = dend; + } + } + + // Known bits are cleared. Die if any bits are left. + if (a[g] != 0) { + VG_(message)(Vg_UserMsg, "UNKNOWN attrlist flags %d:0x%x\n", g, a[g]); + } + } +} + +static void get1attr(ThreadId tid, void *attrData, SizeT attrDataSize) +{ + POST_MEM_WRITE((Addr)attrData, attrDataSize); +} + +static void set1attr(ThreadId tid, void *attrData, SizeT attrDataSize) +{ + PRE_MEM_READ("setattrlist(attrBuf value)", (Addr)attrData, attrDataSize); +} + +PRE(sys_getattrlist) +{ + PRINT("getattrlist(%#lx(%s), %#lx, %#lx, %lu, %lu)", + ARG1, (char *)ARG1, ARG2, ARG3, ARG4, ARG5); + PRE_REG_READ5(int, "getattrlist", + const char *,path, struct vki_attrlist *,attrList, + void *,attrBuf, vki_size_t,attrBufSize, unsigned int,options); + PRE_MEM_RASCIIZ("getattrlist(path)", ARG1); + PRE_MEM_READ("getattrlist(attrList)", ARG2, sizeof(struct vki_attrlist)); + PRE_MEM_WRITE("getattrlist(attrBuf)", ARG3, ARG4); +} + +POST(sys_getattrlist) +{ + if (ARG4 > sizeof(vki_uint32_t)) { + // attrBuf is uint32_t bytes written followed by attr data + vki_uint32_t *sizep = (vki_uint32_t *)ARG3; + POST_MEM_WRITE(ARG3, sizeof(vki_uint32_t)); + scan_attrlist(tid, (struct vki_attrlist *)ARG2, sizep+1, *sizep, &get1attr); + } +} + + +PRE(sys_setattrlist) +{ + PRINT("setattrlist(%#lx(%s), %#lx, %#lx, %lu, %lu)", + ARG1, (char *)ARG1, ARG2, ARG3, ARG4, ARG5); + PRE_REG_READ5(int, "setattrlist", + const char *,path, struct vki_attrlist *,attrList, + void *,attrBuf, vki_size_t,attrBufSize, unsigned int,options); + PRE_MEM_RASCIIZ("setattrlist(path)", ARG1); + PRE_MEM_READ("setattrlist(attrList)", ARG2, sizeof(struct vki_attrlist)); + scan_attrlist(tid, (struct vki_attrlist *)ARG2, (void*)ARG3, ARG4, &set1attr); +} + + +PRE(sys_getdirentriesattr) +{ + PRINT("getdirentriesattr(%ld, %#lx, %#lx, %ld, %#lx, %#lx, %#lx, %ld)", + ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8); + PRE_REG_READ8(int, "getdirentriesattr", + int,fd, struct vki_attrlist *,attrList, + void *,attrBuf, size_t,attrBufSize, + unsigned int *,count, unsigned int *,basep, + unsigned int *,newState, unsigned int,options); + PRE_MEM_READ("getdirentriesattr(attrList)", + ARG2, sizeof(struct vki_attrlist)); + PRE_MEM_WRITE("getdirentriesattr(attrBuf)", ARG3, ARG4); + PRE_MEM_READ("getdirentriesattr(count)", ARG5, sizeof(unsigned int)); + PRE_MEM_WRITE("getdirentriesattr(count)", ARG5, sizeof(unsigned int)); + PRE_MEM_WRITE("getdirentriesattr(basep)", ARG6, sizeof(unsigned int)); + PRE_MEM_WRITE("getdirentriesattr(newState)", ARG7, sizeof(unsigned int)); +} + +POST(sys_getdirentriesattr) +{ + char *p, *end; + unsigned int count; + unsigned int i; + + POST_MEM_WRITE(ARG5, sizeof(unsigned int)); + POST_MEM_WRITE(ARG6, sizeof(unsigned int)); + POST_MEM_WRITE(ARG7, sizeof(unsigned int)); + + // return buffer is concatenation of variable-size structs + count = *(unsigned int *)ARG5; + p = (char *)ARG3; + end = (char *)ARG3 + ARG4; + for (i = 0; i < count; i++) { + vg_assert(p < end); // failure is kernel bug or Valgrind bug + p += *(unsigned int *)p; + } + + POST_MEM_WRITE(ARG3, p - (char *)ARG3); + + PRINT("got %d records, %d/%lu bytes\n", count, p-(char *)ARG3, ARG4); +} + + +PRE(sys_fsctl) +{ + PRINT("fsctl ( %#lx(%s), %ld, %#lx, %ld )", + ARG1, (char *)ARG1, ARG2, ARG3, ARG4); + PRE_REG_READ4( long, "fsctl", + char *,"path", unsigned int,"request", + void *,"data", unsigned int,"options"); + + PRE_MEM_RASCIIZ( "fsctl(path)", ARG1 ); + + switch (ARG2) { + case VKI_afpfsByteRangeLock2FSCTL: { + struct vki_ByteRangeLockPB2 *pb = (struct vki_ByteRangeLockPB2 *)ARG3; + PRE_FIELD_READ("fsctl(afpfsByteRangeLock2, pb->offset)", + pb->offset); + PRE_FIELD_READ("fsctl(afpfsByteRangeLock2, pb->length)", + pb->length); + PRE_FIELD_READ("fsctl(afpfsByteRangeLock2, pb->unLockFlag)", + pb->unLockFlag); + PRE_FIELD_READ("fsctl(afpfsByteRangeLock2, pb->startEndFlag)", + pb->startEndFlag); + PRE_FIELD_READ("fsctl(afpfsByteRangeLock2, pb->fd)", + pb->fd); + + PRE_FIELD_WRITE("fsctl(afpfsByteRangeLock2, pb->retRangeStart)", + pb->retRangeStart); + + // GrP fixme check fd + break; + } + case VKI_FSIOC_SYNC_VOLUME: + PRE_MEM_READ( "fsctl(FSIOC_SYNC_VOLUME)", ARG3, sizeof(int) ); + break; + + default: + // fsctl requests use ioctl encoding + ML_(PRE_unknown_ioctl)(tid, ARG2, ARG3); + break; + } +} + +POST(sys_fsctl) +{ + switch (ARG2) { + case VKI_afpfsByteRangeLock2FSCTL: { + struct vki_ByteRangeLockPB2 *pb = (struct vki_ByteRangeLockPB2 *)ARG3; + POST_FIELD_WRITE(pb->retRangeStart); + break; + } + case VKI_FSIOC_SYNC_VOLUME: + break; + + default: + // fsctl requests use ioctl encoding + ML_(POST_unknown_ioctl)(tid, RES, ARG2, ARG3); + break; + } +} + +PRE(sys_initgroups) +{ + PRINT("sys_initgroups(%s, %#lx, %lu)", (char *)ARG1, ARG2, ARG3); + PRE_REG_READ3(long, "initgroups", + int, setlen, vki_gid_t *, gidset, vki_uid_t, gmuid); + PRE_MEM_READ("gidset", ARG2, ARG1 * sizeof(vki_gid_t)); +} + + +//--------- posix_spawn ---------// +/* Largely copied from PRE(sys_execve) in syswrap-generic.c, and from + the simpler AIX equivalent (syswrap-aix5.c). */ +// Pre_read a char** argument. +static void pre_argv_envp(Addr a, ThreadId tid, Char* s1, Char* s2) +{ + while (True) { + Addr a_deref; + Addr* a_p = (Addr*)a; + PRE_MEM_READ( s1, (Addr)a_p, sizeof(Addr) ); + a_deref = *a_p; + if (0 == a_deref) + break; + PRE_MEM_RASCIIZ( s2, a_deref ); + a += sizeof(char*); + } +} +static SysRes simple_pre_exec_check(const HChar* exe_name) +{ + Int fd, ret; + SysRes res; + Bool setuid_allowed; + + // Check it's readable + res = VG_(open)(exe_name, VKI_O_RDONLY, 0); + if (sr_isError(res)) { + return res; + } + fd = sr_Res(res); + VG_(close)(fd); + + // Check we have execute permissions. We allow setuid executables + // to be run only in the case when we are not simulating them, that + // is, they to be run natively. + setuid_allowed = VG_(clo_trace_children) ? False : True; + ret = VG_(check_executable)(NULL/*&is_setuid*/, + (HChar*)exe_name, setuid_allowed); + if (0 != ret) { + return VG_(mk_SysRes_Error)(ret); + } + return VG_(mk_SysRes_Success)(0); +} +PRE(sys_posix_spawn) +{ + Char* path = NULL; /* path to executable */ + Char** envp = NULL; + Char** argv = NULL; + Char** arg2copy; + Char* launcher_basename = NULL; + Int i, j, tot_args; + SysRes res; + + /* args: pid_t* pid + char* path + posix_spawn_file_actions_t* file_actions + char** argv + char** envp + */ + PRINT("sys_posix_spawn( %#lx, %#lx(%s), %#lx, %#lx, %#lx )", + ARG1, ARG2, ARG2 ? (HChar*)ARG2 : "(null)", ARG3, ARG4, ARG5 ); + + /* Standard pre-syscall checks */ + + PRE_REG_READ5(int, "posix_spawn", vki_pid_t*, pid, char*, path, + void*, file_actions, char**, argv, char**, envp ); + PRE_MEM_WRITE("posix_spawn(pid)", ARG1, sizeof(vki_pid_t) ); + PRE_MEM_RASCIIZ("posix_spawn(path)", ARG2); + // DDD: check file_actions + if (ARG4 != 0) + pre_argv_envp( ARG4, tid, "posix_spawn(argv)", + "posix_spawn(argv[i])" ); + if (ARG5 != 0) + pre_argv_envp( ARG5, tid, "posix_spawn(envp)", + "posix_spawn(envp[i])" ); + + if (0) + VG_(printf)("sys_posix_spawn( %#lx, %#lx(%s), %#lx, %#lx, %#lx )\n", + ARG1, ARG2, ARG2 ? (HChar*)ARG2 : "(null)", ARG3, ARG4, ARG5 ); + + /* Now follows a bunch of logic copied from PRE(sys_execve) in + syswrap-generic.c. */ + + /* Check that the name at least begins in client-accessible storage. */ + if (!VG_(am_is_valid_for_client)( ARG2, 1, VKI_PROT_READ )) { + SET_STATUS_Failure( VKI_EFAULT ); + return; + } + + // Do the important checks: it is a file, is executable, permissions are + // ok, etc. We allow setuid executables to run only in the case when + // we are not simulating them, that is, they to be run natively. + res = simple_pre_exec_check((const HChar*)ARG2); + if (sr_isError(res)) { + SET_STATUS_Failure( sr_Err(res) ); + return; + } + + /* If we're tracing the child, and the launcher name looks bogus + (possibly because launcher.c couldn't figure it out, see + comments therein) then we have no option but to fail. */ + if (VG_(clo_trace_children) + && (VG_(name_of_launcher) == NULL + || VG_(name_of_launcher)[0] != '/')) { + SET_STATUS_Failure( VKI_ECHILD ); /* "No child processes" */ + return; + } + + /* Ok. So let's give it a try. */ + VG_(debugLog)(1, "syswrap", "Posix_spawn of %s\n", (Char*)ARG2); + + // Set up the child's exe path. + // + if (VG_(clo_trace_children)) { + + // We want to exec the launcher. Get its pre-remembered path. + path = VG_(name_of_launcher); + // VG_(name_of_launcher) should have been acquired by m_main at + // startup. The following two assertions should be assured by + // the "If we're tracking the child .." test just above here. + vg_assert(path); + vg_assert(path[0] == '/'); + launcher_basename = path; + + } else { + path = (Char*)ARG2; + } + + // Set up the child's environment. + // + // Remove the valgrind-specific stuff from the environment so the + // child doesn't get vgpreload_core.so, vgpreload_.so, etc. + // This is done unconditionally, since if we are tracing the child, + // the child valgrind will set up the appropriate client environment. + // Nb: we make a copy of the environment before trying to mangle it + // as it might be in read-only memory (this was bug #101881). + // + // Then, if tracing the child, set VALGRIND_LIB for it. + // + if (ARG5 == 0) { + envp = NULL; + } else { + envp = VG_(env_clone)( (Char**)ARG5 ); + vg_assert(envp); + VG_(env_remove_valgrind_env_stuff)( envp ); + } + + if (VG_(clo_trace_children)) { + // Set VALGRIND_LIB in ARG5 (the environment) + VG_(env_setenv)( &envp, VALGRIND_LIB, VG_(libdir)); + } + + // Set up the child's args. If not tracing it, they are + // simply ARG4. Otherwise, they are + // + // [launcher_basename] ++ VG_(args_for_valgrind) ++ [ARG2] ++ ARG4[1..] + // + // except that the first VG_(args_for_valgrind_noexecpass) args + // are omitted. + // + if (!VG_(clo_trace_children)) { + argv = (Char**)ARG4; + } else { + vg_assert( VG_(args_for_valgrind) ); + vg_assert( VG_(args_for_valgrind_noexecpass) >= 0 ); + vg_assert( VG_(args_for_valgrind_noexecpass) + <= VG_(sizeXA)( VG_(args_for_valgrind) ) ); + /* how many args in total will there be? */ + // launcher basename + tot_args = 1; + // V's args + tot_args += VG_(sizeXA)( VG_(args_for_valgrind) ); + tot_args -= VG_(args_for_valgrind_noexecpass); + // name of client exe + tot_args++; + // args for client exe, skipping [0] + arg2copy = (Char**)ARG4; + if (arg2copy && arg2copy[0]) { + for (i = 1; arg2copy[i]; i++) + tot_args++; + } + // allocate + argv = VG_(malloc)( "di.syswrap.pre_sys_execve.1", + (tot_args+1) * sizeof(HChar*) ); + vg_assert(argv); + // copy + j = 0; + argv[j++] = launcher_basename; + for (i = 0; i < VG_(sizeXA)( VG_(args_for_valgrind) ); i++) { + if (i < VG_(args_for_valgrind_noexecpass)) + continue; + argv[j++] = * (HChar**) VG_(indexXA)( VG_(args_for_valgrind), i ); + } + argv[j++] = (Char*)ARG2; + if (arg2copy && arg2copy[0]) + for (i = 1; arg2copy[i]; i++) + argv[j++] = arg2copy[i]; + argv[j++] = NULL; + // check + vg_assert(j == tot_args+1); + } + + /* DDD: sort out the signal state. What signal + state does the child inherit from the parent? */ + + if (0) { + Char **cpp; + VG_(printf)("posix_spawn: %s\n", path); + for (cpp = argv; cpp && *cpp; cpp++) + VG_(printf)("argv: %s\n", *cpp); + if (1) + for (cpp = envp; cpp && *cpp; cpp++) + VG_(printf)("env: %s\n", *cpp); + } + + /* Let the call go through as usual. However, we have to poke + the altered arguments back into the argument slots. */ + ARG2 = (UWord)path; + ARG4 = (UWord)argv; + ARG5 = (UWord)envp; + + /* not to mention .. */ + *flags |= SfMayBlock; +} +POST(sys_posix_spawn) +{ + vg_assert(SUCCESS); + //POST_MEM_WRITE( ARG1, sizeof(vki_pid_t) ); +} + + +PRE(sys_socket) +{ + PRINT("sys_socket ( %ld, %ld, %ld )",ARG1,ARG2,ARG3); + PRE_REG_READ3(long, "socket", int, domain, int, type, int, protocol); +} + +POST(sys_socket) +{ + SysRes r; + vg_assert(SUCCESS); + r = ML_(generic_POST_sys_socket)(tid, VG_(mk_SysRes_Success)(RES)); + SET_STATUS_from_SysRes(r); +} + + +PRE(sys_setsockopt) +{ + PRINT("sys_setsockopt ( %ld, %ld, %ld, %#lx, %ld )", + ARG1,ARG2,ARG3,ARG4,ARG5); + PRE_REG_READ5(long, "setsockopt", + int, s, int, level, int, optname, + const void *, optval, vki_socklen_t, optlen); + ML_(generic_PRE_sys_setsockopt)(tid, ARG1,ARG2,ARG3,ARG4,ARG5); +} + + +PRE(sys_getsockopt) +{ + Addr optval_p = ARG4; + Addr optlen_p = ARG5; + PRINT("sys_getsockopt ( %ld, %ld, %ld, %#lx, %#lx )", + ARG1,ARG2,ARG3,ARG4,ARG5); + PRE_REG_READ5(long, "getsockopt", + int, s, int, level, int, optname, + void *, optval, vki_socklen_t *, optlen); + /* int getsockopt(int socket, int level, int option_name, + void *restrict option_value, + socklen_t *restrict option_len); */ + /* vg_assert(sizeof(socklen_t) == sizeof(UInt)); */ + if (optval_p != (Addr)NULL) { + ML_(buf_and_len_pre_check) ( tid, optval_p, optlen_p, + "socketcall.getsockopt(optval)", + "socketcall.getsockopt(optlen)" ); + } + // DDD: #warning GrP fixme darwin-specific sockopts +} + +POST(sys_getsockopt) +{ + Addr optval_p = ARG4; + Addr optlen_p = ARG5; + vg_assert(SUCCESS); + if (optval_p != (Addr)NULL) { + ML_(buf_and_len_post_check) ( tid, VG_(mk_SysRes_Success)(RES), + optval_p, optlen_p, + "socketcall.getsockopt(optlen_out)" ); + // DDD: #warning GrP fixme darwin-specific sockopts + } +} + + +PRE(sys_connect) +{ + *flags |= SfMayBlock; + PRINT("sys_connect ( %ld, %#lx, %ld )",ARG1,ARG2,ARG3); + PRE_REG_READ3(long, "connect", + int, sockfd, struct sockaddr *, serv_addr, int, addrlen); + ML_(generic_PRE_sys_connect)(tid, ARG1,ARG2,ARG3); +} + + +PRE(sys_accept) +{ + *flags |= SfMayBlock; + PRINT("sys_accept ( %ld, %#lx, %ld )",ARG1,ARG2,ARG3); + PRE_REG_READ3(long, "accept", + int, s, struct sockaddr *, addr, int, *addrlen); + ML_(generic_PRE_sys_accept)(tid, ARG1,ARG2,ARG3); +} + +POST(sys_accept) +{ + SysRes r; + vg_assert(SUCCESS); + r = ML_(generic_POST_sys_accept)(tid, VG_(mk_SysRes_Success)(RES), + ARG1,ARG2,ARG3); + SET_STATUS_from_SysRes(r); +} + + +PRE(sys_sendto) +{ + *flags |= SfMayBlock; + PRINT("sys_sendto ( %ld, %s, %ld, %lu, %#lx, %ld )", + ARG1,(char *)ARG2,ARG3,ARG4,ARG5,ARG6); + PRE_REG_READ6(long, "sendto", + int, s, const void *, msg, int, len, + unsigned int, flags, + const struct sockaddr *, to, int, tolen); + ML_(generic_PRE_sys_sendto)(tid, ARG1,ARG2,ARG3,ARG4,ARG5,ARG6); +} + +PRE(sys_sendfile) +{ +#if VG_WORDSIZE == 4 + PRINT("sys_sendfile(%ld, %ld, %llu, %#lx, %#lx, %ld)", + ARG1, ARG2, LOHI64(ARG3, ARG4), ARG5, ARG6, ARG7); + + PRE_REG_READ7(long, "sendfile", + int, fromfd, int, tofd, + vki_uint32_t, offset_low32, vki_uint32_t, offset_high32, + vki_uint64_t *, nwritten, struct sf_hdtr *, sf_header, int, flags); + PRE_MEM_WRITE("sendfile(nwritten)", ARG5, sizeof(vki_uint64_t)); + if (ARG6) PRE_MEM_WRITE("sendfile(sf_header)", ARG6, sizeof(struct sf_hdtr)); +#else + PRINT("sys_sendfile(%ld, %ld, %ld, %#lx, %#lx, %ld)", + ARG1, ARG2, ARG3, ARG4, ARG5, ARG6); + + PRE_REG_READ6(long, "sendfile", + int, fromfd, int, tofd, + vki_uint64_t, offset, + vki_uint64_t *, nwritten, struct sf_hdtr *, sf_header, int, flags); + PRE_MEM_WRITE("sendfile(nwritten)", ARG4, sizeof(vki_uint64_t)); + if (ARG5) PRE_MEM_WRITE("sendfile(sf_header)", ARG5, sizeof(struct sf_hdtr)); +#endif + + *flags |= SfMayBlock; +} + +POST(sys_sendfile) +{ +#if VG_WORDSIZE == 4 + POST_MEM_WRITE(ARG5, sizeof(vki_uint64_t)); + if (ARG6) POST_MEM_WRITE(ARG6, sizeof(struct sf_hdtr)); +#else + POST_MEM_WRITE(ARG4, sizeof(vki_uint64_t)); + if (ARG5) POST_MEM_WRITE(ARG5, sizeof(struct sf_hdtr)); +#endif +} + +PRE(sys_recvfrom) +{ + *flags |= SfMayBlock; + PRINT("sys_recvfrom ( %ld, %#lx, %ld, %lu, %#lx, %#lx )", + ARG1,ARG2,ARG3,ARG4,ARG5,ARG6); + PRE_REG_READ6(long, "recvfrom", + int, s, void *, buf, int, len, unsigned int, flags, + struct sockaddr *, from, int *, fromlen); + ML_(generic_PRE_sys_recvfrom)(tid, ARG1,ARG2,ARG3,ARG4,ARG5,ARG6); +} + +POST(sys_recvfrom) +{ + vg_assert(SUCCESS); + ML_(generic_POST_sys_recvfrom)(tid, VG_(mk_SysRes_Success)(RES), + ARG1,ARG2,ARG3,ARG4,ARG5,ARG6); +} + + +PRE(sys_sendmsg) +{ + *flags |= SfMayBlock; + PRINT("sys_sendmsg ( %ld, %#lx, %ld )",ARG1,ARG2,ARG3); + PRE_REG_READ3(long, "sendmsg", + int, s, const struct msghdr *, msg, int, flags); + ML_(generic_PRE_sys_sendmsg)(tid, ARG1,ARG2); +} + + +PRE(sys_recvmsg) +{ + *flags |= SfMayBlock; + PRINT("sys_recvmsg ( %ld, %#lx, %ld )",ARG1,ARG2,ARG3); + PRE_REG_READ3(long, "recvmsg", int, s, struct msghdr *, msg, int, flags); + ML_(generic_PRE_sys_recvmsg)(tid, ARG1,ARG2); +} + +POST(sys_recvmsg) +{ + ML_(generic_POST_sys_recvmsg)(tid, ARG1,ARG2); +} + + +PRE(sys_shutdown) +{ + *flags |= SfMayBlock; + PRINT("sys_shutdown ( %ld, %ld )",ARG1,ARG2); + PRE_REG_READ2(int, "shutdown", int, s, int, how); +} + + +PRE(sys_bind) +{ + PRINT("sys_bind ( %ld, %#lx, %ld )",ARG1,ARG2,ARG3); + PRE_REG_READ3(long, "bind", + int, sockfd, struct sockaddr *, my_addr, int, addrlen); + ML_(generic_PRE_sys_bind)(tid, ARG1,ARG2,ARG3); +} + + +PRE(sys_listen) +{ + PRINT("sys_listen ( %ld, %ld )",ARG1,ARG2); + PRE_REG_READ2(long, "listen", int, s, int, backlog); +} + + +PRE(sys_getsockname) +{ + PRINT("sys_getsockname ( %ld, %#lx, %#lx )",ARG1,ARG2,ARG3); + PRE_REG_READ3(long, "getsockname", + int, s, struct sockaddr *, name, int *, namelen); + ML_(generic_PRE_sys_getsockname)(tid, ARG1,ARG2,ARG3); +} + +POST(sys_getsockname) +{ + vg_assert(SUCCESS); + ML_(generic_POST_sys_getsockname)(tid, VG_(mk_SysRes_Success)(RES), + ARG1,ARG2,ARG3); +} + + +PRE(sys_getpeername) +{ + PRINT("sys_getpeername ( %ld, %#lx, %#lx )",ARG1,ARG2,ARG3); + PRE_REG_READ3(long, "getpeername", + int, s, struct sockaddr *, name, int *, namelen); + ML_(generic_PRE_sys_getpeername)(tid, ARG1,ARG2,ARG3); +} + +POST(sys_getpeername) +{ + vg_assert(SUCCESS); + ML_(generic_POST_sys_getpeername)(tid, VG_(mk_SysRes_Success)(RES), + ARG1,ARG2,ARG3); +} + + +PRE(sys_socketpair) +{ + PRINT("sys_socketpair ( %ld, %ld, %ld, %#lx )",ARG1,ARG2,ARG3,ARG4); + PRE_REG_READ4(long, "socketpair", + int, d, int, type, int, protocol, int *, sv); + ML_(generic_PRE_sys_socketpair)(tid, ARG1,ARG2,ARG3,ARG4); +} + +POST(sys_socketpair) +{ + vg_assert(SUCCESS); + ML_(generic_POST_sys_socketpair)(tid, VG_(mk_SysRes_Success)(RES), + ARG1,ARG2,ARG3,ARG4); +} + + +PRE(sys_gethostuuid) +{ + PRINT("sys_gethostuuid ( %#lx, %#lx )", ARG1, ARG2); + PRE_REG_READ2(int,"gethostuuid", + char *,"uuid_buf", + const struct vki_timespec *,"timeout"); + + PRE_MEM_WRITE("uuid_buf", ARG1, 16); + PRE_MEM_READ("timeout", ARG2, sizeof(struct vki_timespec)); + + *flags |= SfMayBlock; +} + + +POST(sys_gethostuuid) +{ + POST_MEM_WRITE(ARG1, 16); +} + +/* Darwin pipe() returns the two descriptors in two registers. */ +PRE(sys_pipe) +{ + PRINT("sys_pipe ( )"); + PRE_REG_READ0(int, "pipe"); +} + +POST(sys_pipe) +{ + Int p0, p1; + vg_assert(SUCCESS); + p0 = RES; + p1 = RESHI; + + if (!ML_(fd_allowed)(p0, "pipe", tid, True) || + !ML_(fd_allowed)(p1, "pipe", tid, True)) { + VG_(close)(p0); + VG_(close)(p1); + SET_STATUS_Failure( VKI_EMFILE ); + } else { + if (VG_(clo_track_fds)) { + ML_(record_fd_open_nameless)(tid, p0); + ML_(record_fd_open_nameless)(tid, p1); + } + } +} + + +PRE(sys_getlogin) +{ + PRINT("getlogin ( %#lx, %ld )", ARG1, ARG2); + PRE_REG_READ2(long, "getlogin", + char *,"namebuf", unsigned int,"namelen"); + + PRE_MEM_WRITE("getlogin(namebuf)", ARG1, ARG2); +} + +POST(sys_getlogin) +{ + POST_MEM_WRITE(ARG1, ARG2); +} + + +PRE(sys_ptrace) +{ + PRINT("ptrace ( %ld, %ld, %#lx, %ld )", ARG1, ARG2, ARG3, ARG4); + PRE_REG_READ4(long, "ptrace", + int,"request", vki_pid_t,"pid", + vki_caddr_t,"addr", int,"data"); + + // Note: some code uses ptrace(random, 0, 0, 0) as a profiling mechanism. + + // GrP fixme anything needed? +} + + +PRE(sys_issetugid) +{ + PRINT("issetugid ( )"); + PRE_REG_READ0(long, "issetugid"); +} + + +PRE(sys_getdtablesize) +{ + PRINT("getdtablesize ( )"); + PRE_REG_READ0(long, "getdtablesize"); +} + +POST(sys_getdtablesize) +{ + // Subtract Valgrind's fd range from client's dtable + if (RES > VG_(fd_hard_limit)) SET_STATUS_Success(VG_(fd_hard_limit)); +} + +PRE(sys_lseek) +{ + PRINT("lseek ( %ld, %ld, %ld )", ARG1,ARG2,ARG3); + PRE_REG_READ4(vki_off_t, "lseek", + unsigned int,fd, int,offset_hi, int,offset_lo, + unsigned int,whence); +} + + +PRE(sys_pathconf) +{ + PRINT("pathconf(%#lx(%s), %ld)", ARG1,(char *)ARG1,ARG2); + PRE_REG_READ2(long,"pathconf", const char *,"path", int,"name"); + PRE_MEM_RASCIIZ("pathconf(path)", ARG1); +} + + +PRE(sys_fpathconf) +{ + PRINT("fpathconf(%ld, %ld)", ARG1,ARG2); + PRE_REG_READ2(long,"fpathconf", int,"fd", int,"name"); + + if (!ML_(fd_allowed)(ARG1, "fpathconf", tid, False)) + SET_STATUS_Failure( VKI_EBADF ); +} + + +PRE(sys_getdirentries) +{ + PRINT("getdirentries(%ld, %#lx, %ld, %#lx)", ARG1, ARG2, ARG3, ARG4); + PRE_REG_READ4(int, "getdirentries", + int, fd, char *, buf, int, nbytes, long *, basep); + PRE_MEM_WRITE("getdirentries(basep)", ARG4, sizeof(long)); + PRE_MEM_WRITE("getdirentries(buf)", ARG2, ARG3); +} + +POST(sys_getdirentries) +{ + POST_MEM_WRITE(ARG4, sizeof(long)); + // GrP fixme be specific about d_name? + POST_MEM_WRITE(ARG2, RES); +} + + +PRE(sys_getdirentries64) +{ + PRINT("getdirentries64(%ld, %#lx, %lu, %#lx)", ARG1, ARG2, ARG3, ARG4); + PRE_REG_READ4(vki_ssize_t, "getdirentries", + int,fd, char *,buf, vki_size_t,nbytes, vki_off_t *,basep); + PRE_MEM_WRITE("getdirentries(position)", ARG4, sizeof(vki_off_t)); + PRE_MEM_WRITE("getdirentries(buf)", ARG2, ARG3); +} + +POST(sys_getdirentries64) +{ + POST_MEM_WRITE(ARG4, sizeof(vki_off_t)); + // GrP fixme be specific about d_name? (fixme copied from 32 bit version) + POST_MEM_WRITE(ARG2, RES); +} + + +PRE(sys_statfs64) +{ + PRINT("sys_statfs64 ( %#lx(%s), %#lx )",ARG1,(char *)ARG1,ARG2); + PRE_REG_READ2(long, "statfs64", const char *, path, struct statfs64 *, buf); + PRE_MEM_RASCIIZ( "statfs64(path)", ARG1 ); + PRE_MEM_WRITE( "statfs64(buf)", ARG2, sizeof(struct vki_statfs64) ); +} + +POST(sys_statfs64) +{ + POST_MEM_WRITE( ARG2, sizeof(struct vki_statfs64) ); +} + + +PRE(sys_fstatfs64) +{ + PRINT("sys_fstatfs64 ( %ld, %#lx )",ARG1,ARG2); + PRE_REG_READ2(long, "fstatfs64", + unsigned int, fd, struct statfs *, buf); + PRE_MEM_WRITE( "fstatfs64(buf)", ARG2, sizeof(struct vki_statfs64) ); +} + +POST(sys_fstatfs64) +{ + POST_MEM_WRITE( ARG2, sizeof(struct vki_statfs64) ); +} + +PRE(sys_csops) +{ + PRINT("sys_csops ( %ld, %#lx, %#lx, %lu )", ARG1, ARG2, ARG3, ARG4); + PRE_REG_READ4(int, "csops", + vki_pid_t, pid, uint32_t, ops, + void *, useraddr, vki_size_t, usersize); + + PRE_MEM_WRITE( "csops(addr)", ARG3, ARG4 ); + + // If the pid is ours, don't mark the program as KILL or HARD + // Maybe we should keep track of this for later calls to STATUS + if (!ARG1 || VG_(getpid)() == ARG1) { + switch (ARG2) { + case VKI_CS_OPS_MARKINVALID: + case VKI_CS_OPS_MARKHARD: + case VKI_CS_OPS_MARKKILL: + SET_STATUS_Success(0); + } + } +} + +POST(sys_csops) +{ + POST_MEM_WRITE( ARG3, ARG4 ); +} + +PRE(sys_auditon) +{ + PRINT("sys_auditon ( %ld, %#lx, %ld )", ARG1, ARG2, ARG3); + PRE_REG_READ3(int,"auditon", + int,"cmd", void*,"data", unsigned int,"length"); + + switch (ARG1) { + + case VKI_A_SETPOLICY: + case VKI_A_SETKMASK: + case VKI_A_SETQCTRL: + case VKI_A_SETCOND: + case VKI_A_SETCLASS: + case VKI_A_SETPMASK: + case VKI_A_SETFSIZE: + // kernel reads data..data+length + PRE_MEM_READ("auditon(data)", ARG2, ARG3); + break; + + case VKI_A_GETKMASK: + case VKI_A_GETPOLICY: + case VKI_A_GETQCTRL: + case VKI_A_GETFSIZE: + case VKI_A_GETCOND: + // kernel writes data..data+length + // GrP fixme be precise about what gets written + PRE_MEM_WRITE("auditon(data)", ARG2, ARG3); + break; + + + case VKI_A_GETCLASS: + case VKI_A_GETPINFO: + case VKI_A_GETPINFO_ADDR: + // kernel reads and writes data..data+length + // GrP fixme be precise about what gets read and written + PRE_MEM_READ("auditon(data)", ARG2, ARG3); + PRE_MEM_WRITE("auditon(data)", ARG2, ARG3); + break; + + case VKI_A_SETKAUDIT: + case VKI_A_SETSTAT: + case VKI_A_SETUMASK: + case VKI_A_SETSMASK: + case VKI_A_GETKAUDIT: + case VKI_A_GETCWD: + case VKI_A_GETCAR: + case VKI_A_GETSTAT: + // unimplemented on darwin + break; + + default: + VG_(message)(Vg_UserMsg, "UNKNOWN auditon cmd %ld", ARG1); + break; + } +} + +POST(sys_auditon) +{ + switch (ARG1) { + + case VKI_A_SETPOLICY: + case VKI_A_SETKMASK: + case VKI_A_SETQCTRL: + case VKI_A_SETCOND: + case VKI_A_SETCLASS: + case VKI_A_SETPMASK: + case VKI_A_SETFSIZE: + // kernel reads data..data+length + break; + + case VKI_A_GETKMASK: + case VKI_A_GETPOLICY: + case VKI_A_GETQCTRL: + case VKI_A_GETFSIZE: + case VKI_A_GETCOND: + // kernel writes data..data+length + // GrP fixme be precise about what gets written + POST_MEM_WRITE(ARG2, ARG3); + break; + + + case VKI_A_GETCLASS: + case VKI_A_GETPINFO: + case VKI_A_GETPINFO_ADDR: + // kernel reads and writes data..data+length + // GrP fixme be precise about what gets read and written + POST_MEM_WRITE(ARG2, ARG3); + break; + + case VKI_A_SETKAUDIT: + case VKI_A_SETSTAT: + case VKI_A_SETUMASK: + case VKI_A_SETSMASK: + case VKI_A_GETKAUDIT: + case VKI_A_GETCWD: + case VKI_A_GETCAR: + case VKI_A_GETSTAT: + // unimplemented on darwin + break; + + default: + break; + } +} + + +PRE(sys_mmap) +{ + // SysRes r; + +#if VG_WORDSIZE == 4 + PRINT("sys_mmap ( %#lx, %lu, %ld, %ld, %ld, %lld )", + ARG1, ARG2, ARG3, ARG4, ARG5, LOHI64(ARG6, ARG7) ); + PRE_REG_READ7(Addr, "mmap", + Addr,start, vki_size_t,length, int,prot, int,flags, int,fd, + unsigned long,offset_hi, unsigned long,offset_lo); + // GrP fixme V mmap and kernel mach_msg collided once - don't use + // V's mechanism for now + // r = ML_(generic_PRE_sys_mmap)( tid, ARG1, ARG2, ARG3, ARG4, ARG5, + // (Off64T)LOHI64(ARG6, ARG7) ); +#else + PRINT("sys_mmap ( %#lx, %lu, %ld, %ld, %ld, %ld )", + ARG1, ARG2, ARG3, ARG4, ARG5, ARG6 ); + PRE_REG_READ6(long, "mmap", + Addr,start, vki_size_t,length, int,prot, int,flags, int,fd, + Off64T,offset); + // r = ML_(generic_PRE_sys_mmap)( tid, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6 ); + +#endif + + // SET_STATUS_from_SysRes(r); +} + +POST(sys_mmap) +{ + if (RES != -1) { + ML_(notify_core_and_tool_of_mmap)(RES, ARG2, ARG3, ARG4, ARG5, ARG6); + // Try to load symbols from the region + VG_(di_notify_mmap)( (Addr)RES, False/*allow_SkFileV*/ ); + } +} + + +PRE(sys_sysctl) +{ + PRINT( "sysctl ( %#lx, %ld, %#lx, %#lx, %#lx, %ld )", + ARG1, ARG2, ARG3, ARG4, ARG5, ARG6 ); + + PRE_REG_READ6(int, "sysctl", int*, name, unsigned int, namelen, + void*, oldp, vki_size_t *, oldlenp, + void*, newp, vki_size_t *, newlenp); + + PRE_MEM_READ("sysctl(name)", ARG1, ARG2); // reads name[0..namelen-1] + if (ARG4) { + // writes *ARG4 + PRE_MEM_WRITE("sysctl(oldlenp)", ARG4, sizeof(size_t)); + if (ARG3) { + // also reads *ARG4, and writes as much as ARG3[0..ARG4-1] + PRE_MEM_READ("sysctl(oldlenp)", ARG4, sizeof(size_t)); + PRE_MEM_WRITE("sysctl(oldp)", ARG3, *(size_t *)ARG4); + } + } + if (ARG5) { + PRE_MEM_READ("sysctl(newp)", ARG5, ARG6); + } + + if (VG_(clo_trace_syscalls)) { + unsigned int i; + int *name = (int *)ARG1; + VG_(printf)(" mib: [ "); + for (i = 0; i < ARG2; i++) { + VG_(printf)("%d ", name[i]); + } + VG_(printf)("]"); + } + + // GrP fixme intercept KERN_PROCARGS and KERN_PROC_PID for our pid + // (executable path and arguments and environment + + { + // Intercept sysctl(kern.usrstack). The kernel's reply would be + // Valgrind's stack, not the client's stack. + // GrP fixme kern_usrstack64 + if (ARG1 && ARG2 == 2 && + ((int *)ARG1)[0] == VKI_CTL_KERN && +#if VG_WORDSIZE == 4 + ((int *)ARG1)[1] == VKI_KERN_USRSTACK32 +#else + ((int *)ARG1)[1] == VKI_KERN_USRSTACK64 +#endif + ) + { + if (ARG5/*newp*/ || ARG6/*newlen*/) { + SET_STATUS_Failure(VKI_EPERM); // USRSTACK is read-only + } else { + Addr *oldp = (Addr *)ARG3; + size_t *oldlenp = (size_t *)ARG4; + if (oldlenp) { + Addr stack_end = VG_(clstk_end)+1; + size_t oldlen = *oldlenp; + // always return actual size + *oldlenp = sizeof(Addr); + if (oldp && oldlen >= sizeof(Addr)) { + // oldp is big enough + // copy value and return 0 + *oldp = stack_end; + SET_STATUS_Success(0); + } else { + // oldp isn't big enough + // copy as much as possible and return ENOMEM + if (oldp) VG_(memcpy)(oldp, &stack_end, oldlen); + SET_STATUS_Failure(VKI_ENOMEM); + } + } + } + } + } + + if (!SUCCESS && !FAILURE) { + // Don't set SfPostOnFail if we've already handled it locally. + *flags |= SfPostOnFail; + } +} + +POST(sys_sysctl) +{ + if (SUCCESS || ERR == VKI_ENOMEM) { + // sysctl can write truncated data and return VKI_ENOMEM + if (ARG4) { + POST_MEM_WRITE(ARG4, sizeof(size_t)); + } + if (ARG3 && ARG4) { + POST_MEM_WRITE(ARG3, *(size_t *)ARG4); + } + } +} + + +PRE(sys_sigpending) +{ + PRINT( "sys_sigpending ( %#lx )", ARG1 ); + PRE_REG_READ1(long, "sigpending", vki_sigset_t *, set); + PRE_MEM_WRITE( "sigpending(set)", ARG1, sizeof(vki_sigset_t)); +} + +POST(sys_sigpending) +{ + POST_MEM_WRITE( ARG1, sizeof(vki_sigset_t) ) ; +} + + +PRE(sys_sigprocmask) +{ + UWord arg1; + PRINT("sigprocmask ( %ld, %#lx, %#lx )", ARG1, ARG2, ARG3); + PRE_REG_READ3(long, "sigprocmask", + int, how, vki_sigset_t *, set, vki_sigset_t *, oldset); + if (ARG2 != 0) + PRE_MEM_READ( "sigprocmask(set)", ARG2, sizeof(vki_sigset_t)); + if (ARG3 != 0) + PRE_MEM_WRITE( "sigprocmask(oldset)", ARG3, sizeof(vki_sigset_t)); + + /* Massage ARG1 ('how'). If ARG2 (the new mask) is NULL then the + value of 'how' is irrelevant, and it appears that Darwin's libc + passes zero, which is not equal to any of + SIG_{BLOCK,UNBLOCK,SETMASK}. This causes + VG_(do_sys_sigprocmask) to complain, since it checks the 'how' + value independently of the other args. Solution: in this case, + simply pass a valid (but irrelevant) value for 'how'. */ + /* Also, in this case the new set is passed to the kernel by + reference, not value, as in some other sigmask related Darwin + syscalls. */ + arg1 = ARG1; + if (ARG2 == 0 /* the new-set is NULL */ + && ARG1 != VKI_SIG_BLOCK + && ARG1 != VKI_SIG_UNBLOCK && ARG1 != VKI_SIG_SETMASK) { + arg1 = VKI_SIG_SETMASK; + } + SET_STATUS_from_SysRes( + VG_(do_sys_sigprocmask) ( tid, arg1, (vki_sigset_t*)ARG2, + (vki_sigset_t*)ARG3 ) + ); + + if (SUCCESS) + *flags |= SfPollAfter; +} + +POST(sys_sigprocmask) +{ + vg_assert(SUCCESS); + if (RES == 0 && ARG3 != 0) + POST_MEM_WRITE( ARG3, sizeof(vki_sigset_t)); +} + + +PRE(sys_sigsuspend) +{ + /* Just hand this off to the kernel. Is that really correct? And + shouldn't we at least set SfPollAfter? These questions apply to + all the Linux versions too. */ + /* I think the first arg is the 32-bit signal mask (by value), and + the other two args are ignored. */ + *flags |= SfMayBlock; + PRINT("sys_sigsuspend ( mask=0x%08lx )", ARG1 ); + PRE_REG_READ1(int, "sigsuspend", int, sigmask); +} + + +/* --------------------------------------------------------------------- + mach_msg: formatted messages + ------------------------------------------------------------------ */ + +static size_t desc_size(mach_msg_descriptor_t *desc) +{ + switch (desc->type.type) { + case MACH_MSG_PORT_DESCRIPTOR: return sizeof(desc->port); + case MACH_MSG_OOL_DESCRIPTOR: return sizeof(desc->out_of_line); + case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: return sizeof(desc->out_of_line); + case MACH_MSG_OOL_PORTS_DESCRIPTOR: return sizeof(desc->ool_ports); + default: + VG_(printf)("UNKNOWN mach message descriptor %d\n", desc->type.type); + return sizeof(desc->type); // guess + } +} + + +static void assign_port_names(mach_msg_ool_ports_descriptor_t *desc, + const char *name) +{ + mach_msg_size_t i; + mach_port_t *ports = (mach_port_t *)desc->address; + for (i = 0; i < desc->count; i++) { + assign_port_name(ports[i], name); + } +} + + +static void import_complex_message(ThreadId tid, mach_msg_header_t *mh) +{ + mach_msg_body_t *body; + mach_msg_size_t count, i; + uint8_t *p; + mach_msg_descriptor_t *desc; + + vg_assert(mh->msgh_bits & MACH_MSGH_BITS_COMPLEX); + + body = (mach_msg_body_t *)(mh+1); + count = body->msgh_descriptor_count; + p = (uint8_t *)(body+1); + + for (i = 0; i < count; i++) { + desc = (mach_msg_descriptor_t *)p; + p += desc_size(desc); + + switch (desc->type.type) { + case MACH_MSG_PORT_DESCRIPTOR: + // single port + record_unnamed_port(tid, desc->port.name, -1); + record_port_insert_rights(desc->port.name, desc->port.disposition); + PRINT("got port %s; ", name_for_port(desc->port.name)); + break; + + case MACH_MSG_OOL_DESCRIPTOR: + case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: + // out-of-line memory - map it + // GrP fixme how is VOLATILE different? do we care? + // GrP fixme do other flags tell us anything? assume shared for now + // GrP fixme more SF_ flags marking mach_msg memory might be nice + // GrP fixme protection + if (desc->out_of_line.size > 0) { + Addr start = VG_PGROUNDDN((Addr)desc->out_of_line.address); + Addr end = VG_PGROUNDUP((Addr)desc->out_of_line.address + + (Addr)desc->out_of_line.size); + PRINT("got ool mem %p..%#lx; ", desc->out_of_line.address, + (Addr)desc->out_of_line.address+desc->out_of_line.size); + + ML_(notify_core_and_tool_of_mmap)( + start, end - start, VKI_PROT_READ|VKI_PROT_WRITE, + VKI_MAP_PRIVATE, -1, 0); + } + // GrP fixme mark only un-rounded part as initialized + break; + + case MACH_MSG_OOL_PORTS_DESCRIPTOR: + // out-of-line array of ports - map it + // GrP fixme see fixmes above + PRINT("got %d ool ports %p..%#lx", desc->ool_ports.count, desc->ool_ports.address, (Addr)desc->ool_ports.address+desc->ool_ports.count*sizeof(mach_port_t)); + + if (desc->ool_ports.count > 0) { + Addr start = VG_PGROUNDDN((Addr)desc->ool_ports.address); + Addr end = VG_PGROUNDUP((Addr)desc->ool_ports.address + desc->ool_ports.count * sizeof(mach_port_t)); + mach_port_t *ports = (mach_port_t *)desc->ool_ports.address; + + ML_(notify_core_and_tool_of_mmap)( + start, end - start, VKI_PROT_READ|VKI_PROT_WRITE, + VKI_MAP_PRIVATE, -1, 0); + + PRINT(":"); + for (i = 0; i < desc->ool_ports.count; i++) { + record_unnamed_port(tid, ports[i], -1); + record_port_insert_rights(ports[i], desc->port.disposition); + PRINT(" %s", name_for_port(ports[i])); + } + } + PRINT(";"); + break; + + default: + VG_(printf)("UNKNOWN Mach descriptor type %u!\n", desc->type.type); + break; + } + } +} + + +static void pre_port_desc_read(ThreadId tid, mach_msg_port_descriptor_t *desc2) +{ +#pragma pack(4) + struct { + mach_port_t name; + mach_msg_size_t pad1; + uint16_t pad2; + uint8_t disposition; + uint8_t type; + } *desc = (void*)desc2; +#pragma pack() + + PRE_FIELD_READ("msg->desc.port.name", desc->name); + PRE_FIELD_READ("msg->desc.port.disposition", desc->disposition); + PRE_FIELD_READ("msg->desc.port.type", desc->type); +} + + +static void pre_ool_desc_read(ThreadId tid, mach_msg_ool_descriptor_t *desc2) +{ +#pragma pack(4) + struct { + Addr address; +#if VG_WORDSIZE != 8 + mach_msg_size_t size; +#endif + uint8_t deallocate; + uint8_t copy; + uint8_t pad1; + uint8_t type; +#if VG_WORDSIZE == 8 + mach_msg_size_t size; +#endif + } *desc = (void*)desc2; +#pragma pack() + + PRE_FIELD_READ("msg->desc.out_of_line.address", desc->address); + PRE_FIELD_READ("msg->desc.out_of_line.size", desc->size); + PRE_FIELD_READ("msg->desc.out_of_line.deallocate", desc->deallocate); + PRE_FIELD_READ("msg->desc.out_of_line.copy", desc->copy); + PRE_FIELD_READ("msg->desc.out_of_line.type", desc->type); +} + +static void pre_oolports_desc_read(ThreadId tid, + mach_msg_ool_ports_descriptor_t *desc2) +{ +#pragma pack(4) + struct { + Addr address; +#if VG_WORDSIZE != 8 + mach_msg_size_t size; +#endif + uint8_t deallocate; + uint8_t copy; + uint8_t disposition; + uint8_t type; +#if VG_WORDSIZE == 8 + mach_msg_size_t size; +#endif + } *desc = (void*)desc2; +#pragma pack() + + PRE_FIELD_READ("msg->desc.ool_ports.address", desc->address); + PRE_FIELD_READ("msg->desc.ool_ports.size", desc->size); + PRE_FIELD_READ("msg->desc.ool_ports.deallocate", desc->deallocate); + PRE_FIELD_READ("msg->desc.ool_ports.copy", desc->copy); + PRE_FIELD_READ("msg->desc.ool_ports.disposition", desc->disposition); + PRE_FIELD_READ("msg->desc.ool_ports.type", desc->type); +} + + +// Returns the size of the descriptor area +// (mach_msg_body_t + any mach_msg_descriptor_t) +static size_t export_complex_message(ThreadId tid, mach_msg_header_t *mh) +{ + mach_msg_body_t *body; + mach_msg_size_t count, i; + uint8_t *p; + mach_msg_descriptor_t *desc; + + vg_assert(mh->msgh_bits & MACH_MSGH_BITS_COMPLEX); + + body = (mach_msg_body_t *)(mh+1); + PRE_MEM_READ("msg->msgh_descriptor_count)", (Addr)body, sizeof(*body)); + + count = body->msgh_descriptor_count; + p = (uint8_t *)(body+1); + + for (i = 0; i < count; i++) { + desc = (mach_msg_descriptor_t *)p; + p += desc_size(desc); + + switch (desc->type.type) { + case MACH_MSG_PORT_DESCRIPTOR: + // single port; no memory map effects + pre_port_desc_read(tid, &desc->port); + break; + + case MACH_MSG_OOL_DESCRIPTOR: + case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: + // out-of-line memory - unmap it if it's marked dealloc + // GrP fixme need to remap if message fails? + // GrP fixme how is VOLATILE different? do we care? + // GrP fixme struct is different for lp64 + pre_ool_desc_read(tid, &desc->out_of_line); + + if (desc->out_of_line.deallocate && desc->out_of_line.size > 0) { + vm_size_t size = desc->out_of_line.size; + Addr start = VG_PGROUNDDN((Addr)desc->out_of_line.address); + Addr end = VG_PGROUNDUP((Addr)desc->out_of_line.address + size); + PRINT("kill ool mem %p..%#lx; ", desc->out_of_line.address, + (Addr)desc->out_of_line.address + size); + ML_(notify_core_and_tool_of_munmap)(start, end - start); + } + break; + + case MACH_MSG_OOL_PORTS_DESCRIPTOR: + // out-of-line array of ports - unmap it if it's marked dealloc + // GrP fixme need to remap if message fails? + // GrP fixme struct different for lp64 + pre_oolports_desc_read(tid, &desc->ool_ports); + + if (desc->ool_ports.deallocate && desc->ool_ports.count > 0) { + vm_size_t size = desc->ool_ports.count * sizeof(mach_port_t); + Addr start = VG_PGROUNDDN((Addr)desc->ool_ports.address); + Addr end = VG_PGROUNDUP((Addr)desc->ool_ports.address + size); + PRINT("kill ool port array %p..%#lx; ", desc->ool_ports.address, + (Addr)desc->ool_ports.address + size); + ML_(notify_core_and_tool_of_munmap)(start, end - start); + } + break; + default: + VG_(printf)("UNKNOWN Mach descriptor type %u!\n", desc->type.type); + break; + } + } + + return (size_t)((Addr)p - (Addr)body); +} + + +/* --------------------------------------------------------------------- + mach_msg: host-related messages + ------------------------------------------------------------------ */ + + +POST(host_info) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_msg_type_number_t host_info_outCnt; + integer_t host_info_out[14]; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (reply->RetCode) PRINT("mig return %d", reply->RetCode); +} + +PRE(host_info) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + host_flavor_t flavor; + mach_msg_type_number_t host_info_outCnt; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("host_info(mach_host_self(), flavor %d)", req->flavor); + + AFTER = POST_FN(host_info); +} + + +POST(host_page_size) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + vm_size_t out_page_size; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + PRINT("page size %u", reply->out_page_size); + } else { + PRINT("mig return %d", reply->RetCode); + } +} + +PRE(host_page_size) +{ + PRINT("host_page_size(mach_host_self(), ...)"); + + AFTER = POST_FN(host_page_size); +} + + +POST(host_get_io_master) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t io_master; + /* end of the kernel processed data */ + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + assign_port_name(reply->io_master.name, "io_master-%p"); + PRINT("%s", name_for_port(reply->io_master.name)); +} + +PRE(host_get_io_master) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + } Request; +#pragma pack() + + // Request *req = (Request *)ARG1; + + PRINT("host_get_io_master(mach_host_self())"); + + AFTER = POST_FN(host_get_io_master); +} + + +POST(host_get_clock_service) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t clock_serv; + /* end of the kernel processed data */ + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + assign_port_name(reply->clock_serv.name, "clock-%p"); + PRINT("%s", name_for_port(reply->clock_serv.name)); +} + +PRE(host_get_clock_service) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + clock_id_t clock_id; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("host_get_clock_service(mach_host_self(), %d)", req->clock_id); + + AFTER = POST_FN(host_get_clock_service); +} + + +PRE(host_request_notification) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t notify_port; + /* end of the kernel processed data */ + NDR_record_t NDR; + host_flavor_t notify_type; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + if (MACH_REMOTE == mach_task_self()) { + if (req->notify_type == 0) { + PRINT("host_request_notification(mach_host_self(), %s, %s)", + "HOST_NOTIFY_CALENDAR_CHANGE", + name_for_port(req->notify_port.name)); + } else { + PRINT("host_request_notification(mach_host_self(), %d, %s)", + req->notify_type, + name_for_port(req->notify_port.name)); + } + } else { + PRINT("host_request_notification(%s, %d, %s)", + name_for_port(MACH_REMOTE), + req->notify_type, + name_for_port(req->notify_port.name)); + } + + // GrP fixme only do this on success + assign_port_name(req->notify_port.name, "host_notify-%p"); +} + + +/* --------------------------------------------------------------------- + mach_msg: messages to a task + ------------------------------------------------------------------ */ + + +PRE(mach_port_type) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_port_name_t name; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_port_type(%s, %s, ...)", + name_for_port(MACH_REMOTE), name_for_port(req->name)); + + AFTER = POST_FN(mach_port_type); +} + +POST(mach_port_type) +{ +} + + +PRE(mach_port_extract_member) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_port_name_t name; + mach_port_name_t pset; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_port_extract_member(%s, 0x%x, 0x%x)", + name_for_port(MACH_REMOTE), + req->name, req->pset); + + AFTER = POST_FN(mach_port_extract_member); + + // GrP fixme port tracker? +} + +POST(mach_port_extract_member) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (reply->RetCode) PRINT("mig return %d", reply->RetCode); +} + + +PRE(mach_port_allocate) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_port_right_t right; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_port_allocate(mach_task_self(), %d, ...)", req->right); + + MACH_ARG(mach_port_allocate.right) = req->right; + + AFTER = POST_FN(mach_port_allocate); +} + +POST(mach_port_allocate) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_port_name_t name; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + if (MACH_REMOTE == vg_task_port) { + // GrP fixme port tracking is too imprecise + // vg_assert(!port_exists(reply->name)); + record_unnamed_port(tid, reply->name, MACH_ARG(mach_port_allocate.right)); + PRINT("got port 0x%x", reply->name); + } else { + VG_(printf)("UNKNOWN inserted port 0x%x into remote task\n", reply->name); + } + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +PRE(mach_port_deallocate) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_port_name_t name; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_port_deallocate(%s, %s)", + name_for_port(MACH_REMOTE), + name_for_port(req->name)); + + MACH_ARG(mach_port.port) = req->name; + + AFTER = POST_FN(mach_port_deallocate); + + // Must block to prevent race (other thread allocates and + // notifies after we deallocate but before we notify) + *flags &= ~SfMayBlock; +} + +POST(mach_port_deallocate) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + if (MACH_REMOTE == vg_task_port) { + // Must have cleared SfMayBlock in PRE to prevent race + record_port_dealloc(MACH_ARG(mach_port.port)); + } else { + VG_(printf)("UNKNOWN remote port dealloc\n"); + } + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +PRE(mach_port_get_refs) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_port_name_t name; + mach_port_right_t right; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_port_get_refs(%s, %s, 0x%x)", + name_for_port(MACH_REMOTE), + name_for_port(req->name), req->right); + + MACH_ARG(mach_port_mod_refs.port) = req->name; + MACH_ARG(mach_port_mod_refs.right) = req->right; + + AFTER = POST_FN(mach_port_get_refs); +} + +POST(mach_port_get_refs) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_port_urefs_t refs; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + PRINT("got refs=%d", reply->refs); + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +PRE(mach_port_mod_refs) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_port_name_t name; + mach_port_right_t right; + mach_port_delta_t delta; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_port_mod_refs(%s, %s, 0x%x, 0x%x)", + name_for_port(MACH_REMOTE), + name_for_port(req->name), req->right, req->delta); + + MACH_ARG(mach_port_mod_refs.port) = req->name; + MACH_ARG(mach_port_mod_refs.right) = req->right; + MACH_ARG(mach_port_mod_refs.delta) = req->delta; + + AFTER = POST_FN(mach_port_mod_refs); + + // Must block to prevent race (other thread allocates and + // notifies after we deallocate but before we notify) + *flags &= ~SfMayBlock; +} + +POST(mach_port_mod_refs) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + if (MACH_REMOTE == vg_task_port) { + // Must have cleared SfMayBlock in PRE to prevent race + record_port_mod_refs(MACH_ARG(mach_port_mod_refs.port), + MACH_PORT_TYPE(MACH_ARG(mach_port_mod_refs.right)), + MACH_ARG(mach_port_mod_refs.delta)); + } else { + VG_(printf)("UNKNOWN remote port mod refs\n"); + } + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +PRE(mach_port_get_set_status) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_port_name_t name; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_port_get_set_status(%s, %s)", + name_for_port(MACH_REMOTE), + name_for_port(req->name)); + + AFTER = POST_FN(mach_port_get_set_status); +} + +POST(mach_port_get_set_status) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_ool_descriptor_t members; + /* end of the kernel processed data */ + NDR_record_t NDR; + mach_msg_type_number_t membersCnt; + mach_msg_trailer_t trailer; + } Reply; +#pragma pack() + + // Reply *reply = (Reply *)ARG1; + + // GrP fixme nothing to do? +} + + +PRE(mach_port_destroy) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_port_name_t name; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_port_destroy(%s, %s)", + name_for_port(MACH_REMOTE), + name_for_port(req->name)); + + MACH_ARG(mach_port.port) = req->name; + + AFTER = POST_FN(mach_port_destroy); + + // Must block to prevent race (other thread allocates and + // notifies after we deallocate but before we notify) + *flags &= ~SfMayBlock; +} + +POST(mach_port_destroy) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + if (MACH_REMOTE == vg_task_port) { + // Must have cleared SfMayBlock in PRE to prevent race + record_port_destroy(MACH_ARG(mach_port.port)); + } else { + VG_(printf)("UNKNOWN remote port destroy\n"); + } + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +PRE(mach_port_request_notification) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t notify; + /* end of the kernel processed data */ + NDR_record_t NDR; + mach_port_name_t name; + mach_msg_id_t msgid; + mach_port_mscount_t sync; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_port_request_notification(%s, %s, %d, %d, %d, %d, ...)", + name_for_port(MACH_REMOTE), + name_for_port(req->name), req->msgid, req->sync, + req->notify.name, req->notify.disposition); + + AFTER = POST_FN(mach_port_request_notification); +} + +POST(mach_port_request_notification) +{ + // GrP fixme port tracker? not sure +} + + +PRE(mach_port_insert_right) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t poly; + /* end of the kernel processed data */ + NDR_record_t NDR; + mach_port_name_t name; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_port_insert_right(%s, %s, %d, %d)", + name_for_port(MACH_REMOTE), + name_for_port(req->name), req->poly.name, req->poly.disposition); + + AFTER = POST_FN(mach_port_insert_right); + + if (MACH_REMOTE == mach_task_self()) { + // GrP fixme import_complex_message handles everything? + // what about export_complex_message for MOVE variants? + } else { + VG_(printf)("UNKNOWN mach_port_insert_right into remote task!\n"); + // GrP fixme also may remove rights from this task? + } + + // GrP fixme port tracker? +} + +POST(mach_port_insert_right) +{ +} + + +PRE(mach_port_get_attributes) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_port_name_t name; + mach_port_flavor_t flavor; + mach_msg_type_number_t port_info_outCnt; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_port_get_attributes(%s, %s, %d, ..., %d)", + name_for_port(MACH_REMOTE), + name_for_port(req->name), req->flavor, req->port_info_outCnt); + + AFTER = POST_FN(mach_port_get_attributes); +} + +POST(mach_port_get_attributes) +{ +} + + +PRE(mach_port_set_attributes) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_port_name_t name; + mach_port_flavor_t flavor; + mach_msg_type_number_t port_infoCnt; + integer_t port_info[10]; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_port_set_attributes(%s, %s, %d, ..., %d)", + name_for_port(MACH_REMOTE), + name_for_port(req->name), req->flavor, req->port_infoCnt); + + AFTER = POST_FN(mach_port_set_attributes); +} + +POST(mach_port_set_attributes) +{ +} + + +PRE(mach_port_insert_member) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_port_name_t name; + mach_port_name_t pset; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_port_insert_member(%s, 0x%x, 0x%x)", + name_for_port(MACH_REMOTE), req->name, req->pset); + + AFTER = POST_FN(mach_port_insert_member); + + // GrP fixme port tracker? +} + +POST(mach_port_insert_member) +{ +} + + +PRE(task_get_special_port) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + int which_port; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + switch (req->which_port) { + case TASK_KERNEL_PORT: + PRINT("task_get_special_port(%s, TASK_KERNEL_PORT)", + name_for_port(MACH_REMOTE)); + break; + case TASK_HOST_PORT: + PRINT("task_get_special_port(%s, TASK_HOST_PORT)", + name_for_port(MACH_REMOTE)); + break; + case TASK_BOOTSTRAP_PORT: + PRINT("task_get_special_port(%s, TASK_BOOTSTRAP_PORT)", + name_for_port(MACH_REMOTE)); + break; + case TASK_WIRED_LEDGER_PORT: + PRINT("task_get_special_port(%s, TASK_WIRED_LEDGER_PORT)", + name_for_port(MACH_REMOTE)); + break; + case TASK_PAGED_LEDGER_PORT: + PRINT("task_get_special_port(%s, TASK_PAGED_LEDGER_PORT)", + name_for_port(MACH_REMOTE)); + break; + default: + PRINT("task_get_special_port(%s, %d)", + name_for_port(MACH_REMOTE), req->which_port); + break; + } + + MACH_ARG(task_get_special_port.which_port) = req->which_port; + + AFTER = POST_FN(task_get_special_port); +} + +POST(task_get_special_port) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t special_port; + /* end of the kernel processed data */ + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + PRINT("got port %#x ", reply->special_port.name); + + switch (MACH_ARG(task_get_special_port.which_port)) { + case TASK_BOOTSTRAP_PORT: + vg_bootstrap_port = reply->special_port.name; + assign_port_name(reply->special_port.name, "bootstrap"); + break; + case TASK_KERNEL_PORT: + assign_port_name(reply->special_port.name, "kernel"); + break; + case TASK_HOST_PORT: + assign_port_name(reply->special_port.name, "host"); + break; + case TASK_WIRED_LEDGER_PORT: + assign_port_name(reply->special_port.name, "wired-ledger"); + break; + case TASK_PAGED_LEDGER_PORT: + assign_port_name(reply->special_port.name, "paged-ledger"); + break; + default: + assign_port_name(reply->special_port.name, "special-%p"); + break; + } + + PRINT("%s", name_for_port(reply->special_port.name)); +} + + +PRE(semaphore_create) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + int policy; + int value; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("semaphore_create(%s, ..., %d, %d)", + name_for_port(MACH_REMOTE), req->policy, req->value); + + AFTER = POST_FN(semaphore_create); +} + +POST(semaphore_create) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t semaphore; + /* end of the kernel processed data */ + mach_msg_trailer_t trailer; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + assign_port_name(reply->semaphore.name, "semaphore-%p"); + PRINT("%s", name_for_port(reply->semaphore.name)); +} + + +PRE(semaphore_destroy) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t semaphore; + /* end of the kernel processed data */ + mach_msg_trailer_t trailer; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("semaphore_destroy(%s, %s)", + name_for_port(MACH_REMOTE), name_for_port(req->semaphore.name)); + + record_port_destroy(req->semaphore.name); + + AFTER = POST_FN(semaphore_destroy); +} + +POST(semaphore_destroy) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_msg_trailer_t trailer; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + if (!reply->RetCode) { + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +PRE(mach_ports_lookup) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + } Request; +#pragma pack() + + // Request *req = (Request *)ARG1; + + PRINT("mach_ports_lookup(%s)", name_for_port(MACH_REMOTE)); + + AFTER = POST_FN(mach_ports_lookup); +} + +POST(mach_ports_lookup) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_ool_ports_descriptor_t init_port_set; + /* end of the kernel processed data */ + NDR_record_t NDR; + mach_msg_type_number_t init_port_setCnt; + } Reply; +#pragma pack() + + // Reply *reply = (Reply *)ARG1; +} + + +PRE(task_threads) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + } Request; +#pragma pack() + + // Request *req = (Request *)ARG1; + + PRINT("task_threads(%s)", name_for_port(MACH_REMOTE)); + + AFTER = POST_FN(task_threads); +} + +POST(task_threads) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_ool_ports_descriptor_t act_list; + /* end of the kernel processed data */ + NDR_record_t NDR; + mach_msg_type_number_t act_listCnt; + mach_msg_trailer_t trailer; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (MACH_REMOTE == vg_task_port) { + assign_port_names(&reply->act_list, "thread-%p"); + } else { + assign_port_names(&reply->act_list, "remote-thread-%p"); + } +} + + +PRE(task_suspend) +{ + PRINT("task_suspend(%s)", name_for_port(MACH_REMOTE)); + + if (MACH_REMOTE == vg_task_port) { + // GrP fixme self-suspend + vg_assert(0); + } else { + // suspend other - no problem + } + + AFTER = POST_FN(task_suspend); +} + +POST(task_suspend) +{ +} + + +PRE(task_resume) +{ + PRINT("task_resume(%s)", name_for_port(MACH_REMOTE)); + + if (MACH_REMOTE == vg_task_port) { + // GrP fixme self-resume + vg_assert(0); + } else { + // resume other - no problem + } + + AFTER = POST_FN(task_resume); +} + +POST(task_resume) +{ +} + + +PRE(vm_allocate) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + vm_address_t address; + vm_size_t size; + int flags; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("vm_allocate (%s, at %#x, size %d, flags %#x)", + name_for_port(MACH_REMOTE), + req->address, req->size, req->flags); + + MACH_ARG(vm_allocate.size) = req->size; + MACH_ARG(vm_allocate.flags) = req->flags; + + AFTER = POST_FN(vm_allocate); +} + +POST(vm_allocate) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + vm_address_t address; + mach_msg_trailer_t trailer; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + if (MACH_REMOTE == vg_task_port) { + PRINT("allocated at %#x", reply->address); + // requesting 0 bytes returns address 0 with no error + if (MACH_ARG(vm_allocate.size)) { + ML_(notify_core_and_tool_of_mmap)( + reply->address, MACH_ARG(vm_allocate.size), + VKI_PROT_READ|VKI_PROT_WRITE, VKI_MAP_ANON, -1, 0); + } + } else { + PRINT("allocated at %#x in remote task %s", reply->address, + name_for_port(MACH_REMOTE)); + } + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +PRE(vm_deallocate) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + vm_address_t address; + vm_size_t size; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("vm_deallocate(%s, at %#x, size %d)", + name_for_port(MACH_REMOTE), + req->address, req->size); + + MACH_ARG(vm_deallocate.address) = req->address; + MACH_ARG(vm_deallocate.size) = req->size; + + AFTER = POST_FN(vm_deallocate); + + // Must block to prevent race (other thread allocates and + // notifies after we deallocate but before we notify) + *flags &= ~SfMayBlock; +} + +POST(vm_deallocate) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_msg_trailer_t trailer; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + if (MACH_REMOTE == vg_task_port) { + if (MACH_ARG(vm_deallocate.size)) { + Addr start = VG_PGROUNDDN(MACH_ARG(vm_deallocate.address)); + Addr end = VG_PGROUNDUP(MACH_ARG(vm_deallocate.address) + + MACH_ARG(vm_deallocate.size)); + // Must have cleared SfMayBlock in PRE to prevent race + ML_(notify_core_and_tool_of_munmap)(start, end - start); + } + } + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +PRE(vm_protect) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + vm_address_t address; + vm_size_t size; + boolean_t set_maximum; + vm_prot_t new_protection; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("vm_protect(%s, at %#x, size %d, set_max %d, prot %d)", + name_for_port(MACH_REMOTE), req->address, req->size, + req->set_maximum, req->new_protection); + + MACH_ARG(vm_protect.address) = req->address; + MACH_ARG(vm_protect.size) = req->size; + MACH_ARG(vm_protect.set_maximum) = req->set_maximum; + MACH_ARG(vm_protect.new_protection) = req->new_protection; + + AFTER = POST_FN(vm_protect); +} + +POST(vm_protect) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_msg_trailer_t trailer; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + if (MACH_REMOTE == vg_task_port) { + Addr start = VG_PGROUNDDN(MACH_ARG(vm_protect.address)); + Addr end = VG_PGROUNDUP(MACH_ARG(vm_protect.address) + + MACH_ARG(vm_protect.size)); + UInt prot = MACH_ARG(vm_protect.new_protection); + if (MACH_ARG(vm_protect.set_maximum)) { + // GrP fixme mprotect max + VG_(printf)("UNKNOWN vm_protect set maximum"); + //VG_(mprotect_max_range)(start, end-start, prot); + } else { + ML_(notify_core_and_tool_of_mprotect)(start, end-start, prot); + } + } + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +PRE(vm_inherit) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + vm_address_t address; + vm_size_t size; + vm_inherit_t new_inheritance; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("vm_inherit(%s, at %#x, size %d, value %d)", + name_for_port(MACH_REMOTE), + req->address, req->size, + req->new_inheritance); + + AFTER = POST_FN(vm_inherit); +} + +POST(vm_inherit) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_msg_trailer_t trailer; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + if (MACH_REMOTE == vg_task_port) { + // GrP fixme do something? + } + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +PRE(vm_read) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + vm_address_t address; + vm_size_t size; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("vm_read(from %s at %#x size %u)", + name_for_port(MACH_REMOTE), req->address, req->size); + + MACH_ARG(vm_read.addr) = req->address; + MACH_ARG(vm_read.size) = req->size; + + AFTER = POST_FN(vm_read); +} + +POST(vm_read) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_ool_descriptor_t data; + /* end of the kernel processed data */ + NDR_record_t NDR; + mach_msg_type_number_t dataCnt; + } Reply; +#pragma pack() + + // Reply *reply = (Reply *)ARG1; + + if (MACH_REMOTE == vg_task_port) { + // vm_read from self + // GrP fixme copy initialized state + } +} + + + +PRE(mach_vm_read) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t address; + mach_vm_size_t size; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_vm_read(from %s at 0x%llx size %llu)", + name_for_port(MACH_REMOTE), req->address, req->size); + + MACH_ARG(mach_vm_read.addr) = req->address; + MACH_ARG(mach_vm_read.size) = req->size; + + AFTER = POST_FN(mach_vm_read); +} + +POST(mach_vm_read) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_ool_descriptor_t data; + /* end of the kernel processed data */ + NDR_record_t NDR; + mach_msg_type_number_t dataCnt; + } Reply; +#pragma pack() + + // Reply *reply = (Reply *)ARG1; + + if (MACH_REMOTE == vg_task_port) { + // vm_read from self + // GrP fixme copy initialized state + } +} + + +PRE(vm_read_overwrite) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + vm_address_t address; + vm_size_t size; + vm_address_t data; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("vm_read_overwrite(from %s at %#x size %u to %#x)", + name_for_port(MACH_REMOTE), req->address, req->size, req->data); + + MACH_ARG(vm_read_overwrite.addr) = req->address; + MACH_ARG(vm_read_overwrite.size) = req->size; + MACH_ARG(vm_read_overwrite.data) = req->data; + + PRE_MEM_WRITE("vm_read_overwrite(data)", req->data, req->size); + + AFTER = POST_FN(vm_read_overwrite); +} + +POST(vm_read_overwrite) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + vm_size_t outsize; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (reply->RetCode) { + PRINT("mig return %d", reply->RetCode); + } else { + PRINT("read %llu bytes", (unsigned long long)reply->outsize); + if (MACH_REMOTE == vg_task_port) { + // vm_read_overwrite from self + // GrP fixme copy initialized state + POST_MEM_WRITE(MACH_ARG(vm_read_overwrite.data), reply->outsize); + } else { + // vm_read_overwrite from remote + POST_MEM_WRITE(MACH_ARG(vm_read_overwrite.data), reply->outsize); + } + } +} + + +PRE(vm_copy) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + vm_address_t source_address; + vm_size_t size; + vm_address_t dest_address; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("vm_copy(%s, %#x, %d, %#x)", + name_for_port(MACH_REMOTE), + req->source_address, req->size, req->dest_address); + + MACH_ARG(vm_copy.src) = req->source_address; + MACH_ARG(vm_copy.dst) = req->dest_address; + MACH_ARG(vm_copy.size) = req->size; + + AFTER = POST_FN(vm_copy); +} + +POST(vm_copy) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_msg_trailer_t trailer; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + if (MACH_REMOTE == vg_task_port) { + // GrP fixme set dst's initialization equal to src's + // and wipe any symbols or translations in dst + } + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +PRE(vm_map) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t object; + /* end of the kernel processed data */ + NDR_record_t NDR; + vm_address_t address; + vm_size_t size; + vm_address_t mask; + int flags; + vm_offset_t offset; + boolean_t copy; + vm_prot_t cur_protection; + vm_prot_t max_protection; + vm_inherit_t inheritance; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + // GrP fixme check these + PRINT("vm_map(in %s, at %#x, size %d, from %s ...)", + name_for_port(MACH_REMOTE), + req->address, req->size, + name_for_port(req->object.name)); + + MACH_ARG(vm_map.size) = req->size; + MACH_ARG(vm_map.copy) = req->copy; + MACH_ARG(vm_map.protection) = (req->cur_protection & req->max_protection); + + AFTER = POST_FN(vm_map); +} + +POST(vm_map) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + vm_address_t address; + mach_msg_trailer_t trailer; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + // GrP fixme check src and dest tasks + PRINT("mapped at %#x", reply->address); + // GrP fixme max prot + ML_(notify_core_and_tool_of_mmap)( + reply->address, VG_PGROUNDUP(MACH_ARG(vm_map.size)), + MACH_ARG(vm_map.protection), VKI_MAP_SHARED, -1, 0); + // GrP fixme VKI_MAP_PRIVATE if !copy? + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +PRE(vm_remap) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t src_task; + /* end of the kernel processed data */ + NDR_record_t NDR; + vm_address_t target_address; + vm_size_t size; + vm_address_t mask; + boolean_t anywhere; + vm_address_t src_address; + boolean_t copy; + vm_inherit_t inheritance; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + // GrP fixme check src and dest tasks + + if (VG_(clo_trace_syscalls)) { + mach_port_name_t source_task = req->src_task.name; + if (source_task == mach_task_self()) { + PRINT("vm_remap(mach_task_self(), " + "to %#x size %d, from mach_task_self() at %#x, ...)", + req->target_address, req->size, req->src_address); + } else { + PRINT("vm_remap(mach_task_self(), " + "to %#x size %d, from task %u at %#x, ...)", + req->target_address, req->size, + source_task, req->src_address); + } + } + + // arg1 is task + // vt->syscall_arg2 = req->target_address; + MACH_ARG(vm_remap.size) = req->size; + // vt->syscall_arg4 = req->copy; + + AFTER = POST_FN(vm_remap); +} + +POST(vm_remap) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + vm_address_t target_address; + vm_prot_t cur_protection; + vm_prot_t max_protection; + mach_msg_trailer_t trailer; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + // GrP fixme check src and dest tasks + UInt prot = reply->cur_protection & reply->max_protection; + // GrP fixme max prot + PRINT("mapped at %#x", reply->target_address); + ML_(notify_core_and_tool_of_mmap)( + reply->target_address, VG_PGROUNDUP(MACH_ARG(vm_remap.size)), + prot, VKI_MAP_SHARED, -1, 0); + // GrP fixme VKI_MAP_FIXED if !copy? + // GrP fixme copy initialized bits from source to dest if source_task is also mach_task_self + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +PRE(mach_make_memory_entry_64) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t parent_entry; + /* end of the kernel processed data */ + NDR_record_t NDR; + memory_object_size_t size; + memory_object_offset_t offset; + vm_prot_t permission; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_make_memory_entry_64(%s, %llu, %llu, %d, ..., %u)", + name_for_port(MACH_REMOTE), + req->size, req->offset, req->permission, req->parent_entry.type); + + AFTER = POST_FN(mach_make_memory_entry_64); +} + +POST(mach_make_memory_entry_64) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t object; + NDR_record_t NDR; + memory_object_size_t size; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) { + assign_port_name(reply->object.name, "memory-%p"); + PRINT("%s", name_for_port(reply->object.name)); + } +} + + +PRE(vm_purgable_control) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + vm_address_t address; + vm_purgable_t control; + int state; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("vm_purgable_control(%s, %#x, %d, %d)", + name_for_port(MACH_REMOTE), + req->address, req->control, req->state); + + // GrP fixme verify address? + + AFTER = POST_FN(vm_purgable_control); +} + +POST(vm_purgable_control) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + int state; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +PRE(mach_vm_purgable_control) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t address; + vm_purgable_t control; + int state; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_vm_purgable_control(%s, 0x%llx, %d, %d)", + name_for_port(MACH_REMOTE), + (unsigned long long)req->address, req->control, req->state); + + // GrP fixme verify address? + + AFTER = POST_FN(mach_vm_purgable_control); +} + +POST(mach_vm_purgable_control) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + int state; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +PRE(mach_vm_allocate) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t address; + mach_vm_size_t size; + int flags; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_vm_allocate (%s, at 0x%llx, size %lld, flags 0x%x)", + name_for_port(MACH_REMOTE), + req->address, req->size, req->flags); + + MACH_ARG(mach_vm_allocate.size) = req->size; + MACH_ARG(mach_vm_allocate.flags) = req->flags; + + AFTER = POST_FN(mach_vm_allocate); +} + +POST(mach_vm_allocate) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_vm_address_t address; + mach_msg_trailer_t trailer; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + if (MACH_REMOTE == vg_task_port) { + PRINT("allocated at 0x%llx", reply->address); + // requesting 0 bytes returns address 0 with no error + if (MACH_ARG(mach_vm_allocate.size)) { + ML_(notify_core_and_tool_of_mmap)( + reply->address, MACH_ARG(mach_vm_allocate.size), + VKI_PROT_READ|VKI_PROT_WRITE, VKI_MAP_ANON, -1, 0); + } + } else { + PRINT("allocated at 0x%llx in remote task %s", reply->address, + name_for_port(MACH_REMOTE)); + } + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +PRE(mach_vm_deallocate) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t address; + mach_vm_size_t size; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_vm_deallocate(%s, at 0x%llx, size %lld)", + name_for_port(MACH_REMOTE), + req->address, req->size); + + MACH_ARG(mach_vm_deallocate.address) = req->address; + MACH_ARG(mach_vm_deallocate.size) = req->size; + + AFTER = POST_FN(mach_vm_deallocate); + + // Must block to prevent race (other thread allocates and + // notifies after we deallocate but before we notify) + *flags &= ~SfMayBlock; +} + +POST(mach_vm_deallocate) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_msg_trailer_t trailer; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + if (MACH_REMOTE == vg_task_port) { + if (MACH_ARG(mach_vm_deallocate.size)) { + Addr start = VG_PGROUNDDN(MACH_ARG(mach_vm_deallocate.address)); + Addr end = VG_PGROUNDUP(MACH_ARG(mach_vm_deallocate.address) + + MACH_ARG(mach_vm_deallocate.size)); + // Must have cleared SfMayBlock in PRE to prevent race + ML_(notify_core_and_tool_of_munmap)(start, end - start); + } + } + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +PRE(mach_vm_protect) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t address; + mach_vm_size_t size; + boolean_t set_maximum; + vm_prot_t new_protection; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_vm_protect(%s, at 0x%llx, size %lld, set_max %d, prot %d)", + name_for_port(MACH_REMOTE), req->address, req->size, + req->set_maximum, req->new_protection); + + MACH_ARG(mach_vm_protect.address) = req->address; + MACH_ARG(mach_vm_protect.size) = req->size; + MACH_ARG(mach_vm_protect.set_maximum) = req->set_maximum; + MACH_ARG(mach_vm_protect.new_protection) = req->new_protection; + + AFTER = POST_FN(mach_vm_protect); +} + +POST(mach_vm_protect) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_msg_trailer_t trailer; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + if (MACH_REMOTE == vg_task_port) { + Addr start = VG_PGROUNDDN(MACH_ARG(mach_vm_protect.address)); + Addr end = VG_PGROUNDUP(MACH_ARG(mach_vm_protect.address) + + MACH_ARG(mach_vm_protect.size)); + UInt prot = MACH_ARG(mach_vm_protect.new_protection); + if (MACH_ARG(mach_vm_protect.set_maximum)) { + // DDD: #warning GrP fixme mprotect max + //VG_(mprotect_max_range)(start, end-start, prot); + } else { + ML_(notify_core_and_tool_of_mprotect)(start, end-start, prot); + } + } + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +PRE(mach_vm_inherit) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t address; + mach_vm_size_t size; + vm_inherit_t new_inheritance; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_vm_inherit(to %s, at 0x%llx, size %llu, value %u)", + name_for_port(MACH_REMOTE), + req->address, req->size, req->new_inheritance); + + AFTER = POST_FN(mach_vm_inherit); +} + +POST(mach_vm_inherit) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_msg_trailer_t trailer; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + // no V-visible side effects + // GrP fixme except maybe fork/exec + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +PRE(mach_vm_copy) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t source_address; + mach_vm_size_t size; + mach_vm_address_t dest_address; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_vm_copy(%s, 0x%llx, %llu, 0x%llx)", + name_for_port(MACH_REMOTE), + req->source_address, req->size, req->dest_address); + + // arg1 is task + // vt->syscall_arg2 = req->source_address; + // vt->syscall_arg3 = req->size; + // vt->syscall_arg4 = req->dest_address; + + AFTER = POST_FN(mach_vm_copy); +} + +POST(mach_vm_copy) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_msg_trailer_t trailer; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + if (MACH_REMOTE == vg_task_port) { + // GrP fixme set dest's initialization equal to src's + // BUT vm_copy allocates no memory + } + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +PRE(mach_vm_map) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t object; + /* end of the kernel processed data */ + NDR_record_t NDR; + mach_vm_address_t address; + mach_vm_size_t size; + mach_vm_address_t mask; + int flags; + memory_object_offset_t offset; + boolean_t copy; + vm_prot_t cur_protection; + vm_prot_t max_protection; + vm_inherit_t inheritance; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + // GrP fixme check these + PRINT("mach_vm_map(in %s, at 0x%llx, size %llu, from %s ...)", + name_for_port(MACH_REMOTE), + req->address, req->size, + name_for_port(req->object.name)); + + MACH_ARG(mach_vm_map.size) = req->size; + MACH_ARG(mach_vm_map.copy) = req->copy; + MACH_ARG(mach_vm_map.protection) = + (req->cur_protection & req->max_protection); + + AFTER = POST_FN(mach_vm_map); +} + +POST(mach_vm_map) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_vm_address_t address; + mach_msg_trailer_t trailer; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + // GrP fixme check src and dest tasks + PRINT("mapped at 0x%llx", reply->address); + // GrP fixme max prot + ML_(notify_core_and_tool_of_mmap)( + reply->address, VG_PGROUNDUP(MACH_ARG(mach_vm_map.size)), + MACH_ARG(mach_vm_map.protection), VKI_MAP_SHARED, -1, 0); + // GrP fixme VKI_MAP_PRIVATE if !copy? + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +PRE(mach_vm_region_recurse) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + mach_vm_address_t address; + natural_t nesting_depth; + mach_msg_type_number_t infoCnt; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("mach_vm_region_recurse(in %s, at 0x%llx, depth %u, count %u)", + name_for_port(MACH_REMOTE), + req->address, req->nesting_depth, req->infoCnt); + + AFTER = POST_FN(mach_vm_region_recurse); +} + +POST(mach_vm_region_recurse) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_vm_address_t address; + mach_vm_size_t size; + natural_t nesting_depth; + mach_msg_type_number_t infoCnt; + int info[19]; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (!reply->RetCode) { + PRINT("got region at 0x%llx, size %llu, depth %u, count %u", + reply->address, reply->size, + reply->nesting_depth, reply->infoCnt); + // GrP fixme mark info contents beyond infoCnt as bogus + } else { + PRINT("mig return %d", reply->RetCode); + } +} + + +/* --------------------------------------------------------------------- + mach_msg: messages to thread + ------------------------------------------------------------------ */ + + + +POST(thread_terminate) +{ +} + + +PRE(thread_terminate) +{ + mach_msg_header_t *mh = (mach_msg_header_t *)ARG1; + Bool self_terminate = (mh->msgh_request_port == MACH_THREAD); + + PRINT("thread_terminate(%s)", name_for_port(mh->msgh_request_port)); + + AFTER = POST_FN(thread_terminate); + + if (self_terminate) { + // Terminating this thread. + // Copied from sys_exit. + ThreadState *tst = VG_(get_ThreadState)(tid); + tst->exitreason = VgSrc_ExitThread; + tst->os_state.exitcode = 0; // GrP fixme anything better? + // What we would like to do is: + // SET_STATUS_Success(0); + // but that doesn't work, because this is a MACH-class syscall, + // and SET_STATUS_Success creates a UNIX-class syscall result. + // Hence we have to laboriously construct the full SysRes "by hand" + // and use that to set the syscall return status. + SET_STATUS_from_SysRes( + VG_(mk_SysRes_x86_darwin)( + VG_DARWIN_SYSCALL_CLASS_MACH, + False/*success*/, 0, 0 + ) + ); + *flags &= ~SfMayBlock; // clear flag set by PRE(mach_msg) + } else { + // Terminating some other thread. + // Do keep the scheduler lock while terminating any other thread. + // Otherwise we might halt the other thread while it holds the lock, + // which would deadlock the process. + // GrP fixme good enough? + // GrP fixme need to clean up other thread's valgrind data? + } +} + + +POST(thread_create) +{ +} + + +PRE(thread_create) +{ + PRINT("thread_create(mach_task_self(), ...)"); + + AFTER = POST_FN(thread_create); + + // GrP fixme + VG_(core_panic)("thread_create() unimplemented"); +} + + +PRE(thread_create_running) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + thread_state_flavor_t flavor; + mach_msg_type_number_t new_stateCnt; + natural_t new_state[144]; + } Request; +#pragma pack() + + Request *req; + thread_state_t regs; + ThreadState *new_thread; + + PRINT("thread_create_running(mach_task_self(), ...)"); + + // The new thread will immediately begin execution, + // so we need to hijack the register state here. + + req = (Request *)ARG1; + regs = (thread_state_t)req->new_state; + + // Build virtual thread. + new_thread = build_thread(regs, req->flavor, req->new_stateCnt); + + // Edit the thread state to send to the real kernel. + hijack_thread_state(regs, req->flavor, req->new_stateCnt, new_thread); + + AFTER = POST_FN(thread_create_running); +} + + +POST(thread_create_running) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t child_act; + /* end of the kernel processed data */ + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + assign_port_name(reply->child_act.name, "thread-%p"); + PRINT("%s", name_for_port(reply->child_act.name)); +} + + +PRE(sys_bsdthread_create) +{ + ThreadState *tst; + + PRINT("bsdthread_create( %#lx, %#lx, %#lx, %#lx, %#lx )", + ARG1, ARG2, ARG3, ARG4, ARG5); + PRE_REG_READ5(pthread_t,"bsdthread_create", + void *,"func", void *,"func_arg", void *,"stack", + pthread_t,"thread", unsigned int,"flags"); + + // The kernel will call V's pthread_hijack() to launch the thread. + // Here we allocate the thread state and pass it to pthread_hijack() + // via the func_arg parameter. + + tst = VG_(get_ThreadState)(VG_(alloc_ThreadState)()); + allocstack(tst->tid); + + tst->os_state.func_arg = (Addr)ARG2; + ARG2 = (Word)tst; + + // Create a semaphore that pthread_hijack will signal once it starts + // POST(sys_bsdthread_create) needs to wait for the new memory map to appear + semaphore_create(mach_task_self(), &tst->os_state.child_go, + SYNC_POLICY_FIFO, 0); + semaphore_create(mach_task_self(), &tst->os_state.child_done, + SYNC_POLICY_FIFO, 0); +} + +POST(sys_bsdthread_create) +{ + // Tell new thread's pthread_hijack to proceed, and wait for it to finish. + // We hold V's lock on the child's behalf. + // If we return before letting pthread_hijack do its thing, V thinks + // the new pthread struct is still unmapped when we return to libc, + // causing false errors. + + ThreadState *tst = (ThreadState *)ARG2; + semaphore_signal(tst->os_state.child_go); + semaphore_wait(tst->os_state.child_done); + semaphore_destroy(mach_task_self(), tst->os_state.child_go); + semaphore_destroy(mach_task_self(), tst->os_state.child_done); + + // GrP fixme semaphore destroy needed when thread creation fails + // GrP fixme probably other cleanup too + + // DDD: I'm not at all sure this is the right spot for this. It probably + // should be in pthread_hijack instead, just before the call to + // start_thread_NORETURN(), call_on_new_stack_0_1(), but we don't have the + // parent tid value there... + VG_TRACK ( pre_thread_ll_create, tid, tst->tid ); +} + + +PRE(sys_bsdthread_terminate) +{ + ThreadState *tst; + + PRINT("bsdthread_terminate( %#lx, %lx, %s, %s )", + ARG1, ARG2, name_for_port(ARG3), name_for_port(ARG4)); + PRE_REG_READ4(int,"bsdthread_terminate", + void *,"freeaddr", size_t,"freesize", + mach_port_t,"kport", mach_port_t,"joinsem"); + + // Free memory and signal semaphore. + // GrP fixme errors? + if (ARG4) semaphore_signal((semaphore_t)ARG4); + if (ARG1 && ARG2) { + ML_(notify_core_and_tool_of_munmap)(ARG1, ARG2); + vm_deallocate(mach_task_self(), (vm_address_t)ARG1, (vm_size_t)ARG2); + } + + // Tell V to terminate the thread. + // Copied from sys_exit. + tst = VG_(get_ThreadState)(tid); + tst->exitreason = VgSrc_ExitThread; + tst->os_state.exitcode = 0; // GrP fixme anything better? + SET_STATUS_Success(0); +} + + +POST(thread_suspend) +{ +} + +PRE(thread_suspend) +{ + mach_msg_header_t *mh = (mach_msg_header_t *)ARG1; + Bool self_suspend = (mh->msgh_request_port == MACH_THREAD); + + PRINT("thread_suspend(%s)", name_for_port(mh->msgh_request_port)); + + AFTER = POST_FN(thread_suspend); + + if (self_suspend) { + // Don't keep the scheduler lock while self-suspending. + // Otherwise we might halt while still holding the lock, + // which would deadlock the process. + *flags |= SfMayBlock; + } else { + // Do keep the scheduler lock while suspending any other thread. + // Otherwise we might halt the other thread while it holds the lock, + // which would deadlock the process. + } +} + + +POST(thread_get_state) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_msg_type_number_t old_stateCnt; + natural_t old_state[144]; + mach_msg_trailer_t trailer; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + // mach_port_t thread = MACH_ARG(thread_get_state.thread); + thread_state_flavor_t flavor = MACH_ARG(thread_get_state.flavor); + + if (!reply->RetCode) { + thread_state_from_vex((thread_state_t)reply->old_state, + flavor, reply->old_stateCnt, + &VG_(get_ThreadState)(tid)->arch.vex); + } else { + PRINT("mig return %d", reply->RetCode); + } +} + +PRE(thread_get_state) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + thread_state_flavor_t flavor; + mach_msg_type_number_t old_stateCnt; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + // Bool self = (req->Head.msgh_request_port == MACH_THREAD); + + // GrP fixme if (self) { + PRINT("thread_get_state(%s, %d)", + name_for_port(req->Head.msgh_request_port), req->flavor); + /*} else { + PRINT("thread_get_state(0x%x, %d)", + req->Head.msgh_request_port, req->flavor); + }*/ + + // Hack the thread state after making the real call. + MACH_ARG(thread_get_state.thread) = req->Head.msgh_request_port; + MACH_ARG(thread_get_state.flavor) = req->flavor; + + AFTER = POST_FN(thread_get_state); +} + + +POST(thread_policy) +{ +} + +PRE(thread_policy) +{ + mach_msg_header_t *mh = (mach_msg_header_t *)ARG1; + // Bool self = (mh->msgh_request_port == MACH_THREAD); + + // GrP fixme if (self) { + PRINT("thread_policy(%s, ...)", name_for_port(mh->msgh_request_port)); + /*} else { + PRINT("thread_policy(thread 0x%x, ...)", mh->msgh_request_port); + }*/ + + AFTER = POST_FN(thread_policy); +} + + +PRE(thread_info) +{ + mach_msg_header_t *mh = (mach_msg_header_t *)ARG1; + + PRINT("thread_info(%s, ...)", name_for_port(mh->msgh_request_port)); + // GrP fixme does any thread info need to be hijacked? + + AFTER = POST_FN(thread_info); +} + +POST(thread_info) +{ + // GrP fixme mark unused parts of thread_info_out as uninitialized? +} + + + +/* --------------------------------------------------------------------- + mach_msg: messages to bootstrap port + ------------------------------------------------------------------ */ + + +POST(bootstrap_register) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + mach_msg_trailer_t trailer; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if (reply->RetCode) PRINT("mig return %d", reply->RetCode); +} + +PRE(bootstrap_register) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t service_port; + /* end of the kernel processed data */ + NDR_record_t NDR; + name_t service_name; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("bootstrap_register(port 0x%x, \"%s\")", + req->service_port.name, req->service_name); + + assign_port_name(req->service_port.name, req->service_name); + + AFTER = POST_FN(bootstrap_register); +} + + +POST(bootstrap_look_up) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t service_port; + /* end of the kernel processed data */ + mach_msg_trailer_t trailer; + } Reply; +#pragma pack() + + Reply *reply = (Reply *)ARG1; + + if ((reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && + reply->service_port.name) + { + assign_port_name(reply->service_port.name, + MACH_ARG(bootstrap_look_up.service_name)); + PRINT("%s", name_for_port(reply->service_port.name)); + } else { + PRINT("not found"); + } + VG_(arena_free)(VG_AR_CORE, MACH_ARG(bootstrap_look_up.service_name)); +} + +PRE(bootstrap_look_up) +{ +#pragma pack(4) + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + name_t service_name; + } Request; +#pragma pack() + + Request *req = (Request *)ARG1; + + PRINT("bootstrap_look_up(\"%s\")", req->service_name); + + MACH_ARG(bootstrap_look_up.service_name) = + VG_(arena_strdup)(VG_AR_CORE, "syswrap-darwin.bootstrap-name", + req->service_name); + + AFTER = POST_FN(bootstrap_look_up); +} + + +/* --------------------------------------------------------------------- + mach_msg: receiver-specific handlers + ------------------------------------------------------------------ */ + + +POST(mach_msg_receive) +{ + // mach_msg_header_t *mh = (mach_msg_header_t *)ARG1; + + // GrP fixme don't know of anything interesting here currently + // import_complex_message handles everything + // PRINT("UNHANDLED reply %d", mh->msgh_id); + + // Assume the call may have mapped or unmapped memory + sync_mappings("after", "mach_msg_receive", 0); +} + +PRE(mach_msg_receive) +{ + mach_msg_header_t *mh = (mach_msg_header_t *)ARG1; + + PRINT("mach_msg_receive(port %s)", name_for_port(mh->msgh_reply_port)); + + AFTER = POST_FN(mach_msg_receive); + + // no message sent, only listening for a reply + // assume message may block + *flags |= SfMayBlock; +} + + +PRE(mach_msg_bootstrap) +{ + // message to bootstrap port + + mach_msg_header_t *mh = (mach_msg_header_t *)ARG1; + + switch (mh->msgh_id) { + case 403: + CALL_PRE(bootstrap_register); + return; + case 404: + CALL_PRE(bootstrap_look_up); + return; + + default: + PRINT("UNHANDLED bootstrap message [id %d, to %s, reply 0x%x]\n", + mh->msgh_id, name_for_port(mh->msgh_request_port), + mh->msgh_reply_port); + return; + } +} + + +PRE(mach_msg_host) +{ + // message to host self - check for host-level kernel calls + + mach_msg_header_t *mh = (mach_msg_header_t *)ARG1; + + switch (mh->msgh_id) { + case 200: + CALL_PRE(host_info); + return; + case 202: + CALL_PRE(host_page_size); + return; + case 205: + CALL_PRE(host_get_io_master); + return; + case 206: + CALL_PRE(host_get_clock_service); + return; + case 217: + CALL_PRE(host_request_notification); + return; + + default: + // unknown message to host self + VG_(printf)("UNKNOWN host message [id %d, to %s, reply 0x%x]\n", + mh->msgh_id, name_for_port(mh->msgh_request_port), + mh->msgh_reply_port); + return; + } +} + +PRE(mach_msg_task) +{ + // message to a task port + + mach_msg_header_t *mh = (mach_msg_header_t *)ARG1; + + switch (mh->msgh_id) { + case 3201: + CALL_PRE(mach_port_type); + return; + case 3204: + CALL_PRE(mach_port_allocate); + return; + case 3205: + CALL_PRE(mach_port_destroy); + return; + case 3206: + CALL_PRE(mach_port_deallocate); + return; + case 3207: + CALL_PRE(mach_port_get_refs); + return; + case 3208: + CALL_PRE(mach_port_mod_refs); + return; + case 3211: + CALL_PRE(mach_port_get_set_status); + return; + case 3213: + CALL_PRE(mach_port_request_notification); + return; + case 3214: + CALL_PRE(mach_port_insert_right); + return; + case 3217: + CALL_PRE(mach_port_get_attributes); + return; + case 3218: + CALL_PRE(mach_port_set_attributes); + return; + case 3226: + CALL_PRE(mach_port_insert_member); + return; + case 3227: + CALL_PRE(mach_port_extract_member); + return; + + case 3402: + CALL_PRE(task_threads); + return; + case 3404: + CALL_PRE(mach_ports_lookup); + return; + + case 3407: + CALL_PRE(task_suspend); + return; + case 3408: + CALL_PRE(task_resume); + return; + + case 3409: + CALL_PRE(task_get_special_port); + return; + case 3411: + CALL_PRE(thread_create); + return; + case 3412: + CALL_PRE(thread_create_running); + return; + + case 3418: + CALL_PRE(semaphore_create); + return; + case 3419: + CALL_PRE(semaphore_destroy); + return; + + case 3801: + CALL_PRE(vm_allocate); + return; + case 3802: + CALL_PRE(vm_deallocate); + return; + case 3803: + CALL_PRE(vm_protect); + return; + case 3804: + CALL_PRE(vm_inherit); + return; + case 3805: + CALL_PRE(vm_read); + return; + case 3808: + CALL_PRE(vm_copy); + return; + case 3809: + CALL_PRE(vm_read_overwrite); + return; + case 3812: + CALL_PRE(vm_map); + return; + case 3814: + CALL_PRE(vm_remap); + return; + case 3825: + CALL_PRE(mach_make_memory_entry_64); + return; + case 3830: + CALL_PRE(vm_purgable_control); + return; + + case 4800: + CALL_PRE(mach_vm_allocate); + return; + case 4801: + CALL_PRE(mach_vm_deallocate); + return; + case 4802: + CALL_PRE(mach_vm_protect); + return; + case 4803: + CALL_PRE(mach_vm_inherit); + return; + case 4804: + CALL_PRE(mach_vm_read); + return; + case 4807: + CALL_PRE(mach_vm_copy); + return; + case 4811: + CALL_PRE(mach_vm_map); + return; + case 4815: + CALL_PRE(mach_vm_region_recurse); + return; + case 4817: + CALL_PRE(mach_make_memory_entry_64); + return; + case 4818: + CALL_PRE(mach_vm_purgable_control); + return; + + default: + // unknown message to task self + VG_(printf)("UNKNOWN task message [id %d, to %s, reply 0x%x]\n", + mh->msgh_id, name_for_port(mh->msgh_remote_port), + mh->msgh_reply_port); + return; + } +} + + +PRE(mach_msg_thread) +{ + // message to local thread - check for thread-level kernel calls + + mach_msg_header_t *mh = (mach_msg_header_t *)ARG1; + + switch (mh->msgh_id) { + case 3600: + CALL_PRE(thread_terminate); + return; + case 3603: + CALL_PRE(thread_get_state); + return; + case 3605: + CALL_PRE(thread_suspend); + return; + case 3612: + CALL_PRE(thread_info); + return; + case 3616: + CALL_PRE(thread_policy); + return; + default: + // unknown message to a thread + VG_(printf)("UNKNOWN thread message [id %d, to %s, reply 0x%x]\n", + mh->msgh_id, name_for_port(mh->msgh_request_port), + mh->msgh_reply_port); + return; + } +} + + +static int is_thread_port(mach_port_t port) +{ + if (port == 0) return False; + + return VG_(lwpid_to_vgtid)(port) != VG_INVALID_THREADID; +} + + +static int is_task_port(mach_port_t port) +{ + if (port == 0) return False; + + if (port == vg_task_port) return True; + + return (0 == VG_(strncmp)("task-", name_for_port(port), 5)); +} + + +/* --------------------------------------------------------------------- + mach_msg: base handlers + ------------------------------------------------------------------ */ + +PRE(mach_msg) +{ + mach_msg_header_t *mh = (mach_msg_header_t *)ARG1; + mach_msg_option_t option = (mach_msg_option_t)ARG2; + // mach_msg_size_t send_size = (mach_msg_size_t)ARG3; + mach_msg_size_t rcv_size = (mach_msg_size_t)ARG4; + // mach_port_t rcv_name = (mach_port_t)ARG5; + size_t complex_header_size = 0; + + PRE_REG_READ7(long, "mach_msg", + mach_msg_header_t*,"msg", mach_msg_option_t,"option", + mach_msg_size_t,"send_size", mach_msg_size_t,"rcv_size", + mach_port_t,"rcv_name", mach_msg_timeout_t,"timeout", + mach_port_t,"notify"); + + // Assume default POST handler until specified otherwise + AFTER = NULL; + + // Assume call may block unless specified otherwise + *flags |= SfMayBlock; + + if (option & MACH_SEND_MSG) { + // Validate outgoing message header + PRE_MEM_READ("mach_msg(msg.msgh_bits)", + (Addr)&mh->msgh_bits, sizeof(mh->msgh_bits)); + // msgh_size not required, use parameter instead + PRE_MEM_READ("mach_msg(msg.msgh_remote_port)", + (Addr)&mh->msgh_remote_port, sizeof(mh->msgh_remote_port)); + PRE_MEM_READ("mach_msg(msg.msgh_local_port)", + (Addr)&mh->msgh_local_port, sizeof(mh->msgh_local_port)); + // msgh_reserved not required + PRE_MEM_READ("mach_msg(msg.msgh_id)", + (Addr)&mh->msgh_id, sizeof(mh->msgh_id)); + + if (mh->msgh_bits & MACH_MSGH_BITS_COMPLEX) { + // Validate typed message data and handle memory map changes. + complex_header_size = export_complex_message(tid, mh); + } + + // GrP fixme handle sender-specified message trailer + // (but is this only for too-secure processes?) + vg_assert(! (mh->msgh_bits & MACH_SEND_TRAILER)); + + MACH_REMOTE = mh->msgh_remote_port; + MACH_MSGH_ID = mh->msgh_id; + } + + if (option & MACH_RCV_MSG) { + // Pre-validate receive buffer + PRE_MEM_WRITE("mach_msg(receive buffer)", (Addr)mh, rcv_size); + } + + // Call a PRE handler. The PRE handler may set an AFTER handler. + + if (!(option & MACH_SEND_MSG)) { + // no message sent, receive only + CALL_PRE(mach_msg_receive); + return; + } + else if (mh->msgh_request_port == vg_host_port) { + // message sent to mach_host_self() + CALL_PRE(mach_msg_host); + return; + } + else if (is_task_port(mh->msgh_request_port)) { + // message sent to a task + CALL_PRE(mach_msg_task); + return; + } + else if (mh->msgh_request_port == vg_bootstrap_port) { + // message sent to bootstrap port + CALL_PRE(mach_msg_bootstrap); + return; + } + else if (is_thread_port(mh->msgh_request_port)) { + // message sent to one of this process's threads + CALL_PRE(mach_msg_thread); + return; + } + else { + // arbitrary message to arbitrary port + PRINT("UNHANDLED mach_msg [id %d, to %s, reply 0x%x]", + mh->msgh_id, name_for_port(mh->msgh_request_port), + mh->msgh_reply_port); + + AFTER = POST_FN(mach_msg_unhandled); + + // Assume the entire message body may be read. + // GrP fixme generates false positives for unknown protocols + /* + PRE_MEM_READ("mach_msg(payload)", + (Addr)((char*)mh + sizeof(mach_msg_header_t) + complex_header_size), + send_size - sizeof(mach_msg_header_t) - complex_header_size); + */ + return; + } +} + +POST(mach_msg) +{ + mach_msg_header_t *mh = (mach_msg_header_t *)ARG1; + mach_msg_option_t option = (mach_msg_option_t)ARG2; + + if (option & MACH_RCV_MSG) { + if (RES != 0) { + // error during send or receive + // GrP fixme need to clean up port rights? + } else { + mach_msg_trailer_t *mt = + (mach_msg_trailer_t *)((Addr)mh + round_msg(mh->msgh_size)); + + // Assume the entire received message and trailer is initialized + // GrP fixme would being more specific catch any bugs? + POST_MEM_WRITE((Addr)mh, + round_msg(mh->msgh_size) + mt->msgh_trailer_size); + + if (mh->msgh_bits & MACH_MSGH_BITS_COMPLEX) { + // Update memory map for out-of-line message data + import_complex_message(tid, mh); + } + } + } + + // Call handler chosen by PRE(mach_msg) + if (AFTER) { + (*AFTER)(tid, arrghs, status); + } +} + + +POST(mach_msg_unhandled) +{ + sync_mappings("after", "mach_msg_unhandled", 0); +} + + +/* --------------------------------------------------------------------- + other Mach traps + ------------------------------------------------------------------ */ + +PRE(mach_reply_port) +{ + PRINT("mach_reply_port()"); +} + +POST(mach_reply_port) +{ + record_named_port(tid, RES, MACH_PORT_RIGHT_RECEIVE, "reply-%p"); + PRINT("reply port %s", name_for_port(RES)); +} + + +PRE(mach_thread_self) +{ + PRINT("mach_thread_self()"); +} + +POST(mach_thread_self) +{ + record_named_port(tid, RES, MACH_PORT_RIGHT_SEND, "thread-%p"); + PRINT("thread %#lx", RES); +} + + +PRE(mach_host_self) +{ + PRINT("mach_host_self()"); +} + +POST(mach_host_self) +{ + vg_host_port = RES; + record_named_port(tid, RES, MACH_PORT_RIGHT_SEND, "mach_host_self()"); + PRINT("host %#lx", RES); +} + + +PRE(mach_task_self) +{ + PRINT("mach_task_self()"); +} + +POST(mach_task_self) +{ + vg_task_port = RES; + record_named_port(tid, RES, MACH_PORT_RIGHT_SEND, "mach_task_self()"); + PRINT("task %#lx", RES); +} + + +PRE(syscall_thread_switch) +{ + PRINT("syscall_thread_switch(%s, %ld, %ld)", + name_for_port(ARG1), ARG2, ARG3); + PRE_REG_READ3(long, "syscall_thread_switch", + mach_port_t,"thread", int,"option", natural_t,"timeout"); + + *flags |= SfMayBlock; +} + + +PRE(semaphore_signal) +{ + PRINT("semaphore_signal(%s)", name_for_port(ARG1)); + PRE_REG_READ1(long, "semaphore_signal", semaphore_t,"semaphore"); +} + + +PRE(semaphore_signal_all) +{ + PRINT("semaphore_signal_all(%s)", name_for_port(ARG1)); + PRE_REG_READ1(long, "semaphore_signal_all", semaphore_t,"semaphore"); +} + + +PRE(semaphore_signal_thread) +{ + PRINT("semaphore_signal_thread(%s, %s)", + name_for_port(ARG1), name_for_port(ARG2)); + PRE_REG_READ2(long, "semaphore_signal_thread", + semaphore_t,"semaphore", mach_port_t,"thread"); +} + + +PRE(semaphore_wait) +{ + PRINT("semaphore_wait(%s)", name_for_port(ARG1)); + PRE_REG_READ1(long, "semaphore_signal", semaphore_t,"semaphore"); + + *flags |= SfMayBlock; +} + + +PRE(semaphore_wait_signal) +{ + PRINT("semaphore_wait_signal(%s, %s)", + name_for_port(ARG1), name_for_port(ARG2)); + PRE_REG_READ2(long, "semaphore_wait_signal", + semaphore_t,"wait_semaphore", + semaphore_t,"signal_semaphore"); + + *flags |= SfMayBlock; +} + + +PRE(semaphore_timedwait) +{ + PRINT("semaphore_timedwait(%s, %g seconds)", + name_for_port(ARG1), ARG2+ARG3/1000000000.0); + PRE_REG_READ3(long, "semaphore_wait_signal", + semaphore_t,"semaphore", + int,"wait_time_hi", + int,"wait_time_lo"); + + *flags |= SfMayBlock; +} + + +PRE(semaphore_timedwait_signal) +{ + PRINT("semaphore_wait_signal(wait %s, signal %s, %g seconds)", + name_for_port(ARG1), name_for_port(ARG2), ARG3+ARG4/1000000000.0); + PRE_REG_READ4(long, "semaphore_wait_signal", + semaphore_t,"wait_semaphore", + semaphore_t,"signal_semaphore", + int,"wait_time_hi", + int,"wait_time_lo"); + + *flags |= SfMayBlock; +} + + +PRE(sys___semwait_signal) +{ + /* args: int cond_sem, int mutex_sem, + int timeout, int relative, + time_t tv_sec, time_t tv_nsec */ + PRINT("sys___semwait_signal(wait %s, signal %s, %ld, %ld, %lds:%ldns)", + name_for_port(ARG1), name_for_port(ARG2), ARG3, ARG4, ARG5, ARG6); + PRE_REG_READ6(long, "sys___semwait_signal", + int,"cond_sem", int,"mutex_sem", + int,"timeout", int,"relative", + vki_time_t,"tv_sec", int,"tv_nsec"); + + *flags |= SfMayBlock; +} + + +PRE(task_for_pid) +{ + PRINT("task_for_pid(%s, %ld, %#lx)", name_for_port(ARG1), ARG2, ARG3); + PRE_REG_READ3(long, "task_for_pid", + mach_port_t,"target", + vki_pid_t, "pid", mach_port_t *,"task"); + PRE_MEM_WRITE("task_for_pid(task)", ARG3, sizeof(mach_port_t)); +} + +POST(task_for_pid) +{ + mach_port_t task; + + POST_MEM_WRITE(ARG3, sizeof(mach_port_t)); + + task = *(mach_port_t *)ARG3; + record_named_port(tid, task, MACH_PORT_RIGHT_SEND, "task-%p"); + PRINT("task 0x%x", task); +} + + +PRE(pid_for_task) +{ + PRINT("pid_for_task(%s, %#lx)", name_for_port(ARG1), ARG2); + PRE_REG_READ2(long, "task_for_pid", mach_port_t,"task", vki_pid_t *,"pid"); + PRE_MEM_WRITE("task_for_pid(pid)", ARG2, sizeof(vki_pid_t)); +} + +POST(pid_for_task) +{ + vki_pid_t pid; + + POST_MEM_WRITE(ARG2, sizeof(vki_pid_t)); + + pid = *(vki_pid_t *)ARG2; + PRINT("pid %u", pid); +} + + +PRE(mach_timebase_info) +{ + PRINT("mach_timebase_info(%#lx)", ARG1); + PRE_REG_READ1(long, "mach_timebase_info", void *,"info"); + PRE_MEM_WRITE("mach_timebase_info(info)", ARG1, sizeof(struct vki_mach_timebase_info)); +} + +POST(mach_timebase_info) +{ + POST_MEM_WRITE(ARG1, sizeof(struct vki_mach_timebase_info)); +} + + +PRE(mach_wait_until) +{ +#if VG_WORDSIZE == 8 + PRINT("mach_wait_until(%llu)", ARG1); + PRE_REG_READ1(long, "mach_wait_until", + unsigned long long,"deadline"); +#else + PRINT("mach_wait_until(%llu)", LOHI64(ARG1, ARG2)); + PRE_REG_READ2(long, "mach_wait_until", + int,"deadline_hi", int,"deadline_lo"); +#endif + *flags |= SfMayBlock; +} + + +PRE(mk_timer_create) +{ + PRINT("mk_timer_create()"); + PRE_REG_READ0(long, "mk_timer_create"); +} + +POST(mk_timer_create) +{ + record_named_port(tid, RES, MACH_PORT_RIGHT_SEND, "mk_timer-%p"); +} + + +PRE(mk_timer_destroy) +{ + PRINT("mk_timer_destroy(%s)", name_for_port(ARG1)); + PRE_REG_READ1(long, "mk_timer_destroy", mach_port_t,"name"); + + // Must block to prevent race (other thread allocates and + // notifies after we deallocate but before we notify) + *flags &= ~SfMayBlock; +} + +POST(mk_timer_destroy) +{ + // Must have cleared SfMayBlock in PRE to prevent race + record_port_destroy(ARG1); +} + + +PRE(mk_timer_arm) +{ +#if VG_WORDSIZE == 8 + PRINT("mk_timer_arm(%s, %llu)", name_for_port(ARG1), ARG2); + PRE_REG_READ2(long, "mk_timer_arm", mach_port_t,"name", + unsigned long,"expire_time"); +#else + PRINT("mk_timer_arm(%s, %llu)", name_for_port(ARG1), LOHI64(ARG2, ARG3)); + PRE_REG_READ3(long, "mk_timer_arm", mach_port_t,"name", + int,"expire_time_hi", int,"expire_time_lo"); +#endif +} + + +PRE(mk_timer_cancel) +{ + PRINT("mk_timer_cancel(%s, %#lx)", name_for_port(ARG1), ARG2); + PRE_REG_READ2(long, "mk_timer_cancel", + mach_port_t,"name", Addr,"result_time"); + if (ARG2) { + PRE_MEM_WRITE("mk_timer_cancel(result_time)", ARG2,sizeof(vki_uint64_t)); + } +} + +POST(mk_timer_cancel) +{ + if (ARG2) { + POST_MEM_WRITE(ARG2, sizeof(vki_uint64_t)); + } +} + + +PRE(iokit_user_client_trap) +{ + PRINT("iokit_user_client_trap(%s, %ld, %lx, %lx, %lx, %lx, %lx, %lx)", + name_for_port(ARG1), ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8); + PRE_REG_READ8(kern_return_t, "iokit_user_client_trap", + mach_port_t,connect, unsigned int,index, + uintptr_t,p1, uintptr_t,p2, uintptr_t,p3, + uintptr_t,p4, uintptr_t,p5, uintptr_t,p6); + + // can't do anything else with this in general + // might be able to use connect+index to choose something sometimes +} + +POST(iokit_user_client_trap) +{ + sync_mappings("after", "iokit_user_client_trap", ARG2); +} + + +PRE(swtch) +{ + PRINT("swtch ( )"); + PRE_REG_READ0(long, "swtch"); + + *flags |= SfMayBlock; +} + + +PRE(swtch_pri) +{ + PRINT("swtch_pri ( %ld )", ARG1); + PRE_REG_READ1(long, "swtch_pri", int,"pri"); + + *flags |= SfMayBlock; +} + + +PRE(sys_FAKE_SIGRETURN) +{ + /* See comments on PRE(sys_rt_sigreturn) in syswrap-amd64-linux.c for + an explanation of what follows. */ + /* This handles the fake signal-return system call created by + sigframe-x86-darwin.c. */ + /* See also comments just below on PRE(sys_sigreturn). */ + + PRINT("FAKE_SIGRETURN ( )"); + + vg_assert(VG_(is_valid_tid)(tid)); + vg_assert(tid >= 1 && tid < VG_N_THREADS); + vg_assert(VG_(is_running_thread)(tid)); + + /* Remove the signal frame from this thread's (guest) stack, + in the process restoring the pre-signal guest state. */ + VG_(sigframe_destroy)(tid, True); + + /* Tell the driver not to update the guest state with the "result", + and set a bogus result to keep it happy. */ + *flags |= SfNoWriteResult; + SET_STATUS_Success(0); + + /* Check to see if any signals arose as a result of this. */ + *flags |= SfPollAfter; +} + + +PRE(sys_sigreturn) +{ + /* This is the "real" sigreturn. But because we construct all the + signal frames ourselves (of course, in m_sigframe), this cannot + happen as a result of normal signal delivery. I think it + happens only when doing siglongjmp, in which case Darwin's Libc + appears to use it for two different purposes: to mess with the + per-thread sigaltstack flags (as per arg 2), or to restore the + thread's state from a ucontext* (as per arg 1). */ + + PRINT("sigreturn ( uctx=%#lx, infostyle=%#lx )", ARG1, ARG2); + + vg_assert(VG_(is_valid_tid)(tid)); + vg_assert(tid >= 1 && tid < VG_N_THREADS); + vg_assert(VG_(is_running_thread)(tid)); + + if (ARG2 == VKI_UC_SET_ALT_STACK) { + /* This is confusing .. the darwin kernel sources imply there is + a per-thread on-altstack/not-on-altstack flag, which is set + by this flag. Just ignore it and claim success for the time + being. */ + VG_(debugLog)(0, "syswrap-darwin", + "WARNING: Ignoring sys_sigreturn( ..., " + "UC_SET_ALT_STACK );\n"); + SET_STATUS_Success(0); + return; + } + if (ARG2 == VKI_UC_RESET_ALT_STACK) { + /* Ditto */ + VG_(debugLog)(0, "syswrap-darwin", + "WARNING: Ignoring sys_sigreturn( ..., " + "UC_RESET_ALT_STACK );\n"); + SET_STATUS_Success(0); + return; + } + + /* Otherwise claim this isn't supported. (Could be + catastrophic). + + What do we have to do if we do need to support it? + + 1. Change the second argument of VG_(sigframe_destroy) from + "Bool isRT" to "UInt sysno", so we can pass the syscall + number, so it can distinguish this case from the + __NR_DARWIN_FAKE_SIGRETURN case. + + 2. In VG_(sigframe_destroy), look at sysno to distinguish the + cases. For __NR_DARWIN_FAKE_SIGRETURN, behave as at present. + For this case, restore the thread's CPU state (or at least + the integer regs) from the ucontext in ARG1 (and do all the + other "signal-returns" stuff too). + + 3. For (2), how do we know where the ucontext is? One way is to + temporarily copy ARG1 into this thread's guest_EBX (or any + other int reg), and have VG_(sigframe_destroy) read + guest_EBX. Why is it ok to trash guest_EBX (or any other int + reg)? Because VG_(sigframe_destroy) is just about to + overwrite all the regs anyway -- since the primary purpose of + calling it is to restore the register state from the ucontext + pointed to by ARG1. + + Hey, it's uggerly. But at least it's documented. + */ + /* But in the meantime ... */ + VG_(debugLog)(0, "syswrap-darwin", + "WARNING: Ignoring sys_sigreturn( uctx=..., 0 );\n"); + VG_(debugLog)(0, "syswrap-darwin", + "WARNING: Thread/program/Valgrind " + "will likely segfault now.\n"); + VG_(debugLog)(0, "syswrap-darwin", + "WARNING: Please file a bug report at " + "http://www.valgrind.org.\n"); + SET_STATUS_Failure( VKI_ENOSYS ); +} + + +/* --------------------------------------------------------------------- + machine-dependent traps + ------------------------------------------------------------------ */ + +#if defined(VGA_x86) +static VexGuestX86SegDescr* alloc_zeroed_x86_LDT ( void ) +{ + Int nbytes = VEX_GUEST_X86_LDT_NENT * sizeof(VexGuestX86SegDescr); + return VG_(arena_calloc)(VG_AR_CORE, "syswrap-darwin.ldt", nbytes, 1); +} +#endif + +PRE(pthread_set_self) +{ + PRINT("pthread_set_self ( %#lx )", ARG1); + PRE_REG_READ1(void, "pthread_set_self", struct pthread_t *, self); + +#if defined(VGA_x86) + // GrP fixme hack this isn't really pthread_set_self + // Point the USER_CTHREAD ldt entry (slot 6, reg 0x37) at this pthread + { + VexGuestX86SegDescr *ldt; + ThreadState *tst = VG_(get_ThreadState)(tid); + ldt = (VexGuestX86SegDescr *)tst->arch.vex.guest_LDT; + if (!ldt) { + ldt = alloc_zeroed_x86_LDT(); + tst->arch.vex.guest_LDT = (HWord)ldt; + } + VG_(memset)(&ldt[6], 0, sizeof(ldt[6])); + ldt[6].LdtEnt.Bits.LimitLow = 1; + ldt[6].LdtEnt.Bits.LimitHi = 0; + ldt[6].LdtEnt.Bits.BaseLow = ARG1 & 0xffff; + ldt[6].LdtEnt.Bits.BaseMid = (ARG1 >> 16) & 0xff; + ldt[6].LdtEnt.Bits.BaseHi = (ARG1 >> 24) & 0xff; + ldt[6].LdtEnt.Bits.Pres = 1; // ACC_P + ldt[6].LdtEnt.Bits.Dpl = 3; // ACC_PL_U + ldt[6].LdtEnt.Bits.Type = 0x12; // ACC_DATA_W + ldt[6].LdtEnt.Bits.Granularity = 1; // SZ_G + ldt[6].LdtEnt.Bits.Default_Big = 1; // SZ_32 + + tst->os_state.pthread = ARG1; + tst->arch.vex.guest_GS = 0x37; + + // What we would like to do is: + // SET_STATUS_Success(0x37); + // but that doesn't work, because this is a MDEP-class syscall, + // and SET_STATUS_Success creates a UNIX-class syscall result. + // Hence we have to laboriously construct the full SysRes "by hand" + // and use that to set the syscall return status. + SET_STATUS_from_SysRes( + VG_(mk_SysRes_x86_darwin)( + VG_DARWIN_SYSNO_CLASS(__NR_pthread_set_self), + False, 0, 0x37 + ) + ); + } + +#elif defined(VGA_amd64) + // GrP fixme bigger hack than x86 + { + ThreadState *tst = VG_(get_ThreadState)(tid); + tst->os_state.pthread = ARG1; + tst->arch.vex.guest_GS_0x60 = ARG1; + // SET_STATUS_Success(0x60); + // see comments on x86 case just above + SET_STATUS_from_SysRes( + VG_(mk_SysRes_amd64_darwin)( + VG_DARWIN_SYSNO_CLASS(__NR_pthread_set_self), + False, 0, 0x60 + ) + ); + } + +#else +#error unknown architecture +#endif +} + + +/* --------------------------------------------------------------------- + syscall tables + ------------------------------------------------------------------ */ + +/* Add a Darwin-specific, arch-independent wrapper to a syscall table. */ +#define MACX_(sysno, name) WRAPPER_ENTRY_X_(darwin, VG_DARWIN_SYSNO_INDEX(sysno), name) +#define MACXY(sysno, name) WRAPPER_ENTRY_XY(darwin, VG_DARWIN_SYSNO_INDEX(sysno), name) +#define _____(sysno) GENX_(sysno, sys_ni_syscall) + +/* + _____ : unsupported by the kernel (sys_ni_syscall) + // _____ : unimplemented in valgrind + GEN : handlers are in syswrap-generic.c + MAC : handlers are in this file + X_ : PRE handler only + XY : PRE and POST handlers +*/ +// DDD: the "sys_" prefixes I think aren't necessary. The name on the right +// is meant to be the name of the function implementing the syscall in the +// kernel. On Darwin that seems to usually match the __NR_xyz name. +const SyscallTableEntry ML_(syscall_table)[] = { +// _____(__NR_syscall), // 0 + MACX_(__NR_exit, sys_exit), + GENX_(__NR_fork, sys_fork), + GENXY(__NR_read, sys_read), + GENX_(__NR_write, sys_write), + GENXY(__NR_open, sys_open), + GENXY(__NR_close, sys_close), + GENXY(__NR_wait4, sys_wait4), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(8)), // old creat + GENX_(__NR_link, sys_link), + GENX_(__NR_unlink, sys_unlink), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(11)), // old execv + GENX_(__NR_chdir, sys_chdir), + GENX_(__NR_fchdir, sys_fchdir), + GENX_(__NR_mknod, sys_mknod), + GENX_(__NR_chmod, sys_chmod), + GENX_(__NR_chown, sys_chown), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(17)), // old break + MACXY(__NR_getfsstat, sys_getfsstat), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(19)), // old lseek + GENX_(__NR_getpid, sys_getpid), // 20 + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(21)), // old mount + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(22)), // old umount + GENX_(__NR_setuid, sys_setuid), + GENX_(__NR_getuid, sys_getuid), + GENX_(__NR_geteuid, sys_geteuid), + MACX_(__NR_ptrace, sys_ptrace), + MACXY(__NR_recvmsg, sys_recvmsg), + MACX_(__NR_sendmsg, sys_sendmsg), + MACXY(__NR_recvfrom, sys_recvfrom), + MACXY(__NR_accept, sys_accept), + MACXY(__NR_getpeername, sys_getpeername), + MACXY(__NR_getsockname, sys_getsockname), + GENX_(__NR_access, sys_access), + MACX_(__NR_chflags, sys_chflags), + MACX_(__NR_fchflags, sys_fchflags), + GENX_(__NR_sync, sys_sync), + GENX_(__NR_kill, sys_kill), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(38)), // old stat + GENX_(__NR_getppid, sys_getppid), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(40)), // old lstat + GENXY(__NR_dup, sys_dup), + MACXY(__NR_pipe, sys_pipe), + GENX_(__NR_getegid, sys_getegid), +// _____(__NR_profil), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(45)), // old ktrace + MACXY(__NR_sigaction, sys_sigaction), + GENX_(__NR_getgid, sys_getgid), + MACXY(__NR_sigprocmask, sys_sigprocmask), + MACXY(__NR_getlogin, sys_getlogin), +// _____(__NR_setlogin), +// _____(__NR_acct), +// _____(__NR_sigpending), + GENXY(__NR_sigaltstack, sys_sigaltstack), + MACXY(__NR_ioctl, sys_ioctl), +// _____(__NR_reboot), +// _____(__NR_revoke), +// _____(__NR_symlink), + GENX_(__NR_readlink, sys_readlink), + GENX_(__NR_execve, sys_execve), + GENX_(__NR_umask, sys_umask), // 60 + GENX_(__NR_chroot, sys_chroot), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(62)), // old fstat + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(63)), // used internally, reserved + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(64)), // old getpagesize + GENX_(__NR_msync, sys_msync), + GENX_(__NR_vfork, sys_fork), // (We treat vfork as fork.) + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(67)), // old vread + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(68)), // old vwrite + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(69)), // old sbrk + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(70)), // old sstk + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(71)), // old mmap + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(72)), // old vadvise + GENXY(__NR_munmap, sys_munmap), + GENXY(__NR_mprotect, sys_mprotect), + GENX_(__NR_madvise, sys_madvise), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(76)), // old vhangup + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(77)), // old vlimit +// _____(__NR_mincore), + GENXY(__NR_getgroups, sys_getgroups), +// _____(__NR_setgroups), // 80 + GENX_(__NR_getpgrp, sys_getpgrp), +// _____(__NR_setpgid), + GENXY(__NR_setitimer, sys_setitimer), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(84)), // old wait +// _____(__NR_swapon), + GENXY(__NR_getitimer, sys_getitimer), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(87)), // old gethostname + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(88)), // old sethostname + MACXY(__NR_getdtablesize, sys_getdtablesize), + GENXY(__NR_dup2, sys_dup2), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(91)), // old getdopt + MACXY(__NR_fcntl, sys_fcntl), + GENX_(__NR_select, sys_select), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(94)), // old setdopt + GENX_(__NR_fsync, sys_fsync), + GENX_(__NR_setpriority, sys_setpriority), + MACXY(__NR_socket, sys_socket), + MACX_(__NR_connect, sys_connect), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(99)), // old accept + GENX_(__NR_getpriority, sys_getpriority), // 100 + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(101)), // old send + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(102)), // old recv + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(103)), // old sigreturn + MACX_(__NR_bind, sys_bind), + MACX_(__NR_setsockopt, sys_setsockopt), + MACX_(__NR_listen, sys_listen), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(107)), // old vtimes + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(108)), // old sigvec + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(109)), // old sigblock + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(110)), // old sigsetmask + MACX_(__NR_sigsuspend, sys_sigsuspend), // old sigsuspend + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(112)), // old sigstack + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(113)), // old recvmsg + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(114)), // old sendmsg + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(115)), // old vtrace + GENXY(__NR_gettimeofday, sys_gettimeofday), + GENXY(__NR_getrusage, sys_getrusage), + MACXY(__NR_getsockopt, sys_getsockopt), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(119)), // old resuba + GENXY(__NR_readv, sys_readv), // 120 + GENX_(__NR_writev, sys_writev), +// _____(__NR_settimeofday), + GENX_(__NR_fchown, sys_fchown), + GENX_(__NR_fchmod, sys_fchmod), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(125)), // old recvfrom +// _____(__NR_setreuid), +// _____(__NR_setregid), + GENX_(__NR_rename, sys_rename), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(129)), // old truncate + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(130)), // old ftruncate + GENX_(__NR_flock, sys_flock), +// _____(__NR_mkfifo), + MACX_(__NR_sendto, sys_sendto), + MACX_(__NR_shutdown, sys_shutdown), + MACXY(__NR_socketpair, sys_socketpair), + GENX_(__NR_mkdir, sys_mkdir), + GENX_(__NR_rmdir, sys_rmdir), + GENX_(__NR_utimes, sys_utimes), + MACX_(__NR_futimes, sys_futimes), +// _____(__NR_adjtime), // 140 + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(141)), // old getpeername + MACXY(__NR_gethostuuid, sys_gethostuuid), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(143)), // old sethostid + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(144)), // old getrlimit + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(145)), // old setrlimit + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(146)), // old killpg + GENX_(__NR_setsid, sys_setsid), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(148)), // old setquota + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(149)), // old qquota + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(150)), // old getsockname +// _____(__NR_getpgid), +// _____(__NR_setprivexec), + GENXY(__NR_pread, sys_pread64), + GENX_(__NR_pwrite, sys_pwrite64), +// _____(__NR_nfssvc), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(156)), // old getdirentries + GENXY(__NR_statfs, sys_statfs), + GENXY(__NR_fstatfs, sys_fstatfs), +// _____(__NR_unmount), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(160)), // old async_daemon +// _____(__NR_getfh), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(162)), // old getdomainname + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(163)), // old setdomainname + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(164)), // ??? +// _____(__NR_quotactl), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(166)), // old exportfs +// _____(__NR_mount), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(168)), // old ustat + MACXY(__NR_csops, sys_csops), // code-signing ops + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(170)), // old table + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(171)), // old wait3 + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(172)), // old rpause +// _____(__NR_waitid), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(174)), // old getdents + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(175)), // old gc_control +// _____(__NR_add_profil), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(177)), // ??? + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(178)), // ??? + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(179)), // ??? + MACX_(__NR_kdebug_trace, sys_kdebug_trace), // 180 + GENX_(__NR_setgid, sys_setgid), + MACX_(__NR_setegid, sys_setegid), + MACX_(__NR_seteuid, sys_seteuid), + MACX_(__NR_sigreturn, sys_sigreturn), +// _____(__NR_chud), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(186)), // ??? + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(187)), // ??? + GENXY(__NR_stat, sys_newstat), + GENXY(__NR_fstat, sys_newfstat), + GENXY(__NR_lstat, sys_newlstat), + MACX_(__NR_pathconf, sys_pathconf), + MACX_(__NR_fpathconf, sys_fpathconf), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(193)), // ??? + GENXY(__NR_getrlimit, sys_getrlimit), + GENX_(__NR_setrlimit, sys_setrlimit), + MACXY(__NR_getdirentries, sys_getdirentries), + MACXY(__NR_mmap, sys_mmap), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(198)), // __syscall + MACX_(__NR_lseek, sys_lseek), + GENX_(__NR_truncate, sys_truncate64), // 200 + GENX_(__NR_ftruncate, sys_ftruncate64), + MACXY(__NR___sysctl, sys_sysctl), + GENX_(__NR_mlock, sys_mlock), + GENX_(__NR_munlock, sys_munlock), +// _____(__NR_undelete), +// _____(__NR_ATsocket), +// _____(__NR_ATgetmsg), +// _____(__NR_ATputmsg), +// _____(__NR_ATPsndreq), +// _____(__NR_ATPsndrsp), +// _____(__NR_ATPgetreq), +// _____(__NR_ATPgetrsp), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(213)), // Reserved for AppleTalk +// _____(__NR_kqueue_from_portset_np), +// _____(__NR_kqueue_portset_np), +// _____(__NR_mkcomplex), +// _____(__NR_statv), +// _____(__NR_lstatv), +// _____(__NR_fstatv), + MACXY(__NR_getattrlist, sys_getattrlist), // 220 + MACX_(__NR_setattrlist, sys_setattrlist), + MACXY(__NR_getdirentriesattr, sys_getdirentriesattr), +// _____(__NR_exchangedata), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(224)), // checkuseraccess +// _____(__NR_searchfs), + GENX_(__NR_delete, sys_unlink), +// _____(__NR_copyfile), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(228)), // ?? + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(229)), // ?? + GENXY(__NR_poll, sys_poll), + MACX_(__NR_watchevent, sys_watchevent), + MACXY(__NR_waitevent, sys_waitevent), + MACX_(__NR_modwatch, sys_modwatch), + MACXY(__NR_getxattr, sys_getxattr), + MACXY(__NR_fgetxattr, sys_fgetxattr), + MACX_(__NR_setxattr, sys_setxattr), + MACX_(__NR_fsetxattr, sys_fsetxattr), +// _____(__NR_removexattr), +// _____(__NR_fremovexattr), + MACXY(__NR_listxattr, sys_listxattr), // 240 + MACXY(__NR_flistxattr, sys_flistxattr), + MACXY(__NR_fsctl, sys_fsctl), + MACX_(__NR_initgroups, sys_initgroups), + MACXY(__NR_posix_spawn, sys_posix_spawn), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(245)), // ??? + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(246)), // ??? +// _____(__NR_nfsclnt), +// _____(__NR_fhopen), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(249)), // ??? +// _____(__NR_minherit), +// _____(__NR_semsys), +// _____(__NR_msgsys), +// _____(__NR_shmsys), + MACXY(__NR_semctl, sys_semctl), + MACX_(__NR_semget, sys_semget), + MACX_(__NR_semop, sys_semop), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(257)), // ??? +// _____(__NR_msgctl), +// _____(__NR_msgget), +// _____(__NR_msgsnd), // 260 +// _____(__NR_msgrcv), +// _____(__NR_shmat), +// _____(__NR_shmctl), +// _____(__NR_shmdt), + MACX_(__NR_shmget, sys_shmget), + MACXY(__NR_shm_open, sys_shm_open), +// _____(__NR_shm_unlink), + MACX_(__NR_sem_open, sys_sem_open), + MACX_(__NR_sem_close, sys_sem_close), + MACX_(__NR_sem_unlink, sys_sem_unlink), +// _____(__NR_sem_wait), + MACX_(__NR_sem_trywait, sys_sem_trywait), +// _____(__NR_sem_post), + MACX_(__NR_sem_post, sys_sem_post), +// _____(__NR_sem_getvalue), + MACXY(__NR_sem_init, sys_sem_init), + MACX_(__NR_sem_destroy, sys_sem_destroy), +// _____(__NR_open_extended), +// _____(__NR_umask_extended), + MACXY(__NR_stat_extended, sys_statx), +// _____(__NR_lstat_extended), // 280 +// _____(__NR_fstat_extended), + MACX_(__NR_chmod_extended, sys_chmod_extended), + MACX_(__NR_fchmod_extended, sys_fchmod_extended), +// _____(__NR_access_extended), + MACX_(__NR_settid, sys_settid), +// _____(__NR_gettid), +// _____(__NR_setsgroups), +// _____(__NR_getsgroups), +// _____(__NR_setwgroups), +// _____(__NR_getwgroups), +// _____(__NR_mkfifo_extended), +// _____(__NR_mkdir_extended), +// _____(__NR_identitysvc), +// _____(__NR_shared_region_check_np), +// _____(__NR_shared_region_map_np), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(296)), // old load_shared_file + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(297)), // old reset_shared_file + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(298)), // old new_system_shared_regions + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(299)), // old shared_region_map_file_np + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(300)), // old shared_region_make_private_np +// _____(__NR___pthread_mutex_destroy), +// _____(__NR___pthread_mutex_init), +// _____(__NR___pthread_mutex_lock), +// _____(__NR___pthread_mutex_trylock), +// _____(__NR___pthread_mutex_unlock), +// _____(__NR___pthread_cond_init), +// _____(__NR___pthread_cond_destroy), +// _____(__NR___pthread_cond_broadcast), +// _____(__NR___pthread_cond_signal), +// _____(__NR_getsid), +// _____(__NR_settid_with_pid), +// _____(__NR___pthread_cond_timedwait), +// _____(__NR_aio_fsync), +// _____(__NR_aio_return), +// _____(__NR_aio_suspend), +// _____(__NR_aio_cancel), +// _____(__NR_aio_error), +// _____(__NR_aio_read), +// _____(__NR_aio_write), +// _____(__NR_lio_listio), // 320 +// _____(__NR___pthread_cond_wait), +// _____(__NR_iopolicysys), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(323)), // ??? +// _____(__NR_mlockall), +// _____(__NR_munlockall), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(326)), // ??? + MACX_(__NR_issetugid, sys_issetugid), +// _____(__NR___pthread_kill), + MACX_(__NR___pthread_sigmask, sys___pthread_sigmask), +// _____(__NR___sigwait), + MACX_(__NR___disable_threadsignal, sys___disable_threadsignal), + MACX_(__NR___pthread_markcancel, sys___pthread_markcancel), + MACX_(__NR___pthread_canceled, sys___pthread_canceled), + MACX_(__NR___semwait_signal, sys___semwait_signal), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(335)), // old utrace +// _____(__NR_proc_info), + MACXY(__NR_sendfile, sys_sendfile), + MACXY(__NR_stat64, sys_stat64), + MACXY(__NR_fstat64, sys_fstat64), + MACXY(__NR_lstat64, sys_lstat64), // 340 +// _____(__NR_stat64_extended), +// _____(__NR_lstat64_extended), +// _____(__NR_fstat64_extended), + MACXY(__NR_getdirentries64, sys_getdirentries64), + MACXY(__NR_statfs64, sys_statfs64), + MACXY(__NR_fstatfs64, sys_fstatfs64), +// _____(__NR_getfsstat64), +// _____(__NR___pthread_chdir), +// _____(__NR___pthread_fchdir), +// _____(__NR_audit), + MACXY(__NR_auditon, sys_auditon), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(352)), // ??? +// _____(__NR_getauid), +// _____(__NR_setauid), +// _____(__NR_getaudit), +// _____(__NR_setaudit), +// _____(__NR_getaudit_addr), +// _____(__NR_setaudit_addr), +// _____(__NR_auditctl), + MACXY(__NR_bsdthread_create, sys_bsdthread_create), // 360 + MACX_(__NR_bsdthread_terminate, sys_bsdthread_terminate), + MACXY(__NR_kqueue, sys_kqueue), + MACXY(__NR_kevent, sys_kevent), +// _____(__NR_lchown), +// _____(__NR_stack_snapshot), + MACX_(__NR_bsdthread_register, sys_bsdthread_register), + MACX_(__NR_workq_open, sys_workq_open), + MACXY(__NR_workq_ops, sys_workq_ops), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(369)), // ??? + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(370)), // ??? + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(371)), // ??? + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(372)), // ??? + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(373)), // ??? + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(374)), // ??? + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(375)), // ??? + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(376)), // ??? + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(377)), // ??? + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(378)), // ??? + _____(VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(379)), // ??? +// _____(__NR___mac_execve), // 380 + MACX_(__NR___mac_syscall, sys___mac_syscall), +// _____(__NR___mac_get_file), +// _____(__NR___mac_set_file), +// _____(__NR___mac_get_link), +// _____(__NR___mac_set_link), +// _____(__NR___mac_get_proc), +// _____(__NR___mac_set_proc), +// _____(__NR___mac_get_fd), +// _____(__NR___mac_set_fd), +// _____(__NR___mac_get_pid), +// _____(__NR___mac_get_lcid), +// _____(__NR___mac_get_lctx), +// _____(__NR___mac_set_lctx), +// _____(__NR_setlcid), +// _____(__NR_getlcid), + // GrP fixme need any special nocancel handling? + GENXY(__NR_read_nocancel, sys_read), + GENX_(__NR_write_nocancel, sys_write), + GENXY(__NR_open_nocancel, sys_open), + GENXY(__NR_close_nocancel, sys_close), + GENXY(__NR_wait4_nocancel, sys_wait4), // 400 + MACXY(__NR_recvmsg_nocancel, sys_recvmsg), + MACX_(__NR_sendmsg_nocancel, sys_sendmsg), + MACXY(__NR_recvfrom_nocancel, sys_recvfrom), + MACXY(__NR_accept_nocancel, sys_accept), + GENX_(__NR_msync_nocancel, sys_msync), + MACXY(__NR_fcntl_nocancel, sys_fcntl), + GENX_(__NR_select_nocancel, sys_select), + GENX_(__NR_fsync_nocancel, sys_fsync), + MACX_(__NR_connect_nocancel, sys_connect), +// _____(__NR_sigsuspend_nocancel), + GENXY(__NR_readv_nocancel, sys_readv), + GENX_(__NR_writev_nocancel, sys_writev), + MACX_(__NR_sendto_nocancel, sys_sendto), + GENXY(__NR_pread_nocancel, sys_pread64), + GENX_(__NR_pwrite_nocancel, sys_pwrite64), +// _____(__NR_waitid_nocancel), + GENXY(__NR_poll_nocancel, sys_poll), +// _____(__NR_msgsnd_nocancel), +// _____(__NR_msgrcv_nocancel), + MACX_(__NR_sem_wait_nocancel, sys_sem_wait_nocancel), // 420 +// _____(__NR_aio_suspend_nocancel), +// _____(__NR___sigwait_nocancel), + MACX_(__NR___semwait_signal_nocancel, sys___semwait_signal), +// _____(__NR___mac_mount), +// _____(__NR___mac_get_mount), +// _____(__NR___mac_getfsstat), +// _____(__NR_MAXSYSCALL) + MACX_(__NR_DARWIN_FAKE_SIGRETURN, sys_FAKE_SIGRETURN) +}; + + +// Mach traps use negative syscall numbers. +// Use ML_(mach_trap_table)[-mach_trap_number] . + +const SyscallTableEntry ML_(mach_trap_table)[] = { + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(0)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(1)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(2)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(3)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(4)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(5)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(6)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(7)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(8)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(9)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(10)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(11)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(12)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(13)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(14)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(15)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(16)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(17)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(18)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(19)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(20)), // -20 + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(21)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(22)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(23)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(24)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(25)), + MACXY(__NR_mach_reply_port, mach_reply_port), + MACXY(__NR_thread_self_trap, mach_thread_self), + MACXY(__NR_task_self_trap, mach_task_self), + MACXY(__NR_host_self_trap, mach_host_self), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(30)), + MACXY(__NR_mach_msg_trap, mach_msg), +// _____(__NR_mach_msg_overwrite_trap), + MACX_(__NR_semaphore_signal_trap, semaphore_signal), + MACX_(__NR_semaphore_signal_all_trap, semaphore_signal_all), + MACX_(__NR_semaphore_signal_thread_trap, semaphore_signal_thread), + MACX_(__NR_semaphore_wait_trap, semaphore_wait), + MACX_(__NR_semaphore_wait_signal_trap, semaphore_wait_signal), + MACX_(__NR_semaphore_timedwait_trap, semaphore_timedwait), + MACX_(__NR_semaphore_timedwait_signal_trap, semaphore_timedwait_signal), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(40)), // -40 +#if defined(VGA_x86) +// _____(__NR_init_process), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(42)), +// _____(__NR_map_fd), +#else + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(41)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(42)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(43)), +#endif +// _____(__NR_task_name_for_pid), + MACXY(__NR_task_for_pid, task_for_pid), + MACXY(__NR_pid_for_task, pid_for_task), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(47)), +#if defined(VGA_x86) +// _____(__NR_macx_swapon), +// _____(__NR_macx_swapoff), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(50)), +// _____(__NR_macx_triggers), +// _____(__NR_macx_backing_store_suspend), +// _____(__NR_macx_backing_store_recovery), +#else + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(48)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(49)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(50)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(51)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(52)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(53)), +#endif + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(54)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(55)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(56)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(57)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(58)), + MACX_(__NR_swtch_pri, swtch_pri), + MACX_(__NR_swtch, swtch), // -60 + MACX_(__NR_syscall_thread_switch, syscall_thread_switch), +// _____(__NR_clock_sleep_trap), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(63)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(64)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(65)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(66)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(67)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(68)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(69)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(70)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(71)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(72)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(73)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(74)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(75)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(76)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(77)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(78)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(79)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(80)), // -80 + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(81)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(82)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(83)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(84)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(85)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(86)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(87)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(88)), + MACXY(__NR_mach_timebase_info, mach_timebase_info), + MACX_(__NR_mach_wait_until, mach_wait_until), + MACXY(__NR_mk_timer_create, mk_timer_create), + MACXY(__NR_mk_timer_destroy, mk_timer_destroy), + MACX_(__NR_mk_timer_arm, mk_timer_arm), + MACXY(__NR_mk_timer_cancel, mk_timer_cancel), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(95)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(96)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(97)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(98)), + _____(VG_DARWIN_SYSCALL_CONSTRUCT_MACH(99)), + MACXY(__NR_iokit_user_client_trap, iokit_user_client_trap), // -100 +}; + + +// Machine-dependent traps have wacky syscall numbers, and use the Mach trap +// calling convention instead of the syscall convention. +// Use ML_(mdep_trap_table)[syscallno - ML_(mdep_trap_base)] . + +#if defined(VGA_x86) +const SyscallTableEntry ML_(mdep_trap_table)[] = { + MACX_(__NR_pthread_set_self, pthread_set_self), +}; +#elif defined(VGA_amd64) +const SyscallTableEntry ML_(mdep_trap_table)[] = { + MACX_(__NR_pthread_set_self, pthread_set_self), +}; +#else +#error unknown architecture +#endif + +const UInt ML_(syscall_table_size) = + sizeof(ML_(syscall_table)) / sizeof(ML_(syscall_table)[0]); + +const UInt ML_(mach_trap_table_size) = + sizeof(ML_(mach_trap_table)) / sizeof(ML_(mach_trap_table)[0]); + +const UInt ML_(mdep_trap_table_size) = + sizeof(ML_(mdep_trap_table)) / sizeof(ML_(mdep_trap_table)[0]); + + +/*--------------------------------------------------------------------*/ +/*--- end syswrap-darwin.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_syswrap/syswrap-generic.c b/coregrind/m_syswrap/syswrap-generic.c index 67a662812..a34ccb931 100644 --- a/coregrind/m_syswrap/syswrap-generic.c +++ b/coregrind/m_syswrap/syswrap-generic.c @@ -45,6 +45,7 @@ #include "pub_core_libcprint.h" #include "pub_core_libcproc.h" #include "pub_core_libcsignal.h" +#include "pub_core_machine.h" // VG_(get_SP) #include "pub_core_mallocfree.h" #include "pub_core_options.h" #include "pub_core_scheduler.h" @@ -228,6 +229,7 @@ ML_(notify_core_and_tool_of_mprotect) ( Addr a, SizeT len, Int prot ) +#if HAVE_MREMAP /* Expand (or shrink) an existing mapping, potentially moving it at the same time (controlled by the MREMAP_MAYMOVE flag). Nightmare. */ @@ -502,6 +504,7 @@ SysRes do_mremap( Addr old_addr, SizeT old_len, # undef MIN_SIZET } +#endif /* HAVE_MREMAP */ /* --------------------------------------------------------------------- @@ -759,6 +762,7 @@ void init_preopened_fds_without_proc_self_fd(void) void VG_(init_preopened_fds)(void) { // Nb: AIX5 is handled in syswrap-aix5.c. +// DDD: should probably use HAVE_PROC here or similar, instead. #if defined(VGO_linux) Int ret; struct vki_dirent d; @@ -793,6 +797,9 @@ void VG_(init_preopened_fds)(void) out: VG_(close)(sr_Res(f)); +#elif defined(VGO_darwin) + init_preopened_fds_without_proc_self_fd(); + #else # error Unknown OS #endif @@ -905,6 +912,7 @@ static void check_cmsg_for_fds(ThreadId tid, struct vki_msghdr *msg) } } +/* GrP kernel ignores sa_len (at least on Darwin); this checks the rest */ static void pre_mem_read_sockaddr ( ThreadId tid, Char *description, @@ -929,6 +937,7 @@ void pre_mem_read_sockaddr ( ThreadId tid, case VKI_AF_UNIX: VG_(sprintf) ( outmsg, description, "sun_path" ); PRE_MEM_RASCIIZ( outmsg, (Addr) sun->sun_path ); + // GrP fixme max of sun_len-2? what about nul char? break; case VKI_AF_INET: @@ -1576,6 +1585,7 @@ ML_(generic_PRE_sys_semctl) ( ThreadId tid, union vki_semun arg = *(union vki_semun *)&arg3; UInt nsems; switch (arg2 /* cmd */) { +#if defined(VKI_IPC_INFO) case VKI_IPC_INFO: case VKI_SEM_INFO: case VKI_IPC_INFO|VKI_IPC_64: @@ -1583,32 +1593,51 @@ ML_(generic_PRE_sys_semctl) ( ThreadId tid, PRE_MEM_WRITE( "semctl(IPC_INFO, arg.buf)", (Addr)arg.buf, sizeof(struct vki_seminfo) ); break; +#endif + case VKI_IPC_STAT: +#if defined(VKI_SEM_STAT) case VKI_SEM_STAT: +#endif PRE_MEM_WRITE( "semctl(IPC_STAT, arg.buf)", (Addr)arg.buf, sizeof(struct vki_semid_ds) ); break; + +#if defined(VKI_IPC_64) case VKI_IPC_STAT|VKI_IPC_64: +#if defined(VKI_SEM_STAT) case VKI_SEM_STAT|VKI_IPC_64: +#endif PRE_MEM_WRITE( "semctl(IPC_STAT, arg.buf)", (Addr)arg.buf, sizeof(struct vki_semid64_ds) ); break; +#endif + case VKI_IPC_SET: PRE_MEM_READ( "semctl(IPC_SET, arg.buf)", (Addr)arg.buf, sizeof(struct vki_semid_ds) ); break; + +#if defined(VKI_IPC_64) case VKI_IPC_SET|VKI_IPC_64: PRE_MEM_READ( "semctl(IPC_SET, arg.buf)", (Addr)arg.buf, sizeof(struct vki_semid64_ds) ); break; +#endif + case VKI_GETALL: +#if defined(VKI_IPC_64) case VKI_GETALL|VKI_IPC_64: +#endif nsems = get_sem_count( arg0 ); PRE_MEM_WRITE( "semctl(IPC_GETALL, arg.array)", (Addr)arg.array, sizeof(unsigned short) * nsems ); break; + case VKI_SETALL: +#if defined(VKI_IPC_64) case VKI_SETALL|VKI_IPC_64: +#endif nsems = get_sem_count( arg0 ); PRE_MEM_READ( "semctl(IPC_SETALL, arg.array)", (Addr)arg.array, sizeof(unsigned short) * nsems ); @@ -1625,22 +1654,33 @@ ML_(generic_POST_sys_semctl) ( ThreadId tid, union vki_semun arg = *(union vki_semun *)&arg3; UInt nsems; switch (arg2 /* cmd */) { +#if defined(VKI_IPC_INFO) case VKI_IPC_INFO: case VKI_SEM_INFO: case VKI_IPC_INFO|VKI_IPC_64: case VKI_SEM_INFO|VKI_IPC_64: POST_MEM_WRITE( (Addr)arg.buf, sizeof(struct vki_seminfo) ); break; +#endif + case VKI_IPC_STAT: +#if defined(VKI_SEM_STAT) case VKI_SEM_STAT: +#endif POST_MEM_WRITE( (Addr)arg.buf, sizeof(struct vki_semid_ds) ); break; + +#if defined(VKI_IPC_64) case VKI_IPC_STAT|VKI_IPC_64: case VKI_SEM_STAT|VKI_IPC_64: POST_MEM_WRITE( (Addr)arg.buf, sizeof(struct vki_semid64_ds) ); break; +#endif + case VKI_GETALL: +#if defined(VKI_IPC_64) case VKI_GETALL|VKI_IPC_64: +#endif nsems = get_sem_count( arg0 ); POST_MEM_WRITE( (Addr)arg.array, sizeof(unsigned short) * nsems ); break; @@ -1765,37 +1805,56 @@ ML_(generic_PRE_sys_shmctl) ( ThreadId tid, { /* int shmctl(int shmid, int cmd, struct shmid_ds *buf); */ switch (arg1 /* cmd */) { +#if defined(VKI_IPC_INFO) case VKI_IPC_INFO: PRE_MEM_WRITE( "shmctl(IPC_INFO, buf)", arg2, sizeof(struct vki_shminfo) ); break; +#if defined(VKI_IPC_64) case VKI_IPC_INFO|VKI_IPC_64: PRE_MEM_WRITE( "shmctl(IPC_INFO, buf)", arg2, sizeof(struct vki_shminfo64) ); break; +#endif +#endif + +#if defined(VKI_SHM_INFO) case VKI_SHM_INFO: +#if defined(VKI_IPC_64) case VKI_SHM_INFO|VKI_IPC_64: +#endif PRE_MEM_WRITE( "shmctl(SHM_INFO, buf)", arg2, sizeof(struct vki_shm_info) ); break; +#endif + case VKI_IPC_STAT: +#if defined(VKI_SHM_STAT) case VKI_SHM_STAT: +#endif PRE_MEM_WRITE( "shmctl(IPC_STAT, buf)", arg2, sizeof(struct vki_shmid_ds) ); break; + +#if defined(VKI_IPC_64) case VKI_IPC_STAT|VKI_IPC_64: case VKI_SHM_STAT|VKI_IPC_64: PRE_MEM_WRITE( "shmctl(IPC_STAT, arg.buf)", arg2, sizeof(struct vki_shmid64_ds) ); break; +#endif + case VKI_IPC_SET: PRE_MEM_READ( "shmctl(IPC_SET, arg.buf)", arg2, sizeof(struct vki_shmid_ds) ); break; + +#if defined(VKI_IPC_64) case VKI_IPC_SET|VKI_IPC_64: PRE_MEM_READ( "shmctl(IPC_SET, arg.buf)", arg2, sizeof(struct vki_shmid64_ds) ); break; +#endif } } @@ -1805,24 +1864,37 @@ ML_(generic_POST_sys_shmctl) ( ThreadId tid, UWord arg0, UWord arg1, UWord arg2 ) { switch (arg1 /* cmd */) { +#if defined(VKI_IPC_INFO) case VKI_IPC_INFO: POST_MEM_WRITE( arg2, sizeof(struct vki_shminfo) ); break; case VKI_IPC_INFO|VKI_IPC_64: POST_MEM_WRITE( arg2, sizeof(struct vki_shminfo64) ); break; +#endif + +#if defined(VKI_SHM_INFO) case VKI_SHM_INFO: case VKI_SHM_INFO|VKI_IPC_64: POST_MEM_WRITE( arg2, sizeof(struct vki_shm_info) ); break; +#endif + case VKI_IPC_STAT: +#if defined(VKI_SHM_STAT) case VKI_SHM_STAT: +#endif POST_MEM_WRITE( arg2, sizeof(struct vki_shmid_ds) ); break; + +#if defined(VKI_IPC_64) case VKI_IPC_STAT|VKI_IPC_64: case VKI_SHM_STAT|VKI_IPC_64: POST_MEM_WRITE( arg2, sizeof(struct vki_shmid64_ds) ); break; +#endif + + } } @@ -1866,6 +1938,16 @@ ML_(generic_PRE_sys_mmap) ( ThreadId tid, MapRequest mreq; Bool mreq_ok; +#if defined(VGO_darwin) + // Nb: we can't use this on Darwin, it has races: + // * needs to RETRY if advisory succeeds but map fails + // (could have been some other thread in a nonblocking call) + // * needs to not use fixed-position mmap() on Darwin + // (mmap will cheerfully smash whatever's already there, which might + // be a new mapping from some other thread in a nonblocking call) + VG_(core_panic)("can't use ML_(generic_PRE_sys_mmap) on Darwin"); +#endif + if (arg2 == 0) { /* SuSV3 says: If len is zero, mmap() shall fail and no mapping shall be established. */ @@ -2004,6 +2086,7 @@ ML_(generic_PRE_sys_mmap) ( ThreadId tid, #if VG_WORDSIZE == 4 // Combine two 32-bit values into a 64-bit value // Always use with low-numbered arg first (e.g. LOHI64(ARG1,ARG2) ) +// GrP fixme correct for ppc-linux? #define LOHI64(lo,hi) ( ((ULong)(lo)) | (((ULong)(hi)) << 32) ) #endif @@ -2023,8 +2106,17 @@ PRE(sys_exit) PRE(sys_ni_syscall) { - // Nb: AIX5 is handled in syswrap-aix5.c. - PRINT("non-existent syscall! (ni_syscall)"); + PRINT("unimplemented (by the kernel) syscall %ld! (ni_syscall)\n", +// Nb: AIX5 is handled in syswrap-aix5.c. +// DDD: make this generic +#if defined(VGO_linux) + SYSNO +#elif defined(VGO_darwin) + VG_DARWIN_SYSNO_PRINT(SYSNO) +#else +# error Unknown OS +#endif + ); PRE_REG_READ0(long, "ni_syscall"); SET_STATUS_Failure( VKI_ENOSYS ); } @@ -2130,6 +2222,7 @@ PRE(sys_getitimer) PRE_timeval_WRITE( "getitimer(&value->it_interval)", &(value->it_interval)); PRE_timeval_WRITE( "getitimer(&value->it_value)", &(value->it_value)); } + POST(sys_getitimer) { if (ARG2 != (Addr)NULL) { @@ -2185,6 +2278,7 @@ PRE(sys_madvise) unsigned long, start, vki_size_t, length, int, advice); } +#if HAVE_MREMAP PRE(sys_mremap) { // Nb: this is different to the glibc version described in the man pages, @@ -2207,6 +2301,7 @@ PRE(sys_mremap) do_mremap((Addr)ARG1, ARG2, (Addr)ARG5, ARG3, ARG4, tid) ); } +#endif /* HAVE_MREMAP */ PRE(sys_nice) { @@ -2811,9 +2906,17 @@ PRE(sys_fork) if (!SUCCESS) return; +#if defined(VGO_linux) || defined(VGO_aix5) // RES is 0 for child, non-0 (the child's PID) for parent. is_child = ( RES == 0 ? True : False ); child_pid = ( is_child ? -1 : RES ); +#elif defined(VGO_darwin) + // RES is the child's pid. RESHI is 1 for child, 0 for parent. + is_child = RESHI; + child_pid = RES; +#else +# error Unknown OS +#endif VG_(do_atfork_pre)(tid); @@ -2830,7 +2933,7 @@ PRE(sys_fork) if (!VG_(logging_to_socket) && VG_(clo_child_silent_after_fork)) VG_(clo_log_fd) = -1; - } else { + } else { VG_(do_atfork_parent)(tid); PRINT(" fork: process %d created child %d\n", VG_(getpid)(), child_pid); @@ -3002,6 +3105,12 @@ static void common_post_getrlimit(ThreadId tid, UWord a1, UWord a2) { POST_MEM_WRITE( a2, sizeof(struct vki_rlimit) ); +#ifdef _RLIMIT_POSIX_FLAG + // Darwin will sometimes set _RLIMIT_POSIX_FLAG on getrlimit calls. + // Unset it here to make the switch case below work correctly. + a1 &= ~_RLIMIT_POSIX_FLAG; +#endif + switch (a1) { case VKI_RLIMIT_NOFILE: ((struct vki_rlimit *)a2)->rlim_cur = VG_(fd_soft_limit); @@ -3063,6 +3172,7 @@ PRE(sys_gettimeofday) PRINT("sys_gettimeofday ( %#lx, %#lx )", ARG1,ARG2); PRE_REG_READ2(long, "gettimeofday", struct timeval *, tv, struct timezone *, tz); + // GrP fixme does darwin write to *tz anymore? if (ARG1 != 0) PRE_timeval_WRITE( "gettimeofday(tv)", ARG1 ); if (ARG2 != 0) @@ -3286,6 +3396,7 @@ PRE(sys_mprotect) if (!ML_(valid_client_addr)(ARG1, ARG2, tid, "mprotect")) { SET_STATUS_Failure( VKI_ENOMEM ); } +#if defined(VKI_PROT_GROWSDOWN) else if (ARG3 & (VKI_PROT_GROWSDOWN|VKI_PROT_GROWSUP)) { /* Deal with mprotects on growable stack areas. @@ -3337,6 +3448,7 @@ PRE(sys_mprotect) SET_STATUS_Failure( VKI_EINVAL ); } } +#endif // defined(VKI_PROT_GROWSDOWN) } POST(sys_mprotect) @@ -3412,6 +3524,7 @@ PRE(sys_open) } PRE_MEM_RASCIIZ( "open(filename)", ARG1 ); +#if HAVE_PROC /* Handle the case where the open is of /proc/self/cmdline or /proc//cmdline, and just give it a copy of the fd for the fake file we cooked up at startup (in m_main). Also, seek the @@ -3436,6 +3549,7 @@ PRE(sys_open) return; } } +#endif // HAVE_PROC /* Otherwise handle normally */ *flags |= SfMayBlock; @@ -3558,6 +3672,7 @@ PRE(sys_readlink) PRE_MEM_WRITE( "readlink(buf)", ARG2,ARG3 ); { +#if HAVE_PROC /* * Handle the case where readlink is looking at /proc/self/exe or * /proc//exe. @@ -3573,6 +3688,7 @@ PRE(sys_readlink) SET_STATUS_from_SysRes( VG_(do_syscall3)(saved, (UWord)name, ARG2, ARG3)); } else +#endif // HAVE_PROC { /* Normal case */ SET_STATUS_from_SysRes( VG_(do_syscall3)(saved, ARG1, ARG2, ARG3)); @@ -3703,12 +3819,19 @@ PRE(sys_setreuid) PRE(sys_setrlimit) { + UWord arg1 = ARG1; PRINT("sys_setrlimit ( %ld, %#lx )", ARG1,ARG2); PRE_REG_READ2(long, "setrlimit", unsigned int, resource, struct rlimit *, rlim); PRE_MEM_READ( "setrlimit(rlim)", ARG2, sizeof(struct vki_rlimit) ); - if (ARG1 == VKI_RLIMIT_NOFILE) { +#ifdef _RLIMIT_POSIX_FLAG + // Darwin will sometimes set _RLIMIT_POSIX_FLAG on setrlimit calls. + // Unset it here to make the if statements below work correctly. + arg1 &= ~_RLIMIT_POSIX_FLAG; +#endif + + if (arg1 == VKI_RLIMIT_NOFILE) { if (((struct vki_rlimit *)ARG2)->rlim_cur > VG_(fd_hard_limit) || ((struct vki_rlimit *)ARG2)->rlim_max != VG_(fd_hard_limit)) { SET_STATUS_Failure( VKI_EPERM ); @@ -3718,7 +3841,7 @@ PRE(sys_setrlimit) SET_STATUS_Success( 0 ); } } - else if (ARG1 == VKI_RLIMIT_DATA) { + else if (arg1 == VKI_RLIMIT_DATA) { if (((struct vki_rlimit *)ARG2)->rlim_cur > VG_(client_rlimit_data).rlim_max || ((struct vki_rlimit *)ARG2)->rlim_max > VG_(client_rlimit_data).rlim_max) { SET_STATUS_Failure( VKI_EPERM ); @@ -3728,7 +3851,7 @@ PRE(sys_setrlimit) SET_STATUS_Success( 0 ); } } - else if (ARG1 == VKI_RLIMIT_STACK && tid == 1) { + else if (arg1 == VKI_RLIMIT_STACK && tid == 1) { if (((struct vki_rlimit *)ARG2)->rlim_cur > VG_(client_rlimit_stack).rlim_max || ((struct vki_rlimit *)ARG2)->rlim_max > VG_(client_rlimit_stack).rlim_max) { SET_STATUS_Failure( VKI_EPERM ); @@ -3927,7 +4050,6 @@ PRE(sys_utimes) PRE_timeval_READ( "utimes(tvp[0])", ARG2 ); PRE_timeval_READ( "utimes(tvp[1])", ARG2+sizeof(struct vki_timeval) ); } - } PRE(sys_acct) diff --git a/coregrind/m_syswrap/syswrap-main.c b/coregrind/m_syswrap/syswrap-main.c index cc6166b95..8238cb5e9 100644 --- a/coregrind/m_syswrap/syswrap-main.c +++ b/coregrind/m_syswrap/syswrap-main.c @@ -29,6 +29,7 @@ */ #include "libvex_guest_offsets.h" +#include "libvex_trc_values.h" #include "pub_core_basics.h" #include "pub_core_aspacemgr.h" #include "pub_core_vki.h" @@ -52,6 +53,9 @@ #include "priv_types_n_macros.h" #include "priv_syswrap-main.h" +#if defined(VGO_darwin) +#include "priv_syswrap-darwin.h" +#endif /* Useful info which needs to be recorded somewhere: Use of registers in syscalls is: @@ -62,9 +66,19 @@ amd64 rax rdi rsi rdx r10 r8 r9 n/a n/a rax (== NUM) ppc32 r0 r3 r4 r5 r6 r7 r8 n/a n/a r3+CR0.SO (== ARG1) ppc64 r0 r3 r4 r5 r6 r7 r8 n/a n/a r3+CR0.SO (== ARG1) + AIX: ppc32 r2 r3 r4 r5 r6 r7 r8 r9 r10 r3(res),r4(err) ppc64 r2 r3 r4 r5 r6 r7 r8 r9 r10 r3(res),r4(err) + + DARWIN: + x86 eax +4 +8 +12 +16 +20 +24 +28 +32 edx:eax, eflags.c + amd64 rax rdi rsi rdx rcx r8 r9 +8 +16 rdx:rax, rflags.c + + For x86-darwin, "+N" denotes "in memory at N(%esp)"; ditto + amd64-darwin. Apparently 0(%esp) is some kind of return address + (perhaps for syscalls done with "sysenter"?) I don't think it is + relevant for syscalls done with "int $0x80/1/2". */ /* This is the top level of the system-call handler module. All @@ -141,6 +155,10 @@ ppc32: Success(N) ==> r3 = N, CR0.SO = 0 Fail(N) ==> r3 = N, CR0.SO = 1 + Darwin: + x86: Success(N) ==> edx:eax = N, cc = 0 + Fail(N) ==> edx:eax = N, cc = 1 + * The post wrapper is called if: - it exists, and @@ -255,6 +273,25 @@ UWord ML_(do_syscall_for_client_WRK)( Word syscallno, const vki_sigset_t *restore_mask, Word sigsetSzB, /* unused */ Word __nr_sigprocmask ); +#elif defined(VGO_darwin) +extern +UWord ML_(do_syscall_for_client_unix_WRK)( Word syscallno, + void* guest_state, + const vki_sigset_t *syscall_mask, + const vki_sigset_t *restore_mask, + Word sigsetSzB ); /* unused */ +extern +UWord ML_(do_syscall_for_client_mach_WRK)( Word syscallno, + void* guest_state, + const vki_sigset_t *syscall_mask, + const vki_sigset_t *restore_mask, + Word sigsetSzB ); /* unused */ +extern +UWord ML_(do_syscall_for_client_mdep_WRK)( Word syscallno, + void* guest_state, + const vki_sigset_t *syscall_mask, + const vki_sigset_t *restore_mask, + Word sigsetSzB ); /* unused */ #else # error "Unknown OS" #endif @@ -278,6 +315,31 @@ void do_syscall_for_client ( Int syscallno, syscall_mask, &saved, 0/*unused:sigsetSzB*/, __NR_rt_sigprocmask ); +# elif defined(VGO_darwin) + switch (VG_DARWIN_SYSNO_CLASS(syscallno)) { + case VG_DARWIN_SYSCALL_CLASS_UNIX: + err = ML_(do_syscall_for_client_unix_WRK)( + VG_DARWIN_SYSNO_NUM(syscallno), &tst->arch.vex, + syscall_mask, &saved, 0/*unused:sigsetSzB*/ + ); + break; + case VG_DARWIN_SYSCALL_CLASS_MACH: + err = ML_(do_syscall_for_client_mach_WRK)( + VG_DARWIN_SYSNO_NUM(syscallno), &tst->arch.vex, + syscall_mask, &saved, 0/*unused:sigsetSzB*/ + ); + break; + case VG_DARWIN_SYSCALL_CLASS_MDEP: + err = ML_(do_syscall_for_client_mdep_WRK)( + VG_DARWIN_SYSNO_NUM(syscallno), &tst->arch.vex, + syscall_mask, &saved, 0/*unused:sigsetSzB*/ + ); + break; + default: + vg_assert(0); + /*NOTREACHED*/ + break; + } # else # error "Unknown OS" # endif @@ -313,6 +375,14 @@ Bool eq_SyscallStatus ( SyscallStatus* s1, SyscallStatus* s2 ) /* was: return s1->what == s2->what && sr_EQ( s1->sres, s2->sres ); */ if (s1->what == s2->what && sr_EQ( s1->sres, s2->sres )) return True; +# if defined(VGO_darwin) + /* Darwin-specific debugging guff */ + vg_assert(s1->what == s2->what); + VG_(printf)("eq_SyscallStatus:\n"); + VG_(printf)(" {%lu %lu %u}\n", s1->sres._wLO, s1->sres._wHI, s1->sres._mode); + VG_(printf)(" {%lu %lu %u}\n", s2->sres._wLO, s2->sres._wHI, s2->sres._mode); + vg_assert(0); +# endif return False; } @@ -334,7 +404,8 @@ SyscallStatus convert_SysRes_to_SyscallStatus ( SysRes res ) static void getSyscallArgsFromGuestState ( /*OUT*/SyscallArgs* canonical, - /*IN*/ VexGuestArchState* gst_vanilla ) + /*IN*/ VexGuestArchState* gst_vanilla, + /*IN*/ UInt trc ) { #if defined(VGP_x86_linux) VexGuestX86State* gst = (VexGuestX86State*)gst_vanilla; @@ -411,6 +482,120 @@ void getSyscallArgsFromGuestState ( /*OUT*/SyscallArgs* canonical, canonical->arg7 = gst->guest_GPR9; canonical->arg8 = gst->guest_GPR10; +#elif defined(VGP_x86_darwin) + VexGuestX86State* gst = (VexGuestX86State*)gst_vanilla; + UWord *stack = (UWord *)gst->guest_ESP; + // GrP fixme hope syscalls aren't called with really shallow stacks... + canonical->sysno = gst->guest_EAX; + if (canonical->sysno != 0) { + // stack[0] is return address + canonical->arg1 = stack[1]; + canonical->arg2 = stack[2]; + canonical->arg3 = stack[3]; + canonical->arg4 = stack[4]; + canonical->arg5 = stack[5]; + canonical->arg6 = stack[6]; + canonical->arg7 = stack[7]; + canonical->arg8 = stack[8]; + } else { + // GrP fixme hack handle syscall() + // GrP fixme what about __syscall() ? + // stack[0] is return address + // DDD: the tool can't see that the params have been shifted! Can + // lead to incorrect checking, I think, because the PRRAn/PSARn + // macros will mention the pre-shifted args. + canonical->sysno = stack[1]; + vg_assert(canonical->sysno != 0); + canonical->arg1 = stack[2]; + canonical->arg2 = stack[3]; + canonical->arg3 = stack[4]; + canonical->arg4 = stack[5]; + canonical->arg5 = stack[6]; + canonical->arg6 = stack[7]; + canonical->arg7 = stack[8]; + canonical->arg8 = stack[9]; + + PRINT("SYSCALL[%d,?](%3lld) syscall(#%ld, ...); please stand by...\n", + VG_(getpid)(), /*tid,*/ (Long)0, canonical->sysno); + } + + // DDD: Would it be better to stash the JMP kind into the Darwin + // thread state rather than passing in the trc? + switch (trc) { + case VEX_TRC_JMP_SYS_INT128: + // int $0x80 = Unix, 64-bit result + vg_assert(canonical->sysno >= 0); + canonical->sysno = VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(canonical->sysno); + break; + case VEX_TRC_JMP_SYS_SYSENTER: + // syscall = Unix, 32-bit result + // OR Mach, 32-bit result + if (canonical->sysno >= 0) { + // GrP fixme hack I386_SYSCALL_NUMBER_MASK + canonical->sysno = VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(canonical->sysno + & 0xffff); + } else { + canonical->sysno = VG_DARWIN_SYSCALL_CONSTRUCT_MACH(-canonical->sysno); + } + break; + case VEX_TRC_JMP_SYS_INT129: + // int $0x81 = Mach, 32-bit result + vg_assert(canonical->sysno < 0); + canonical->sysno = VG_DARWIN_SYSCALL_CONSTRUCT_MACH(-canonical->sysno); + break; + case VEX_TRC_JMP_SYS_INT130: + // int $0x82 = mdep, 32-bit result + vg_assert(canonical->sysno >= 0); + canonical->sysno = VG_DARWIN_SYSCALL_CONSTRUCT_MDEP(canonical->sysno); + break; + default: + vg_assert(0); + break; + } + +#elif defined(VGP_amd64_darwin) + VexGuestAMD64State* gst = (VexGuestAMD64State*)gst_vanilla; + UWord *stack = (UWord *)gst->guest_RSP; + + vg_assert(trc == VEX_TRC_JMP_SYS_SYSCALL); + + // GrP fixme hope syscalls aren't called with really shallow stacks... + canonical->sysno = gst->guest_RAX; + if (canonical->sysno != __NR_syscall) { + // stack[0] is return address + canonical->arg1 = gst->guest_RDI; + canonical->arg2 = gst->guest_RSI; + canonical->arg3 = gst->guest_RDX; + canonical->arg4 = gst->guest_R10; // not rcx with syscall insn + canonical->arg5 = gst->guest_R8; + canonical->arg6 = gst->guest_R9; + canonical->arg7 = stack[1]; + canonical->arg8 = stack[2]; + } else { + // GrP fixme hack handle syscall() + // GrP fixme what about __syscall() ? + // stack[0] is return address + // DDD: the tool can't see that the params have been shifted! Can + // lead to incorrect checking, I think, because the PRRAn/PSARn + // macros will mention the pre-shifted args. + canonical->sysno = VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(gst->guest_RDI); + vg_assert(canonical->sysno != __NR_syscall); + canonical->arg1 = gst->guest_RSI; + canonical->arg2 = gst->guest_RDX; + canonical->arg3 = gst->guest_R10; // not rcx with syscall insn + canonical->arg4 = gst->guest_R8; + canonical->arg5 = gst->guest_R9; + canonical->arg6 = stack[1]; + canonical->arg7 = stack[2]; + canonical->arg8 = stack[3]; + + PRINT("SYSCALL[%d,?](%3lld) syscall(#%lx, ...); please stand by...\n", + VG_(getpid)(), /*tid,*/ (Long)0, + VG_DARWIN_SYSNO_PRINT(canonical->sysno)); + } + + // no canonical->sysno adjustment needed + #else # error "getSyscallArgsFromGuestState: unknown arch" #endif @@ -484,6 +669,40 @@ void putSyscallArgsIntoGuestState ( /*IN*/ SyscallArgs* canonical, gst->guest_GPR9 = canonical->arg7; gst->guest_GPR10 = canonical->arg8; +#elif defined(VGP_x86_darwin) + VexGuestX86State* gst = (VexGuestX86State*)gst_vanilla; + UWord *stack = (UWord *)gst->guest_ESP; + + gst->guest_EAX = VG_DARWIN_SYSNO_NUM(canonical->sysno); + + // GrP fixme? gst->guest_TEMP_EFLAG_C = 0; + // stack[0] is return address + stack[1] = canonical->arg1; + stack[2] = canonical->arg2; + stack[3] = canonical->arg3; + stack[4] = canonical->arg4; + stack[5] = canonical->arg5; + stack[6] = canonical->arg6; + stack[7] = canonical->arg7; + stack[8] = canonical->arg8; + +#elif defined(VGP_amd64_darwin) + VexGuestAMD64State* gst = (VexGuestAMD64State*)gst_vanilla; + UWord *stack = (UWord *)gst->guest_RSP; + + gst->guest_RAX = VG_DARWIN_SYSNO_NUM(canonical->sysno); + // GrP fixme? gst->guest_TEMP_EFLAG_C = 0; + + // stack[0] is return address + gst->guest_RDI = canonical->arg1; + gst->guest_RSI = canonical->arg2; + gst->guest_RDX = canonical->arg3; + gst->guest_RCX = canonical->arg4; + gst->guest_R8 = canonical->arg5; + gst->guest_R9 = canonical->arg6; + stack[1] = canonical->arg7; + stack[2] = canonical->arg8; + #else # error "putSyscallArgsIntoGuestState: unknown arch" #endif @@ -529,13 +748,77 @@ void getSyscallStatusFromGuestState ( /*OUT*/SyscallStatus* canonical, gst->guest_GPR4 ); canonical->what = SsComplete; +# elif defined(VGP_x86_darwin) + /* duplicates logic in m_signals.VG_UCONTEXT_SYSCALL_SYSRES */ + VexGuestX86State* gst = (VexGuestX86State*)gst_vanilla; + UInt carry = 1 & LibVEX_GuestX86_get_eflags(gst); + UInt err = 0; + UInt wLO = 0; + UInt wHI = 0; + switch (gst->guest_SC_CLASS) { + case VG_DARWIN_SYSCALL_CLASS_UNIX: + // int $0x80 = Unix, 64-bit result + err = carry; + wLO = gst->guest_EAX; + wHI = gst->guest_EDX; + break; + case VG_DARWIN_SYSCALL_CLASS_MACH: + // int $0x81 = Mach, 32-bit result + wLO = gst->guest_EAX; + break; + case VG_DARWIN_SYSCALL_CLASS_MDEP: + // int $0x82 = mdep, 32-bit result + wLO = gst->guest_EAX; + break; + default: + vg_assert(0); + break; + } + canonical->sres = VG_(mk_SysRes_x86_darwin)( + gst->guest_SC_CLASS, err ? True : False, + wHI, wLO + ); + canonical->what = SsComplete; + +# elif defined(VGP_amd64_darwin) + /* duplicates logic in m_signals.VG_UCONTEXT_SYSCALL_SYSRES */ + VexGuestAMD64State* gst = (VexGuestAMD64State*)gst_vanilla; + ULong carry = 1 & LibVEX_GuestAMD64_get_rflags(gst); + ULong err = 0; + ULong wLO = 0; + ULong wHI = 0; + switch (gst->guest_SC_CLASS) { + case VG_DARWIN_SYSCALL_CLASS_UNIX: + // syscall = Unix, 128-bit result + err = carry; + wLO = gst->guest_RAX; + wHI = gst->guest_RDX; + break; + case VG_DARWIN_SYSCALL_CLASS_MACH: + // syscall = Mach, 64-bit result + wLO = gst->guest_RAX; + break; + case VG_DARWIN_SYSCALL_CLASS_MDEP: + // syscall = mdep, 64-bit result + wLO = gst->guest_RAX; + break; + default: + vg_assert(0); + break; + } + canonical->sres = VG_(mk_SysRes_amd64_darwin)( + gst->guest_SC_CLASS, err ? True : False, + wHI, wLO + ); + canonical->what = SsComplete; + # else # error "getSyscallStatusFromGuestState: unknown arch" # endif } static -void putSyscallStatusIntoGuestState ( /*IN*/ ThreadId tid, +void putSyscallStatusIntoGuestState ( /*IN*/ ThreadId tid, /*IN*/ SyscallStatus* canonical, /*OUT*/VexGuestArchState* gst_vanilla ) { @@ -623,6 +906,72 @@ void putSyscallStatusIntoGuestState ( /*IN*/ ThreadId tid, VG_TRACK( post_reg_write, Vg_CoreSysCall, tid, OFFSET_ppc64_GPR4, sizeof(UWord) ); +#elif defined(VGP_x86_darwin) + VexGuestX86State* gst = (VexGuestX86State*)gst_vanilla; + SysRes sres = canonical->sres; + vg_assert(canonical->what == SsComplete); + /* Unfortunately here we have to break abstraction and look + directly inside 'res', in order to decide what to do. */ + switch (sres._mode) { + case SysRes_MACH: // int $0x81 = Mach, 32-bit result + case SysRes_MDEP: // int $0x82 = mdep, 32-bit result + gst->guest_EAX = sres._wLO; + VG_TRACK( post_reg_write, Vg_CoreSysCall, tid, + OFFSET_x86_EAX, sizeof(UInt) ); + break; + case SysRes_UNIX_OK: // int $0x80 = Unix, 64-bit result + case SysRes_UNIX_ERR: // int $0x80 = Unix, 64-bit error + gst->guest_EAX = sres._wLO; + VG_TRACK( post_reg_write, Vg_CoreSysCall, tid, + OFFSET_x86_EAX, sizeof(UInt) ); + gst->guest_EDX = sres._wHI; + VG_TRACK( post_reg_write, Vg_CoreSysCall, tid, + OFFSET_x86_EDX, sizeof(UInt) ); + LibVEX_GuestX86_put_eflag_c( sres._mode==SysRes_UNIX_ERR ? 1 : 0, + gst ); + // GrP fixme sets defined for entire eflags, not just bit c + // DDD: this breaks exp-ptrcheck. + VG_TRACK( post_reg_write, Vg_CoreSysCall, tid, + offsetof(VexGuestX86State, guest_CC_DEP1), sizeof(UInt) ); + break; + default: + vg_assert(0); + break; + } + +#elif defined(VGP_amd64_darwin) + VexGuestAMD64State* gst = (VexGuestAMD64State*)gst_vanilla; + SysRes sres = canonical->sres; + vg_assert(canonical->what == SsComplete); + /* Unfortunately here we have to break abstraction and look + directly inside 'res', in order to decide what to do. */ + switch (sres._mode) { + case SysRes_MACH: // syscall = Mach, 64-bit result + case SysRes_MDEP: // syscall = mdep, 64-bit result + gst->guest_RAX = sres._wLO; + VG_TRACK( post_reg_write, Vg_CoreSysCall, tid, + OFFSET_amd64_RAX, sizeof(ULong) ); + break; + case SysRes_UNIX_OK: // syscall = Unix, 128-bit result + case SysRes_UNIX_ERR: // syscall = Unix, 128-bit error + gst->guest_RAX = sres._wLO; + VG_TRACK( post_reg_write, Vg_CoreSysCall, tid, + OFFSET_amd64_RAX, sizeof(ULong) ); + gst->guest_RDX = sres._wHI; + VG_TRACK( post_reg_write, Vg_CoreSysCall, tid, + OFFSET_amd64_RDX, sizeof(ULong) ); + LibVEX_GuestAMD64_put_rflag_c( sres._mode==SysRes_UNIX_ERR ? 1 : 0, + gst ); + // GrP fixme sets defined for entire rflags, not just bit c + // DDD: this breaks exp-ptrcheck. + VG_TRACK( post_reg_write, Vg_CoreSysCall, tid, + offsetof(VexGuestAMD64State, guest_CC_DEP1), sizeof(ULong) ); + break; + default: + vg_assert(0); + break; + } + # else # error "putSyscallStatusIntoGuestState: unknown arch" # endif @@ -702,6 +1051,29 @@ void getSyscallArgLayout ( /*OUT*/SyscallArgLayout* layout ) layout->o_arg7 = OFFSET_ppc64_GPR9; layout->o_arg8 = OFFSET_ppc64_GPR10; +#elif defined(VGP_x86_darwin) + layout->o_sysno = OFFSET_x86_EAX; + // syscall parameters are on stack in C convention + layout->s_arg1 = sizeof(UWord) * 1; + layout->s_arg2 = sizeof(UWord) * 2; + layout->s_arg3 = sizeof(UWord) * 3; + layout->s_arg4 = sizeof(UWord) * 4; + layout->s_arg5 = sizeof(UWord) * 5; + layout->s_arg6 = sizeof(UWord) * 6; + layout->s_arg7 = sizeof(UWord) * 7; + layout->s_arg8 = sizeof(UWord) * 8; + +#elif defined(VGP_amd64_darwin) + layout->o_sysno = OFFSET_amd64_RAX; + layout->o_arg1 = OFFSET_amd64_RDI; + layout->o_arg2 = OFFSET_amd64_RSI; + layout->o_arg3 = OFFSET_amd64_RDX; + layout->o_arg4 = OFFSET_amd64_RCX; + layout->o_arg5 = OFFSET_amd64_R8; + layout->o_arg6 = OFFSET_amd64_R9; + layout->s_arg7 = sizeof(UWord) * 1; + layout->s_arg8 = sizeof(UWord) * 2; + #else # error "getSyscallLayout: unknown arch" #endif @@ -723,11 +1095,20 @@ void bad_before ( ThreadId tid, /*OUT*/UWord* flags ) { VG_(message) - (Vg_DebugMsg,"WARNING: unhandled syscall: %llu", (ULong)args->sysno); -# if defined(VGO_aix5) + (Vg_DebugMsg,"WARNING: unhandled syscall: %lld", (Long)args->sysno); + // DDD: make this generic with a common function. +# if defined(VGO_linux) + // nothing +# elif defined(VGO_aix5) VG_(message) (Vg_DebugMsg," name of syscall: \"%s\"", VG_(aix5_sysno_to_sysname)(args->sysno)); +# elif defined(VGO_darwin) + VG_(message) + (Vg_DebugMsg," a.k.a.: %lld", + (Long)VG_DARWIN_SYSNO_PRINT(args->sysno)); +# else +# error unknown OS # endif if (VG_(clo_verbosity) > 1) { VG_(get_and_pp_StackTrace)(tid, VG_(clo_backtrace_size)); @@ -747,7 +1128,7 @@ void bad_before ( ThreadId tid, static SyscallTableEntry bad_sys = { bad_before, NULL }; -static const SyscallTableEntry* get_syscall_entry ( UInt syscallno ) +static const SyscallTableEntry* get_syscall_entry ( Int syscallno ) { const SyscallTableEntry* sys = NULL; @@ -762,6 +1143,30 @@ static const SyscallTableEntry* get_syscall_entry ( UInt syscallno ) # elif defined(VGP_ppc64_aix5) sys = ML_(get_ppc64_aix5_syscall_entry) ( syscallno ); +# elif defined(VGO_darwin) + Int idx = VG_DARWIN_SYSNO_INDEX(syscallno); + + switch (VG_DARWIN_SYSNO_CLASS(syscallno)) { + case VG_DARWIN_SYSCALL_CLASS_UNIX: + if (idx >= 0 && idx < ML_(syscall_table_size) && + ML_(syscall_table)[idx].before != NULL) + sys = &ML_(syscall_table)[idx]; + break; + case VG_DARWIN_SYSCALL_CLASS_MACH: + if (idx >= 0 && idx < ML_(mach_trap_table_size) && + ML_(mach_trap_table)[idx].before != NULL) + sys = &ML_(mach_trap_table)[idx]; + break; + case VG_DARWIN_SYSCALL_CLASS_MDEP: + if (idx >= 0 && idx < ML_(mdep_trap_table_size) && + ML_(mdep_trap_table)[idx].before != NULL) + sys = &ML_(mdep_trap_table)[idx]; + break; + default: + vg_assert(0); + break; + } + # else # error Unknown OS # endif @@ -815,7 +1220,7 @@ static void ensure_initialised ( void ) /* --- This is the main function of this file. --- */ -void VG_(client_syscall) ( ThreadId tid ) +void VG_(client_syscall) ( ThreadId tid, UInt trc ) { Word sysno; ThreadState* tst; @@ -924,7 +1329,7 @@ void VG_(client_syscall) ( ThreadId tid ) sci = & syscallInfo[tid]; vg_assert(sci->status.what == SsIdle); - getSyscallArgsFromGuestState( &sci->orig_args, &tst->arch.vex ); + getSyscallArgsFromGuestState( &sci->orig_args, &tst->arch.vex, trc ); /* Copy .orig_args to .args. The pre-handler may modify .args, but we want to keep the originals too, just in case. */ @@ -934,6 +1339,19 @@ void VG_(client_syscall) ( ThreadId tid ) is interrupted by a signal. */ sysno = sci->orig_args.sysno; +# if defined(VGO_darwin) + /* Record syscall class. But why? Because the syscall might be + interrupted by a signal, and in the signal handler (which will + be m_signals.async_signalhandler) we will need to build a SysRes + reflecting the syscall return result. In order to do that we + need to know the syscall class. Hence stash it in the guest + state of this thread. This madness is not needed on Linux or + AIX5, because those OSs only have a single syscall return + convention and so there is no ambiguity involved in converting + the post-signal machine state into a SysRes. */ + tst->arch.vex.guest_SC_CLASS = VG_DARWIN_SYSNO_CLASS(sysno); +# endif + /* The default what-to-do-next thing is hand the syscall to the kernel, so we pre-set that here. Set .sres to something harmless looking (is irrelevant because .what is not @@ -967,7 +1385,16 @@ void VG_(client_syscall) ( ThreadId tid ) sci->flags is zero. */ - PRINT("SYSCALL[%d,%d](%3lld) ", VG_(getpid)(), tid, (ULong)sysno); + PRINT("SYSCALL[%d,%d](%3lld) ", VG_(getpid)(), tid, + // DDD: make this generic + #if defined(VGO_linux) || defined(VGO_aix5) + (Long)sysno + #elif defined(VGO_darwin) + (Long)VG_DARWIN_SYSNO_PRINT(sysno) + #else + # error Unknown OS + #endif + ); /* Do any pre-syscall actions */ if (VG_(needs).syscall_wrapper) { @@ -1055,7 +1482,12 @@ void VG_(client_syscall) ( ThreadId tid ) /* Gack. More impedance matching. Copy the possibly modified syscall args back into the guest state. */ - vg_assert(eq_SyscallArgs(&sci->args, &sci->orig_args)); + /* JRS 2009-Mar-16: if the syscall args are possibly modified, + then this assertion is senseless: + vg_assert(eq_SyscallArgs(&sci->args, &sci->orig_args)); + The case that exposed it was sys_posix_spawn on Darwin, + which heavily modifies its arguments but then lets the call + go through anyway, with SfToBlock set, hence we end up here. */ putSyscallArgsIntoGuestState( &sci->args, &tst->arch.vex ); /* Drop the bigLock */ @@ -1077,6 +1509,13 @@ void VG_(client_syscall) ( ThreadId tid ) VG_(post_syscall). Once that's done, control drops back to the scheduler. */ + /* Darwin: do_syscall_for_client may not return if the + syscall was workq_ops(WQOPS_THREAD_RETURN) and the kernel + responded by starting the thread at wqthread_hijack(reuse=1) + (to run another workqueue item). In that case, wqthread_hijack + calls ML_(wqthread_continue), which is similar to + VG_(fixup_guest_state_after_syscall_interrupted). */ + /* Reacquire the lock */ VG_(acquire_BigLock)(tid, "VG_(client_syscall)[async]"); @@ -1089,6 +1528,10 @@ void VG_(client_syscall) ( ThreadId tid ) if (VG_(clo_trace_syscalls)) { Bool failed = sr_isError(sci->status.sres); Word tmp_sysno = sysno; +# if defined(VGO_darwin) + // DDD: genericise this + tmp_sysno = VG_DARWIN_SYSNO_PRINT(tmp_sysno); +# endif if (failed) { PRINT("SYSCALL[%d,%d](%3ld) ... [async] --> Failure(0x%llx)", VG_(getpid)(), tid, tmp_sysno, @@ -1165,6 +1608,7 @@ void VG_(client_syscall) ( ThreadId tid ) There are two ways to get here: the normal way -- being called by VG_(client_syscall), and the unusual way, from VG_(fixup_guest_state_after_syscall_interrupted). + Darwin: there's a third way, ML_(wqthread_continue). */ void VG_(post_syscall) (ThreadId tid) { @@ -1198,6 +1642,15 @@ void VG_(post_syscall) (ThreadId tid) getSyscallStatusFromGuestState( &test_status, &tst->arch.vex ); if (!(sci->flags & SfNoWriteResult)) vg_assert(eq_SyscallStatus( &sci->status, &test_status )); + /* Failure of the above assertion on Darwin can indicate a problem + in the syscall wrappers that pre-fail or pre-succeed the + syscall, by calling SET_STATUS_Success or SET_STATUS_Failure, + when they really should call SET_STATUS_from_SysRes. The former + create a UNIX-class syscall result on Darwin, which may not be + correct for the syscall; if that's the case then this assertion + fires. See PRE(pthread_set_self) for an example. On non-Darwin + platforms this assertion is should never fail, and this comment + is completely irrelevant. */ /* Ok, looks sane */ /* Get the system call number. Because the pre-handler isn't @@ -1283,6 +1736,23 @@ void VG_(post_syscall) (ThreadId tid) extern const Addr ML_(blksys_complete); extern const Addr ML_(blksys_committed); extern const Addr ML_(blksys_finished); +#elif defined(VGO_darwin) + /* Darwin requires extra uglyness */ + extern const Addr ML_(blksys_setup_MACH); + extern const Addr ML_(blksys_restart_MACH); + extern const Addr ML_(blksys_complete_MACH); + extern const Addr ML_(blksys_committed_MACH); + extern const Addr ML_(blksys_finished_MACH); + extern const Addr ML_(blksys_setup_MDEP); + extern const Addr ML_(blksys_restart_MDEP); + extern const Addr ML_(blksys_complete_MDEP); + extern const Addr ML_(blksys_committed_MDEP); + extern const Addr ML_(blksys_finished_MDEP); + extern const Addr ML_(blksys_setup_UNIX); + extern const Addr ML_(blksys_restart_UNIX); + extern const Addr ML_(blksys_complete_UNIX); + extern const Addr ML_(blksys_committed_UNIX); + extern const Addr ML_(blksys_finished_UNIX); #else # error "Unknown OS" #endif @@ -1375,6 +1845,34 @@ void ML_(fixup_guest_state_to_restart_syscall) ( ThreadArchState* arch ) vg_assert(p[0] == 0x44 && p[1] == 0x0 && p[2] == 0x0 && p[3] == 0x2); } +#elif defined(VGP_x86_darwin) + arch->vex.guest_EIP = arch->vex.guest_IP_AT_SYSCALL; + + /* Make sure our caller is actually sane, and we're really backing + back over a syscall. + + int $0x80 == CD 80 + int $0x81 == CD 81 + int $0x82 == CD 82 + sysenter == 0F 34 + */ + { + UChar *p = (UChar *)arch->vex.guest_EIP; + Bool ok = (p[0] == 0xCD && p[1] == 0x80) + || (p[0] == 0xCD && p[1] == 0x81) + || (p[0] == 0xCD && p[1] == 0x82) + || (p[0] == 0x0F && p[1] == 0x34); + if (!ok) + VG_(message)(Vg_DebugMsg, + "?! restarting over syscall at %#x %02x %02x\n", + arch->vex.guest_EIP, p[0], p[1]); + vg_assert(ok); + } + +#elif defined(VGP_amd64_darwin) + // DDD: #warning GrP fixme amd64 restart unimplemented + vg_assert(0); + #else # error "ML_(fixup_guest_state_to_restart_syscall): unknown plat" #endif @@ -1450,6 +1948,28 @@ VG_(fixup_guest_state_after_syscall_interrupted)( ThreadId tid, = ip >= ML_(blksys_complete) && ip < ML_(blksys_committed); in_committed_to_finished = ip >= ML_(blksys_committed) && ip < ML_(blksys_finished); +# elif defined(VGO_darwin) + outside_range + = (ip < ML_(blksys_setup_MACH) || ip >= ML_(blksys_finished_MACH)) + && (ip < ML_(blksys_setup_MDEP) || ip >= ML_(blksys_finished_MDEP)) + && (ip < ML_(blksys_setup_UNIX) || ip >= ML_(blksys_finished_UNIX)); + in_setup_to_restart + = (ip >= ML_(blksys_setup_MACH) && ip < ML_(blksys_restart_MACH)) + || (ip >= ML_(blksys_setup_MDEP) && ip < ML_(blksys_restart_MDEP)) + || (ip >= ML_(blksys_setup_UNIX) && ip < ML_(blksys_restart_UNIX)); + at_restart + = (ip == ML_(blksys_restart_MACH)) + || (ip == ML_(blksys_restart_MDEP)) + || (ip == ML_(blksys_restart_UNIX)); + in_complete_to_committed + = (ip >= ML_(blksys_complete_MACH) && ip < ML_(blksys_committed_MACH)) + || (ip >= ML_(blksys_complete_MDEP) && ip < ML_(blksys_committed_MDEP)) + || (ip >= ML_(blksys_complete_UNIX) && ip < ML_(blksys_committed_UNIX)); + in_committed_to_finished + = (ip >= ML_(blksys_committed_MACH) && ip < ML_(blksys_finished_MACH)) + || (ip >= ML_(blksys_committed_MDEP) && ip < ML_(blksys_finished_MDEP)) + || (ip >= ML_(blksys_committed_UNIX) && ip < ML_(blksys_finished_UNIX)); + /* Wasn't that just So Much Fun? Does your head hurt yet? Mine does. */ # else # error "Unknown OS" # endif @@ -1558,6 +2078,45 @@ VG_(fixup_guest_state_after_syscall_interrupted)( ThreadId tid, } +#if defined(VGO_darwin) +// Clean up after workq_ops(WQOPS_THREAD_RETURN) jumped to wqthread_hijack. +// This is similar to VG_(fixup_guest_state_after_syscall_interrupted). +// This longjmps back to the scheduler. +void ML_(wqthread_continue_NORETURN)(ThreadId tid) +{ + ThreadState* tst; + SyscallInfo* sci; + + VG_(acquire_BigLock)(tid, "wqthread_continue_NORETURN"); + + PRINT("SYSCALL[%d,%d](%3lld) workq_ops() starting new workqueue item\n", + VG_(getpid)(), tid, (Long)VG_DARWIN_SYSNO_PRINT(__NR_workq_ops)); + + vg_assert(VG_(is_valid_tid)(tid)); + vg_assert(tid >= 1 && tid < VG_N_THREADS); + vg_assert(VG_(is_running_thread)(tid)); + + tst = VG_(get_ThreadState)(tid); + sci = & syscallInfo[tid]; + vg_assert(sci->status.what != SsIdle); + vg_assert(tst->os_state.wq_jmpbuf_valid); // check this BEFORE post_syscall + + // Pretend the syscall completed normally, but don't touch the thread state. + sci->status = convert_SysRes_to_SyscallStatus( VG_(mk_SysRes_Success)(0) ); + sci->flags |= SfNoWriteResult; + VG_(post_syscall)(tid); + + sci->status.what = SsIdle; + + vg_assert(tst->sched_jmpbuf_valid); + __builtin_longjmp(tst->sched_jmpbuf, True); + + /* NOTREACHED */ + vg_assert(0); +} +#endif + + /* --------------------------------------------------------------------- A place to store the where-to-call-when-really-done pointer ------------------------------------------------------------------ */ diff --git a/coregrind/m_syswrap/syswrap-x86-darwin.c b/coregrind/m_syswrap/syswrap-x86-darwin.c new file mode 100644 index 000000000..553203323 --- /dev/null +++ b/coregrind/m_syswrap/syswrap-x86-darwin.c @@ -0,0 +1,502 @@ + +/*--------------------------------------------------------------------*/ +/*--- Darwin-specific syscalls, etc. syswrap-x86-darwin.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2005-2009 Apple Inc. + Greg Parker gparker@apple.com + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics.h" +#include "pub_core_vki.h" +#include "pub_core_threadstate.h" +#include "pub_core_aspacemgr.h" +#include "pub_core_xarray.h" +#include "pub_core_clientstate.h" +#include "pub_core_debuglog.h" +#include "pub_core_debuginfo.h" // VG_(di_notify_*) +#include "pub_core_transtab.h" // VG_(discard_translations) +#include "pub_core_libcbase.h" +#include "pub_core_libcassert.h" +#include "pub_core_libcfile.h" +#include "pub_core_libcprint.h" +#include "pub_core_libcproc.h" +#include "pub_core_libcsignal.h" +#include "pub_core_mallocfree.h" +#include "pub_core_options.h" +#include "pub_core_scheduler.h" +#include "pub_core_signals.h" +#include "pub_core_syscall.h" +#include "pub_core_syswrap.h" +#include "pub_core_tooliface.h" + +#include "priv_types_n_macros.h" +#include "priv_syswrap-generic.h" /* for decls of generic wrappers */ +#include "priv_syswrap-darwin.h" /* for decls of darwin-ish wrappers */ +#include "priv_syswrap-main.h" + + +#include + +static void x86_thread_state32_from_vex(i386_thread_state_t *mach, + VexGuestX86State *vex) +{ + mach->__eax = vex->guest_EAX; + mach->__ebx = vex->guest_EBX; + mach->__ecx = vex->guest_ECX; + mach->__edx = vex->guest_EDX; + mach->__edi = vex->guest_EDI; + mach->__esi = vex->guest_ESI; + mach->__ebp = vex->guest_EBP; + mach->__esp = vex->guest_ESP; + mach->__ss = vex->guest_SS; + mach->__eflags = LibVEX_GuestX86_get_eflags(vex); + mach->__eip = vex->guest_EIP; + mach->__cs = vex->guest_CS; + mach->__ds = vex->guest_DS; + mach->__es = vex->guest_ES; + mach->__fs = vex->guest_FS; + mach->__gs = vex->guest_GS; +} + + +static void x86_float_state32_from_vex(i386_float_state_t *mach, + VexGuestX86State *vex) +{ + // DDD: #warning GrP fixme fp state + + VG_(memcpy)(&mach->__fpu_xmm0, &vex->guest_XMM0, 8 * sizeof(mach->__fpu_xmm0)); +} + + +void thread_state_from_vex(thread_state_t mach_generic, + thread_state_flavor_t flavor, + mach_msg_type_number_t count, + VexGuestArchState *vex_generic) +{ + VexGuestX86State *vex = (VexGuestX86State *)vex_generic; + + switch (flavor) { + case i386_THREAD_STATE: + vg_assert(count == i386_THREAD_STATE_COUNT); + x86_thread_state32_from_vex((i386_thread_state_t *)mach_generic, vex); + break; + + case i386_FLOAT_STATE: + vg_assert(count == i386_FLOAT_STATE_COUNT); + x86_float_state32_from_vex((i386_float_state_t *)mach_generic, vex); + break; + + default: + vg_assert(0); + } +} + + +static void x86_thread_state32_to_vex(const i386_thread_state_t *mach, + VexGuestX86State *vex) +{ + LibVEX_GuestX86_initialise(vex); + vex->guest_EAX = mach->__eax; + vex->guest_EBX = mach->__ebx; + vex->guest_ECX = mach->__ecx; + vex->guest_EDX = mach->__edx; + vex->guest_EDI = mach->__edi; + vex->guest_ESI = mach->__esi; + vex->guest_EBP = mach->__ebp; + vex->guest_ESP = mach->__esp; + vex->guest_SS = mach->__ss; + // DDD: #warning GrP fixme eflags + vex->guest_EIP = mach->__eip; + vex->guest_CS = mach->__cs; + vex->guest_DS = mach->__ds; + vex->guest_ES = mach->__es; + vex->guest_FS = mach->__fs; + vex->guest_GS = mach->__gs; +} + +static void x86_float_state32_to_vex(const i386_float_state_t *mach, + VexGuestX86State *vex) +{ + // DDD: #warning GrP fixme fp state + + VG_(memcpy)(&vex->guest_XMM0, &mach->__fpu_xmm0, 8 * sizeof(mach->__fpu_xmm0)); +} + + +void thread_state_to_vex(const thread_state_t mach_generic, + thread_state_flavor_t flavor, + mach_msg_type_number_t count, + VexGuestArchState *vex_generic) +{ + VexGuestX86State *vex = (VexGuestX86State *)vex_generic; + + switch(flavor) { + case i386_THREAD_STATE: + vg_assert(count == i386_THREAD_STATE_COUNT); + x86_thread_state32_to_vex((const i386_thread_state_t*)mach_generic,vex); + break; + case i386_FLOAT_STATE: + vg_assert(count == i386_FLOAT_STATE_COUNT); + x86_float_state32_to_vex((const i386_float_state_t*)mach_generic,vex); + break; + + default: + vg_assert(0); + break; + } +} + + +ThreadState *build_thread(const thread_state_t state, + thread_state_flavor_t flavor, + mach_msg_type_number_t count) +{ + ThreadId tid = VG_(alloc_ThreadState)(); + ThreadState *tst = VG_(get_ThreadState)(tid); + + vg_assert(flavor == i386_THREAD_STATE); + vg_assert(count == i386_THREAD_STATE_COUNT); + + // Initialize machine registers + + thread_state_to_vex(state, flavor, count, &tst->arch.vex); + + I_die_here; + // GrP fixme signals, sig_mask, tmp_sig_mask, os_state.parent + + find_stack_segment(tid, tst->arch.vex.guest_ESP); + + return tst; +} + + +// Edit the thread state to send to the real kernel. +// The real thread will run start_thread_NORETURN(tst) +// on a separate non-client stack. +void hijack_thread_state(thread_state_t mach_generic, + thread_state_flavor_t flavor, + mach_msg_type_number_t count, + ThreadState *tst) +{ + i386_thread_state_t *mach = (i386_thread_state_t *)mach_generic; + char *stack; + + vg_assert(flavor == i386_THREAD_STATE); + vg_assert(count == i386_THREAD_STATE_COUNT); + + stack = (char *)allocstack(tst->tid); + stack -= 64+320; // make room for top frame + memset(stack, 0, 64+320); // ...and clear it + *(uintptr_t *)stack = (uintptr_t)tst; // set parameter + stack -= sizeof(uintptr_t); + *(uintptr_t *)stack = 0; // push fake return address + + mach->__eip = (uintptr_t)&start_thread_NORETURN; + mach->__esp = (uintptr_t)stack; +} + + +/* Call f(arg1), but first switch stacks, using 'stack' as the new + stack, and use 'retaddr' as f's return-to address. Also, clear all + the integer registers before entering f.*/ +__attribute__((noreturn)) +void call_on_new_stack_0_1 ( Addr stack, + Addr retaddr, + void (*f)(Word), + Word arg1 ); +// 4(%esp) == stack (must be 16-byte aligned) +// 8(%esp) == retaddr +// 12(%esp) == f +// 16(%esp) == arg1 +asm( +".globl _call_on_new_stack_0_1\n" +"_call_on_new_stack_0_1:\n" +" movl %esp, %esi\n" // remember old stack pointer +" movl 4(%esi), %esp\n" // set new stack +" pushl $0\n" // align stack +" pushl $0\n" // align stack +" pushl $0\n" // align stack +" pushl 16(%esi)\n" // arg1 to stack +" pushl 8(%esi)\n" // retaddr to stack +" pushl 12(%esi)\n" // f to stack +" movl $0, %eax\n" // zero all GP regs +" movl $0, %ebx\n" +" movl $0, %ecx\n" +" movl $0, %edx\n" +" movl $0, %esi\n" +" movl $0, %edi\n" +" movl $0, %ebp\n" +" ret\n" // jump to f +" ud2\n" // should never get here +); + + +asm( +".globl _pthread_hijack_asm\n" +"_pthread_hijack_asm:\n" +" movl %esp,%ebp\n" +" push $0\n" // alignment pad +" push %ebp\n" // original sp +" push %esi\n" // flags +" push %edi\n" // stacksize +" push %edx\n" // func_arg +" push %ecx\n" // func +" push %ebx\n" // kport +" push %eax\n" // self +" push $0\n" // fake return address +" jmp _pthread_hijack\n" + ); + + + +void pthread_hijack(Addr self, Addr kport, Addr func, Addr func_arg, + Addr stacksize, Addr flags, Addr sp) +{ + vki_sigset_t blockall; + ThreadState *tst = (ThreadState *)func_arg; + VexGuestX86State *vex = &tst->arch.vex; + + // VG_(printf)("pthread_hijack pthread %p, machthread %p, func %p, arg %p, stack %p, flags %p, stack %p\n", self, kport, func, func_arg, stacksize, flags, sp); + + // Wait for parent thread's permission. + // The parent thread holds V's lock on our behalf. + semaphore_wait(tst->os_state.child_go); + + /* Start the thread with all signals blocked. VG_(scheduler) will + set the mask correctly when we finally get there. */ + VG_(sigfillset)(&blockall); + VG_(sigprocmask)(VKI_SIG_SETMASK, &blockall, NULL); + + // Set thread's registers + // Do this FIRST because some code below tries to collect a backtrace, + // which requires valid register data. + // DDD: need to do post_reg_write events here? + LibVEX_GuestX86_initialise(vex); + vex->guest_EIP = pthread_starter; + vex->guest_EAX = self; + vex->guest_EBX = kport; + vex->guest_ECX = func; + vex->guest_EDX = tst->os_state.func_arg; + vex->guest_EDI = stacksize; + vex->guest_ESI = flags; + vex->guest_ESP = sp; + + // Record thread's stack and Mach port and pthread struct + tst->os_state.pthread = self; + tst->os_state.lwpid = kport; + record_named_port(tst->tid, kport, MACH_PORT_RIGHT_SEND, "thread-%p"); + + if ((flags & 0x01000000) == 0) { + // kernel allocated stack - needs mapping + Addr stack = VG_PGROUNDUP(sp) - stacksize; + tst->client_stack_highest_word = stack+stacksize; + tst->client_stack_szB = stacksize; + + // pthread structure + ML_(notify_core_and_tool_of_mmap)( + stack+stacksize, pthread_structsize, + VKI_PROT_READ|VKI_PROT_WRITE, VKI_MAP_PRIVATE, -1, 0); + // stack contents + ML_(notify_core_and_tool_of_mmap)( + stack, stacksize, + VKI_PROT_READ|VKI_PROT_WRITE, VKI_MAP_PRIVATE, -1, 0); + // guard page + ML_(notify_core_and_tool_of_mmap)( + stack-VKI_PAGE_SIZE, VKI_PAGE_SIZE, + 0, VKI_MAP_PRIVATE, -1, 0); + } else { + // client allocated stack + find_stack_segment(tst->tid, sp); + } + VG_(am_do_sync_check)("after", "pthread_hijack", 0); + + // DDD: should this be here rather than in POST(sys_bsdthread_create)? + // But we don't have ptid here... + //VG_TRACK ( pre_thread_ll_create, ptid, tst->tid ); + + // Tell parent thread's POST(sys_bsdthread_create) that we're done + // initializing registers and mapping memory. + semaphore_signal(tst->os_state.child_done); + // LOCK IS GONE BELOW THIS POINT + + // Go! + call_on_new_stack_0_1(tst->os_state.valgrind_stack_init_SP, 0, + start_thread_NORETURN, (Word)tst); + + /*NOTREACHED*/ + vg_assert(0); +} + + + +asm( +".globl _wqthread_hijack_asm\n" +"_wqthread_hijack_asm:\n" +" movl %esp,%ebp\n" +" push $0\n" // alignment +" push $0\n" // alignment +" push %ebp\n" // original sp +" push %edi\n" // reuse +" push %edx\n" // workitem +" push %ecx\n" // stackaddr +" push %ebx\n" // kport +" push %eax\n" // self +" push $0\n" // fake return address +" jmp _wqthread_hijack\n" + ); + + +/* wqthread note: The kernel may create or destroy pthreads in the + wqthread pool at any time with no userspace interaction, + and wqthread_start may be entered at any time with no userspace + interaction. + To handle this in valgrind, we create and destroy a valgrind + thread for every work item. +*/ +void wqthread_hijack(Addr self, Addr kport, Addr stackaddr, Addr workitem, + Int reuse, Addr sp) +{ + ThreadState *tst; + VexGuestX86State *vex; + Addr stack; + SizeT stacksize; + vki_sigset_t blockall; + + /* When we enter here we hold no lock (!), so we better acquire it + pronto. Why do we hold no lock? Because (presumably) the only + way to get here is as a result of a SfMayBlock syscall + "workq_ops(WQOPS_THREAD_RETURN)", which will have dropped the + lock. At least that's clear for the 'reuse' case. The + non-reuse case? Dunno, perhaps it's a new thread the kernel + pulled out of a hat. In any case we still need to take a + lock. */ + VG_(acquire_BigLock_LL)("wqthread_hijack"); + + /* Start the thread with all signals blocked. VG_(scheduler) will + set the mask correctly when we finally get there. */ + VG_(sigfillset)(&blockall); + VG_(sigprocmask)(VKI_SIG_SETMASK, &blockall, NULL); + + if (reuse) { + // This thread already exists; we're merely re-entering + // after leaving via workq_ops(WQOPS_THREAD_RETURN). + // Don't allocate any V thread resources. + // Do reset thread registers. + ThreadId tid = VG_(lwpid_to_vgtid)(kport); + vg_assert(VG_(is_valid_tid)(tid)); + vg_assert(mach_thread_self() == kport); + + tst = VG_(get_ThreadState)(tid); + vex = &tst->arch.vex; + vg_assert(tst->os_state.pthread == self); + } + else { + // This is a new thread. + tst = VG_(get_ThreadState)(VG_(alloc_ThreadState)()); + vex = &tst->arch.vex; + allocstack(tst->tid); + LibVEX_GuestX86_initialise(vex); + } + + // Set thread's registers + // Do this FIRST because some code below tries to collect a backtrace, + // which requires valid register data. + vex->guest_EIP = wqthread_starter; + vex->guest_EAX = self; + vex->guest_EBX = kport; + vex->guest_ECX = stackaddr; + vex->guest_EDX = workitem; + vex->guest_EDI = reuse; + vex->guest_ESI = 0; + vex->guest_ESP = sp; + + stacksize = 512*1024; // wq stacks are always DEFAULT_STACK_SIZE + stack = VG_PGROUNDUP(sp) - stacksize; + + if (reuse) { + // Continue V's thread back in the scheduler. + // The client thread is of course in another location entirely. + + /* Drop the lock before going into + ML_(wqthread_continue_NORETURN). The latter will immediately + attempt to reacquire it in non-LL mode, which is a bit + wasteful but I don't think is harmful. A better solution + would be to not drop the lock but instead "upgrade" it from a + LL lock to a full lock, but that's too much like hard work + right now. */ + VG_(release_BigLock_LL)("wqthread_hijack(1)"); + ML_(wqthread_continue_NORETURN)(tst->tid); + } + else { + // Record thread's stack and Mach port and pthread struct + tst->os_state.pthread = self; + tst->os_state.lwpid = kport; + record_named_port(tst->tid, kport, MACH_PORT_RIGHT_SEND, "wqthread-%p"); + + // kernel allocated stack - needs mapping + tst->client_stack_highest_word = stack+stacksize; + tst->client_stack_szB = stacksize; + + // GrP fixme scheduler lock?! + + // pthread structure + ML_(notify_core_and_tool_of_mmap)( + stack+stacksize, pthread_structsize, + VKI_PROT_READ|VKI_PROT_WRITE, VKI_MAP_PRIVATE, -1, 0); + // stack contents + // GrP fixme uninitialized! + ML_(notify_core_and_tool_of_mmap)( + stack, stacksize, + VKI_PROT_READ|VKI_PROT_WRITE, VKI_MAP_PRIVATE, -1, 0); + // guard page + // GrP fixme ban_mem_stack! + ML_(notify_core_and_tool_of_mmap)( + stack-VKI_PAGE_SIZE, VKI_PAGE_SIZE, + 0, VKI_MAP_PRIVATE, -1, 0); + + VG_(am_do_sync_check)("after", "wqthread_hijack", 0); + + // Go! + /* Same comments as the 'release' in the then-clause. + start_thread_NORETURN calls run_thread_NORETURN calls + thread_wrapper which acquires the lock before continuing. + Let's hope nothing non-thread-local happens until that point. + + DDD: I think this is plain wrong .. if we get to + thread_wrapper not holding the lock, and someone has recycled + this thread slot in the meantime, we're hosed. Is that + possible, though? */ + VG_(release_BigLock_LL)("wqthread_hijack(2)"); + call_on_new_stack_0_1(tst->os_state.valgrind_stack_init_SP, 0, + start_thread_NORETURN, (Word)tst); + } + + /*NOTREACHED*/ + vg_assert(0); +} + +/*--------------------------------------------------------------------*/ +/*--- end syswrap-x86-darwin.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/m_trampoline.S b/coregrind/m_trampoline.S index d267172b7..e52076dd0 100644 --- a/coregrind/m_trampoline.S +++ b/coregrind/m_trampoline.S @@ -735,7 +735,307 @@ VG_(trampoline_stuff_end): # undef UD2_256 # undef UD2_1024 # undef UD2_PAGE + +/*---------------- x86-darwin ----------------*/ +#else +#if defined(VGP_x86_darwin) + + /* a leading page of unexecutable code */ +.fill 2048, 2, 0x0b0f /* `ud2` */ + +.globl VG_(trampoline_stuff_start) +VG_(trampoline_stuff_start): + +.globl VG_(x86_darwin_SUBST_FOR_sigreturn) +VG_(x86_darwin_SUBST_FOR_sigreturn): + /* XXX does this need to have any special form? (cf x86-linux + version) */ + movl $ __NR_DARWIN_FAKE_SIGRETURN, %eax + int $0x80 + ud2 + +.globl VG_(darwin_REDIR_FOR_strlen) +VG_(darwin_REDIR_FOR_strlen): + movl 4(%esp), %edx + movl %edx, %eax + jmp 1f +0: + incl %eax +1: + cmpb $0, (%eax) + jne 0b + subl %edx, %eax + ret + +.globl VG_(darwin_REDIR_FOR_strcat) +VG_(darwin_REDIR_FOR_strcat): + pushl %esi + movl 8(%esp), %esi + movl 12(%esp), %ecx + movl %esi, %edx + jmp 1f +0: + incl %edx +1: + cmpb $0, (%edx) + jne 0b +2: + movzbl (%ecx), %eax + incl %ecx + movb %al, (%edx) + incl %edx + testb %al, %al + jne 2b + movl %esi, %eax + popl %esi + ret + + +.globl VG_(darwin_REDIR_FOR_strcmp) +VG_(darwin_REDIR_FOR_strcmp): + movl 4(%esp), %edx + movl 8(%esp), %ecx + jmp 1f +0: + incl %edx + incl %ecx +1: + movzbl (%edx), %eax + testb %al, %al + je 2f + cmpb (%ecx), %al + je 0b +2: + movzbl (%ecx),%edx + movzbl %al,%eax + subl %edx, %eax + ret + + +.globl VG_(darwin_REDIR_FOR_strcpy) +VG_(darwin_REDIR_FOR_strcpy): + pushl %ebp + movl %esp, %ebp + pushl %esi + movl 8(%ebp), %esi + movl 12(%ebp), %ecx + movl %esi, %edx + jmp 1f +0: + incl %ecx + incl %edx +1: + movzbl (%ecx), %eax + testb %al, %al + movb %al, (%edx) + jne 0b + movl %esi, %eax + popl %esi + leave + ret + +.globl VG_(darwin_REDIR_FOR_strlcat) +VG_(darwin_REDIR_FOR_strlcat): + pushl %ebp + movl %esp, %ebp + pushl %edi + pushl %esi + subl $16, %esp + movl 8(%ebp), %esi + movl 16(%ebp), %ecx + movl %esi, %edx + leal (%ecx,%esi), %eax + jmp 1f +0: + incl %edx +1: + cmpl %edx, %eax + je 2f + cmpb $0, (%edx) + jne 0b +2: + movl %edx, %edi + subl %esi, %edi + movl %ecx, %esi + subl %edi, %esi + je 3f + movl 12(%ebp), %eax + jmp 6f +3: + movl 12(%ebp), %eax + movl %eax, (%esp) + call VG_(darwin_REDIR_FOR_strlen) + jmp 7f +4: + cmpl $1, %esi + je 5f + movb %cl, (%edx) + decl %esi + incl %edx +5: + incl %eax +6: + movzbl (%eax), %ecx + testb %cl, %cl + jne 4b + movb $0, (%edx) + subl 12(%ebp), %eax +7: + addl $16, %esp + leal (%edi,%eax), %eax + popl %esi + popl %edi + leave + ret + +.globl VG_(trampoline_stuff_end) +VG_(trampoline_stuff_end): + + /* a trailing page of unexecutable code */ +.fill 2048, 2, 0x0b0f /* `ud2` */ + + +/*---------------- amd64-darwin ----------------*/ +#else +#if defined(VGP_amd64_darwin) + + /* a leading page of unexecutable code */ +.fill 2048, 2, 0x0b0f /* `ud2` */ + +.globl VG_(trampoline_stuff_start) +VG_(trampoline_stuff_start): + +.globl VG_(darwin_REDIR_FOR_strlen) +VG_(darwin_REDIR_FOR_strlen): + movq %rdi, %rax + jmp 1f +0: + incq %rax +1: + cmpb $0, (%rax) + jne 0b + subq %rdi, %rax + ret + +.globl VG_(darwin_REDIR_FOR_strcat) +VG_(darwin_REDIR_FOR_strcat): + movq %rdi, %rdx + jmp 1f +0: + incq %rdx +1: + cmpb $0, (%rdx) + jne 0b +2: + movzbl (%rsi), %eax + incq %rsi + movb %al, (%rdx) + incq %rdx + testb %al, %al + jne 2b + movq %rdi, %rax + ret + + +.globl VG_(darwin_REDIR_FOR_strcmp) +VG_(darwin_REDIR_FOR_strcmp): + jmp 1f +0: + incq %rdi + incq %rsi +1: + movzbl (%rdi), %eax + testb %al, %al + je 2f + cmpb (%rsi), %al + je 0b +2: + movzbl (%rsi), %edx + movzbl %al, %eax + subl %edx, %eax + ret + +.globl VG_(darwin_REDIR_FOR_strcpy) +VG_(darwin_REDIR_FOR_strcpy): + pushq %rbp + movq %rdi, %rdx + movq %rsp, %rbp + jmp 1f +0: + incq %rsi + incq %rdx +1: + movzbl (%rsi), %eax + testb %al, %al + movb %al, (%rdx) + jne 0b + leave + movq %rdi, %rax + ret + +.globl VG_(darwin_REDIR_FOR_strlcat) +VG_(darwin_REDIR_FOR_strlcat): + pushq %rbp + leaq (%rdx,%rdi), %rax + movq %rdi, %rcx + movq %rsp, %rbp + pushq %rbx + subq $8, %rsp + jmp 1f +0: + incq %rcx +1: + cmpq %rcx, %rax + je 2f + cmpb $0, (%rcx) + jne 0b +2: + movq %rcx, %rbx + subq %rdi, %rbx + movq %rdx, %rdi + subq %rbx, %rdi + je 3f + movq %rsi, %rax + jmp 6f +3: + movq %rsi, %rdi + call VG_(darwin_REDIR_FOR_strlen) + jmp 7f +4: + cmpq $1, %rdi + je 5f + movb %dl, (%rcx) + decq %rdi + incq %rcx +5: + incq %rax +6: + movzbl (%rax), %edx + testb %dl, %dl + jne 4b + movb $0, (%rcx) + subq %rsi, %rax +7: + leaq (%rbx,%rax), %rax + addq $8, %rsp + popq %rbx + leave + ret + +.globl VG_(darwin_REDIR_FOR_arc4random) +VG_(darwin_REDIR_FOR_arc4random): + /* not very random, hope dyld won't mind */ + movq $0x76616c6772696e64, %rax + ret + +.globl VG_(trampoline_stuff_end) +VG_(trampoline_stuff_end): + + /* a trailing page of unexecutable code */ +.fill 2048, 2, 0x0b0f /* `ud2` */ + + /*---------------- unknown ----------------*/ #else # error Unknown platform @@ -746,6 +1046,8 @@ VG_(trampoline_stuff_end): #endif #endif #endif +#endif +#endif #if defined(VGO_linux) /* Let the linker know we don't need an executable stack */ diff --git a/coregrind/m_translate.c b/coregrind/m_translate.c index ea7ec468b..1e1f049d5 100644 --- a/coregrind/m_translate.c +++ b/coregrind/m_translate.c @@ -708,6 +708,14 @@ static Bool translations_allowable_from_seg ( NSegment const* seg ) static Bool self_check_required ( NSegment const* seg, ThreadId tid ) { +#if defined(VGO_darwin) + // GrP fixme hack - dyld i386 IMPORT gets rewritten + // to really do this correctly, we'd need to flush the + // translation cache whenever a segment became +WX + if (seg->hasX && seg->hasW) { + return True; + } +#endif switch (VG_(clo_smc_check)) { case Vg_SmcNone: return False; case Vg_SmcAll: return True; @@ -1417,6 +1425,9 @@ Bool VG_(translate) ( ThreadId tid, # if defined(VGP_amd64_linux) vex_abiinfo.guest_amd64_assume_fs_is_zero = True; # endif +# if defined(VGP_amd64_darwin) + vex_abiinfo.guest_amd64_assume_gs_is_0x60 = True; +# endif # if defined(VGP_ppc32_linux) vex_abiinfo.guest_ppc_zap_RZ_at_blr = False; vex_abiinfo.guest_ppc_zap_RZ_at_bl = NULL; diff --git a/coregrind/m_ume/elf.c b/coregrind/m_ume/elf.c index f7719605c..8dfbb1f05 100644 --- a/coregrind/m_ume/elf.c +++ b/coregrind/m_ume/elf.c @@ -44,7 +44,6 @@ #include "priv_ume.h" - #if defined(HAVE_ELF) /* --- !!! --- EXTERNAL HEADERS start --- !!! --- */ diff --git a/coregrind/m_ume/macho.c b/coregrind/m_ume/macho.c new file mode 100644 index 000000000..db5db4f9b --- /dev/null +++ b/coregrind/m_ume/macho.c @@ -0,0 +1,776 @@ + +/*--------------------------------------------------------------------*/ +/*--- User-mode execve() for Mach-O executables m_ume_macho.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2005-2009 Apple Inc. + Greg Parker gparker@apple.com + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "pub_core_basics.h" +#include "pub_core_vki.h" + +#include "pub_core_aspacemgr.h" // various mapping fns +#include "pub_core_debuglog.h" +#include "pub_core_libcassert.h" // VG_(exit), vg_assert +#include "pub_core_libcbase.h" // VG_(memcmp), etc +#include "pub_core_libcfile.h" // VG_(open) et al +#include "pub_core_libcprint.h" +#include "pub_core_libcproc.h" +#include "pub_core_machine.h" // VG_ELF_CLASS (XXX: which should be moved) +#include "pub_core_mallocfree.h" // VG_(malloc), VG_(free) +#include "pub_core_syscall.h" // VG_(strerror) +#include "pub_core_ume.h" // self + +#include "priv_ume.h" + + +#if defined(HAVE_MACHO) + +#include + +#include +#include +#include + +#if VG_WORDSIZE == 4 +#define MAGIC MH_MAGIC +#define MACH_HEADER mach_header +#define LC_SEGMENT_CMD LC_SEGMENT +#define SEGMENT_COMMAND segment_command +#define SECTION section +#else +#define MAGIC MH_MAGIC_64 +#define MACH_HEADER mach_header_64 +#define LC_SEGMENT_CMD LC_SEGMENT_64 +#define SEGMENT_COMMAND segment_command_64 +#define SECTION section_64 +#endif + + +static void print(const char *str) +{ + VG_(printf)("%s", str); +} + +static void check_mmap(SysRes res, Addr base, SizeT len) +{ + if (sr_isError(res)) { + VG_(printf)("valgrind: mmap(0x%llx, %lld) failed in UME.\n", + (ULong)base, (Long)len); + VG_(exit)(1); + } +} + + +static int +load_thin_file(int fd, vki_off_t offset, vki_off_t size, unsigned long filetype, + const char *filename, + vki_uint8_t **out_stack_start, vki_uint8_t **out_stack_end, + vki_uint8_t **out_text, vki_uint8_t **out_entry, vki_uint8_t **out_linker_entry); + +static int +load_fat_file(int fd, vki_off_t offset, vki_off_t size, unsigned long filetype, + const char *filename, + vki_uint8_t **out_stack_start, vki_uint8_t **out_stack_end, + vki_uint8_t **out_text, vki_uint8_t **out_entry, vki_uint8_t **out_linker_entry); + +static int +load_mach_file(int fd, vki_off_t offset, vki_off_t size, unsigned long filetype, + const char *filename, + vki_uint8_t **out_stack_start, vki_uint8_t **out_stack_end, + vki_uint8_t **out_text, vki_uint8_t **out_entry, vki_uint8_t **out_linker_entry); + + +/* Open and map a dylinker file. + Returns 0 on success, -1 on any failure. + filename must be an absolute path. + The dylinker's entry point is returned in *out_linker_entry. + */ +static int +open_dylinker(const char *filename, vki_uint8_t **out_linker_entry) +{ + struct vg_stat sb; + vki_size_t filesize; + SysRes res; + int fd; + int err; + + if (filename[0] != '/') { + print("bad executable (dylinker name is not an absolute path)\n"); + return -1; + } + + res = VG_(open)(filename, VKI_O_RDONLY, 0); + fd = sr_Res(res); + if (sr_isError(res)) { + print("couldn't open dylinker: "); + print(filename); + print("\n"); + return -1; + } + err = VG_(fstat)(fd, &sb); + if (err) { + print("couldn't stat dylinker: "); + print(filename); + print("\n"); + VG_(close)(fd); + return -1; + } + filesize = sb.size; + + err = load_mach_file(fd, 0, filesize, MH_DYLINKER, filename, + NULL, NULL, NULL, out_linker_entry, NULL); + if (err) { + print("...while loading dylinker: "); + print(filename); + print("\n"); + } + VG_(close)(fd); + return err; +} + + +/* + Process an LC_SEGMENT command, mapping it into memory if appropriate. + fd[offset..size) is a Mach-O thin file. + Returns 0 on success, -1 on any failure. + If this segment contains the executable's Mach headers, their + loaded address is returned in *text. + If this segment is a __UNIXSTACK, its start address is returned in + *stack_start. +*/ +static int +load_segment(int fd, vki_off_t offset, vki_off_t size, + vki_uint8_t **text, vki_uint8_t **stack_start, + struct SEGMENT_COMMAND *segcmd, const HChar *filename) +{ + SysRes res; + Addr addr; + vki_size_t filesize; // page-aligned + vki_size_t vmsize; // page-aligned + unsigned int prot; + + // GrP fixme mark __UNIXSTACK as SF_STACK + +#if VG_WORDSIZE == 8 + if (segcmd->vmaddr == 0 && 0 == VG_(strcmp)(segcmd->segname, SEG_PAGEZERO)) { + if (segcmd->vmsize != 0x100000000) { + print("bad executable (__PAGEZERO is not 4 GB)\n"); + return -1; + } + return 0; + } +#endif + + // Record the segment containing the Mach headers themselves + if (segcmd->fileoff == 0 && segcmd->filesize != 0) { + if (text) *text = (vki_uint8_t *)segcmd->vmaddr; + } + + // Record the __UNIXSTACK start + if (0 == VG_(strcmp)(segcmd->segname, SEG_UNIXSTACK)) { + if (stack_start) *stack_start = (vki_uint8_t *)segcmd->vmaddr; + } + + // Sanity-check the segment + if (segcmd->fileoff + segcmd->filesize > size) { + print("bad executable (invalid segment command)\n"); + return -1; + } + if (segcmd->vmsize == 0) { + return 0; // nothing to map - ok + } + + // Get desired memory protection + // GrP fixme need maxprot too + prot = (((segcmd->initprot & VM_PROT_READ) ? VKI_PROT_READ : 0) | + ((segcmd->initprot & VM_PROT_WRITE) ? VKI_PROT_WRITE : 0) | + ((segcmd->initprot & VM_PROT_EXECUTE) ? VKI_PROT_EXEC : 0)); + + // Map the segment + filesize = VG_PGROUNDUP(segcmd->filesize); + vmsize = VG_PGROUNDUP(segcmd->vmsize); + if (filesize > 0) { + addr = (Addr)segcmd->vmaddr; + res = VG_(am_mmap_named_file_fixed_client)(addr, filesize, prot, fd, + offset + segcmd->fileoff, + filename); + check_mmap(res, addr, filesize); + } + + // Zero-fill the remainder of the segment, if any + if (segcmd->filesize != filesize) { + // non-page-aligned part + // GrP fixme kernel doesn't do this? + //bzero(segcmd->filesize+(vki_uint8_t *)addr, filesize-segcmd->filesize); + } + if (filesize != vmsize) { + // page-aligned part + SizeT length = vmsize - filesize; + addr = (Addr)(filesize + segcmd->vmaddr); + res = VG_(am_mmap_anon_fixed_client)(addr, length, prot); + check_mmap(res, addr, length); + } + + return 0; +} + + +/* + Parse a LC_THREAD or LC_UNIXTHREAD command. + Return 0 on success, -1 on any failure. + The stack address is returned in *stack. If the executable requested + a non-default stack address, *customstack is set to TRUE. The thread's + entry point is returned in *entry. + The stack itself (if any) is not mapped. + Other custom register settings are silently ignored (GrP fixme). +*/ +static int +load_genericthread(vki_uint8_t **stack_end, + int *customstack, vki_uint8_t **entry, + struct thread_command *threadcmd) +{ + unsigned int flavor; + unsigned int count; + unsigned int *p; + unsigned int left; + + p = (unsigned int *)(threadcmd + 1); + left = (threadcmd->cmdsize - sizeof(struct thread_command)) / sizeof(*p); + + while (left > 0) { + if (left < 2) { + print("bad executable (invalid thread command)\n"); + return -1; + } + flavor = *p++; left--; + count = *p++; left--; + + if (left < count) { + print("bad executable (invalid thread command 2)\n"); + return -1; + } + +#if defined(VGA_x86) + if (flavor == i386_THREAD_STATE && count == i386_THREAD_STATE_COUNT) { + i386_thread_state_t *state = (i386_thread_state_t *)p; + if (entry) *entry = (vki_uint8_t *)state->__eip; + if (stack_end) *stack_end = (vki_uint8_t *)(state->__esp ? state->__esp : VKI_USRSTACK); + if (customstack) *customstack = state->__esp; + return 0; + } + +#elif defined(VGA_amd64) + if (flavor == x86_THREAD_STATE64 && count == x86_THREAD_STATE64_COUNT){ + x86_thread_state64_t *state = (x86_thread_state64_t *)p; + if (entry) *entry = (vki_uint8_t *)state->__rip; + if (stack_end) *stack_end = (vki_uint8_t *)(state->__rsp ? state->__rsp : VKI_USRSTACK64); + if (customstack) *customstack = state->__rsp; + return 0; + } + +#else +# error unknown platform +#endif + p += count; + left -= count; + } + + print("bad executable (no arch-compatible thread state)\n"); + return -1; +} + + +/* Returns the main stack size on this platform, + using getrlimit or a fixed size. + GrP fixme 64-bit? */ +static vki_size_t default_stack_size(void) +{ + struct vki_rlimit lim; + int err = VG_(getrlimit)(VKI_RLIMIT_STACK, &lim); + if (err) return 8*1024*1024; // 8 MB + else return lim.rlim_cur; +} + + +/* + Processes a LC_UNIXTHREAD command. + Returns 0 on success, -1 on any failure. + The stack is mapped in and returned in *out_stack. + The thread's entry point is returned in *out_entry. +*/ +static int +load_unixthread(vki_uint8_t **out_stack_start, vki_uint8_t **out_stack_end, + vki_uint8_t **out_entry, struct thread_command *threadcmd) +{ + int err; + vki_uint8_t *stack_end; + int customstack; + + err = load_genericthread(&stack_end, &customstack, out_entry, threadcmd); + if (err) return -1; + + if (!stack_end) { + print("bad executable (no thread stack)\n"); + return -1; + } + + if (!customstack) { + // Map the stack + vki_size_t stacksize = VG_PGROUNDUP(default_stack_size()); + vm_address_t stackbase = VG_PGROUNDDN(stack_end-stacksize); + SysRes res; + + res = VG_(am_mmap_anon_fixed_client)(stackbase, stacksize, VKI_PROT_READ|VKI_PROT_WRITE|VKI_PROT_EXEC); + check_mmap(res, stackbase, stacksize); + if (out_stack_start) *out_stack_start = (vki_uint8_t *)stackbase; + } else { + // custom stack - mapped via __UNIXTHREAD segment + } + + if (out_stack_end) *out_stack_end = stack_end; + + return 0; +} + + +/* + Processes an LC_LOAD_DYLINKER command. + Returns 0 on success, -1 on any error. + The linker itself is mapped into memory. + The linker's entry point is returned in *linker_entry. +*/ +static int +load_dylinker(vki_uint8_t **linker_entry, struct dylinker_command *dycmd) +{ + const char *name; + + if (dycmd->name.offset >= dycmd->cmdsize) { + print("bad executable (invalid dylinker command)\n"); + return -1; + } + + name = dycmd->name.offset + (char *)dycmd; + + // GrP fixme assumes name is terminated somewhere + return open_dylinker(name, linker_entry); +} + + +/* + Process an LC_THREAD command. + Returns 0 on success, -1 on any failure. + The thread's entry point is returned in *out_entry. +*/ +static int +load_thread(vki_uint8_t **out_entry, struct thread_command *threadcmd) +{ + int customstack; + int err; + + err = load_genericthread(NULL, &customstack, out_entry, threadcmd); + if (err) return -1; + if (customstack) { + print("bad executable (stackless thread has stack)\n"); + return -1; + } + return 0; +} + + +/* + Loads a Mach-O executable into memory, along with any threads, + stacks, and dylinker. + Returns 0 on success, -1 on any failure. + fd[offset..offset+size) is a Mach-O thin file. + filetype is MH_EXECUTE or MH_DYLINKER. + The mapped but empty stack is returned in *out_stack. + The executable's Mach headers are returned in *out_text. + The executable's entry point is returned in *out_entry. + The dylinker's entry point (if any) is returned in *out_linker_entry. + GrP fixme need to return whether dylinker was found - stack layout is different +*/ +static int +load_thin_file(int fd, vki_off_t offset, vki_off_t size, unsigned long filetype, + const char *filename, + vki_uint8_t **out_stack_start, vki_uint8_t **out_stack_end, + vki_uint8_t **out_text, vki_uint8_t **out_entry, vki_uint8_t **out_linker_entry) +{ + struct MACH_HEADER mh; + vki_uint8_t *headers; + vki_uint8_t *headers_end; + struct load_command *lc; + struct load_command *lcend; + struct SEGMENT_COMMAND *segcmd; + struct thread_command *threadcmd; + struct dylinker_command *dycmd; + int err; + SysRes res; + vki_size_t len; + + vki_uint8_t *stack_start = NULL; // allocated thread stack (hot end) + vki_uint8_t *stack_end = NULL; // allocated thread stack (cold end) + vki_uint8_t *entry = NULL; // static entry point + vki_uint8_t *text = NULL; // start of text segment (i.e. the mach headers) + vki_uint8_t *linker_entry = NULL; // dylinker entry point + + // Read Mach-O header + if (sizeof(mh) > size) { + print("bad executable (no Mach-O header)\n"); + } + res = VG_(pread)(fd, &mh, sizeof(mh), offset); + if (sr_isError(res) || sr_Res(res) != sizeof(mh)) { + print("bad executable (no Mach-O header)\n"); + return -1; + } + + + // Sanity-check the header itself + if (mh.magic != MAGIC) { + print("bad executable (no Mach-O magic)\n"); + return -1; + } + + if (mh.filetype != filetype) { + // expecting MH_EXECUTE or MH_DYLINKER + print("bad executable (wrong file type)\n"); + return -1; + } + + + // Map all headers into memory + len = sizeof(mh) + mh.sizeofcmds; + if (len > size) { + print("bad executable (missing load commands)\n"); + return -1; + } + + headers = VG_(malloc)("ume.macho.headers", len); + res = VG_(pread)(fd, headers, len, offset); + if (sr_isError(res)) { + print("couldn't read load commands from executable\n"); + return -1; + } + headers_end = headers + size; + + + // Map some segments into client memory: + // LC_SEGMENT (text, data, etc) + // UNIXSTACK (stack) + // LOAD_DYLINKER (dyld) + lcend = (struct load_command *)(headers + mh.sizeofcmds + sizeof(mh)); + for (lc = (struct load_command *)(headers + sizeof(mh)); + lc < lcend; + lc = (struct load_command *)(lc->cmdsize + (vki_uint8_t *)lc)) + { + if ((vki_uint8_t *)lc < headers || + lc->cmdsize+(vki_uint8_t *)lc > headers_end) { + print("bad executable (invalid load commands)\n"); + return -1; + } + + switch (lc->cmd) { + case LC_SEGMENT_CMD: + if (lc->cmdsize < sizeof(struct SEGMENT_COMMAND)) { + print("bad executable (invalid load commands)\n"); + return -1; + } + segcmd = (struct SEGMENT_COMMAND *)lc; + err = load_segment(fd, offset, size, &text, &stack_start, + segcmd, filename); + if (err) return -1; + + break; + + case LC_UNIXTHREAD: + if (stack_end || entry) { + print("bad executable (multiple thread commands)\n"); + return -1; + } + if (lc->cmdsize < sizeof(struct thread_command)) { + print("bad executable (invalid load commands)\n"); + return -1; + } + threadcmd = (struct thread_command *)lc; + err = load_unixthread(&stack_start, &stack_end, &entry, threadcmd); + if (err) return -1; + break; + + case LC_LOAD_DYLINKER: + if (filetype == MH_DYLINKER) { + print("bad executable (dylinker needs a dylinker)\n"); + return -1; + } + if (linker_entry) { + print("bad executable (multiple dylinker commands)\n"); + } + if (lc->cmdsize < sizeof(struct dylinker_command)) { + print("bad executable (invalid load commands)\n"); + return -1; + } + dycmd = (struct dylinker_command *)lc; + err = load_dylinker(&linker_entry, dycmd); + if (err) return -1; + break; + + case LC_THREAD: + if (filetype == MH_EXECUTE) { + print("bad executable (stackless thread)\n"); + return -1; + } + if (stack_end || entry) { + print("bad executable (multiple thread commands)\n"); + return -1; + } + if (lc->cmdsize < sizeof(struct thread_command)) { + print("bad executable (invalid load commands)\n"); + return -1; + } + threadcmd = (struct thread_command *)lc; + err = load_thread(&entry, threadcmd); + if (err) return -1; + break; + + default: + break; + } + } + + + // Done with the headers + VG_(free)(headers); + + if (filetype == MH_EXECUTE) { + // Verify the necessary pieces for an executable: + // a stack + // a text segment + // an entry point (static or linker) + if (!stack_end || !stack_start) { + print("bad executable (no stack)\n"); + return -1; + } + if (!text) { + print("bad executable (no text segment)\n"); + return -1; + } + if (!entry && !linker_entry) { + print("bad executable (no entry point)\n"); + return -1; + } + } + else if (filetype == MH_DYLINKER) { + // Verify the necessary pieces for a dylinker: + // an entry point + if (!entry) { + print("bad executable (no entry point)\n"); + return -1; + } + } + + if (out_stack_start) *out_stack_start = stack_start; + if (out_stack_end) *out_stack_end = stack_end; + if (out_text) *out_text = text; + if (out_entry) *out_entry = entry; + if (out_linker_entry) *out_linker_entry = linker_entry; + + return 0; +} + + +/* + Load a fat Mach-O executable. +*/ +static int +load_fat_file(int fd, vki_off_t offset, vki_off_t size, unsigned long filetype, + const char *filename, + vki_uint8_t **out_stack_start, vki_uint8_t **out_stack_end, + vki_uint8_t **out_text, vki_uint8_t **out_entry, vki_uint8_t **out_linker_entry) +{ + struct fat_header fh; + vki_off_t arch_offset; + int i; + cpu_type_t good_arch; + SysRes res; + +#if defined(VGA_ppc32) + good_arch = CPU_TYPE_POWERPC; +#elif defined(VGA_ppc64) + good_arch = CPU_TYPE_POWERPC64; +#elif defined(VGA_x86) + good_arch = CPU_TYPE_I386; +#elif defined(VGA_amd64) + good_arch = CPU_TYPE_X86_64; +#else +# error unknown architecture +#endif + + // Read fat header + // All fat contents are BIG-ENDIAN + if (size < sizeof(fh)) { + print("bad executable (bad fat header)\n"); + return -1; + } + res = VG_(pread)(fd, &fh, sizeof(fh), offset); + if (sr_isError(res) || sr_Res(res) != sizeof(fh)) { + print("bad executable (bad fat header)\n"); + return -1; + } + + // Scan arch headers looking for a good one + arch_offset = offset + sizeof(fh); + fh.nfat_arch = VG_(ntohl)(fh.nfat_arch); + for (i = 0; i < fh.nfat_arch; i++) { + struct fat_arch arch; + if (arch_offset + sizeof(arch) > size) { + print("bad executable (corrupt fat archs)\n"); + return -1; + } + + res = VG_(pread)(fd, &arch, sizeof(arch), arch_offset); + arch_offset += sizeof(arch); + if (sr_isError(res) || sr_Res(res) != sizeof(arch)) { + VG_(printf)("bad executable (corrupt fat arch) %x %llu\n", + arch.cputype, (ULong)arch_offset); + return -1; + } + + arch.cputype = VG_(ntohl)(arch.cputype); + arch.cpusubtype = VG_(ntohl)(arch.cpusubtype); + arch.offset = VG_(ntohl)(arch.offset); + arch.size = VG_(ntohl)(arch.size); + arch.align = VG_(ntohl)(arch.align); + if (arch.cputype == good_arch) { + // use this arch + if (arch.offset > size || arch.offset + arch.size > size) { + print("bad executable (corrupt fat arch 2)\n"); + return -1; + } + return load_mach_file(fd, offset+arch.offset, arch.size, filetype, + filename, out_stack_start, out_stack_end, + out_text, out_entry, out_linker_entry); + } + } + + print("bad executable (can't run on this machine)\n"); + return -1; +} + +/* + Load a Mach-O executable or dylinker. + The file may be fat or thin. +*/ +static int +load_mach_file(int fd, vki_off_t offset, vki_off_t size, unsigned long filetype, + const char *filename, + vki_uint8_t **out_stack_start, vki_uint8_t **out_stack_end, + vki_uint8_t **out_text, vki_uint8_t **out_entry, vki_uint8_t **out_linker_entry) +{ + vki_uint32_t magic; + SysRes res; + + if (size < sizeof(magic)) { + print("bad executable (no Mach-O magic)\n"); + return -1; + } + res = VG_(pread)(fd, &magic, sizeof(magic), offset); + if (sr_isError(res) || sr_Res(res) != sizeof(magic)) { + print("bad executable (no Mach-O magic)\n"); + return -1; + } + + if (magic == MAGIC) { + // thin + return load_thin_file(fd, offset, size, filetype, filename, + out_stack_start, out_stack_end, + out_text, out_entry, out_linker_entry); + } else if (magic == VG_(htonl)(FAT_MAGIC)) { + // fat + return load_fat_file(fd, offset, size, filetype, filename, + out_stack_start, out_stack_end, + out_text, out_entry, out_linker_entry); + } else { + // huh? + print("bad executable (bad Mach-O magic)\n"); + return -1; + } +} + + +Bool VG_(match_macho)(Char *hdr, Int len) +{ + vki_uint32_t *magic = (vki_uint32_t *)hdr; + + // GrP fixme check more carefully for matching fat arch? + + return (len >= VKI_PAGE_SIZE && + (*magic == MAGIC || *magic == VG_(ntohl)(FAT_MAGIC))) + ? True : False; +} + + +Int VG_(load_macho)(Int fd, const HChar *name, ExeInfo *info) +{ + int err; + struct vg_stat sb; + vki_uint8_t *stack_start; + vki_uint8_t *stack_end; + vki_uint8_t *text; + vki_uint8_t *entry; + vki_uint8_t *linker_entry; + + err = VG_(fstat)(fd, &sb); + if (err) { + print("couldn't stat executable\n"); + return VKI_ENOEXEC; + } + + err = load_mach_file(fd, 0, sb.size, MH_EXECUTE, name, + &stack_start, &stack_end, + &text, &entry, &linker_entry); + if (err) return VKI_ENOEXEC; + + // GrP fixme exe_base + // GrP fixme exe_end + info->entry = (Addr)entry; + info->init_ip = (Addr)(linker_entry ? linker_entry : entry); + info->brkbase = 0xffffffff; // GrP fixme hack + info->init_toc = 0; // GrP fixme unused + + info->stack_start = (Addr)stack_start; + info->stack_end = (Addr)stack_end; + info->text = (Addr)text; + info->dynamic = linker_entry ? True : False; + + info->executable_path = VG_(strdup)("ume.macho.executable_path", name); + + return 0; +} + +#endif // defined(HAVE_MACHO) + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ + diff --git a/coregrind/m_ume/main.c b/coregrind/m_ume/main.c index a32bec7ee..1673fc980 100644 --- a/coregrind/m_ume/main.c +++ b/coregrind/m_ume/main.c @@ -58,6 +58,9 @@ static ExeHandler exe_handlers[] = { # if defined(HAVE_SCRIPT) { "script", VG_(match_script), VG_(load_script) }, # endif +# if defined(HAVE_MACHO) + { "Mach-O", VG_(match_macho), VG_(load_macho) }, +# endif }; #define EXE_HANDLER_COUNT (sizeof(exe_handlers)/sizeof(exe_handlers[0])) diff --git a/coregrind/m_ume/priv_ume.h b/coregrind/m_ume/priv_ume.h index ce5f09046..ca3f95bc1 100644 --- a/coregrind/m_ume/priv_ume.h +++ b/coregrind/m_ume/priv_ume.h @@ -42,6 +42,11 @@ extern Bool VG_(match_script) ( Char *hdr, Int len ); extern Int VG_(load_script) ( Int fd, const HChar *name, ExeInfo *info ); #endif +#if defined(HAVE_MACHO) +extern Bool VG_(match_macho) ( Char *hdr, Int len ); +extern Int VG_(load_macho) ( Int fd, const HChar *name, ExeInfo *info ); +#endif + #endif /* __PRIV_UME_H */ /*--------------------------------------------------------------------*/ diff --git a/coregrind/m_vki.c b/coregrind/m_vki.c index 25beafbf6..9a4c3ef1d 100644 --- a/coregrind/m_vki.c +++ b/coregrind/m_vki.c @@ -77,6 +77,12 @@ void VG_(vki_do_initial_consistency_checks) ( void ) # if defined(VGO_linux) || defined(VGO_aix5) /* nothing to check */ +# elif defined(VGP_x86_darwin) || defined(VGP_amd64_darwin) + vg_assert(_VKI_NSIG == NSIG); + vg_assert(_VKI_NSIG == 32); + vg_assert(_VKI_NSIG_WORDS == 1); + vg_assert(sizeof(sigset_t) /* defined by Darwin */ + == sizeof(vki_sigset_t) /* what we actually use */); # else # error "Unknown plat" # endif @@ -87,6 +93,39 @@ void VG_(vki_do_initial_consistency_checks) ( void ) /* the toK- and fromK- forms are identical */ vg_assert( sizeof(vki_sigaction_toK_t) == sizeof(vki_sigaction_fromK_t) ); +# elif defined(VGO_darwin) + /* the toK- and fromK- forms differ by one function-pointer field + (sa_tramp) */ + vg_assert( sizeof(vki_sigaction_toK_t) + == sizeof(vki_sigaction_fromK_t) + sizeof(void*) ); + + vg_assert(sizeof(struct sigaction) == sizeof(vki_sigaction_fromK_t)); + vg_assert(sizeof(struct __sigaction) == sizeof(vki_sigaction_toK_t)); + { struct __sigaction t1; + vki_sigaction_toK_t t2; + struct sigaction f1; + vki_sigaction_fromK_t f2; + vg_assert(sizeof(t1.sa_handler) == sizeof(t2.ksa_handler)); + vg_assert(sizeof(t1.sa_tramp) == sizeof(t2.sa_tramp)); + vg_assert(sizeof(t1.sa_mask) == sizeof(t2.sa_mask)); + vg_assert(sizeof(t1.sa_flags) == sizeof(t2.sa_flags)); + vg_assert(sizeof(f1.sa_handler) == sizeof(f2.ksa_handler)); + vg_assert(sizeof(f1.sa_mask) == sizeof(f2.sa_mask)); + vg_assert(sizeof(f1.sa_flags) == sizeof(f2.sa_flags)); +# if 0 + vg_assert(offsetof(t1,sa_handler) == offsetof(t2.ksa_handler)); + vg_assert(offsetof(t1.sa_tramp) == offsetof(t2.sa_tramp)); + vg_assert(offsetof(t1.sa_mask) == offsetof(t2.sa_mask)); + vg_assert(offsetof(t1.sa_flags) == offsetof(t2.sa_flags)); + vg_assert(offsetof(f1.sa_handler) == offsetof(f2.ksa_handler)); + vg_assert(offsetof(f1.sa_mask) == offsetof(f2.sa_mask)); + vg_assert(offsetof(f1.sa_flags) == offsetof(f2.sa_flags)); +# endif + } + /* also .. */ + /* VKI_SET_SIGMASK is hardwired into syscall-x86-darwin.S and + syscall-amd64-darwin.S */ + vg_assert(VKI_SIG_SETMASK == 3); # else # error "Unknown OS" diff --git a/coregrind/pub_core_aspacemgr.h b/coregrind/pub_core_aspacemgr.h index c47120fc7..372b9237a 100644 --- a/coregrind/pub_core_aspacemgr.h +++ b/coregrind/pub_core_aspacemgr.h @@ -120,7 +120,6 @@ extern void VG_(am_show_nsegments) ( Int logLevel, HChar* who ); extern Bool VG_(am_do_sync_check) ( const HChar* fn, const HChar* file, Int line ); - //-------------------------------------------------------------- // Functions pertaining to the central query-notify mechanism // used to handle mmap/munmap/mprotect resulting from client @@ -250,6 +249,8 @@ extern Bool VG_(am_aix5_sbrk_allowed); segment array accordingly. */ extern SysRes VG_(am_mmap_file_fixed_client) ( Addr start, SizeT length, UInt prot, Int fd, Off64T offset ); +extern SysRes VG_(am_mmap_named_file_fixed_client) + ( Addr start, SizeT length, UInt prot, Int fd, Off64T offset, const HChar *name ); /* Map anonymously at a fixed address for the client, and update the segment array accordingly. */ @@ -400,6 +401,22 @@ extern VgStack* VG_(am_alloc_VgStack)( /*OUT*/Addr* initial_sp ); extern Int VG_(am_get_VgStack_unused_szB)( VgStack* stack ); +// DDD: this is ugly +#if defined(VGO_darwin) +typedef + struct { + Bool is_added; // Added or removed seg? + Addr start; + SizeT end; + UInt prot; // Not used for removed segs. + Off64T offset; // Not used for removed segs. + } + ChangedSeg; + +extern void VG_(get_changed_segments)( + const HChar* when, const HChar* where, /*OUT*/ChangedSeg* css, + Int css_size, /*OUT*/Int* css_used); +#endif #endif // __PUB_CORE_ASPACEMGR_H diff --git a/coregrind/pub_core_debuginfo.h b/coregrind/pub_core_debuginfo.h index 6f1c94430..b947af1e3 100644 --- a/coregrind/pub_core_debuginfo.h +++ b/coregrind/pub_core_debuginfo.h @@ -56,7 +56,7 @@ extern void VG_(di_initialise) ( void ); in later queries to m_debuginfo. In this case the handle value will be one or above. If the returned value is zero, no debug info was read. */ -#if defined(VGO_linux) +#if defined(VGO_linux) || defined(VGO_darwin) extern ULong VG_(di_notify_mmap)( Addr a, Bool allow_SkFileV ); extern void VG_(di_notify_munmap)( Addr a, SizeT len ); @@ -70,6 +70,7 @@ extern void VG_(di_notify_pdb_debuginfo)( Int fd, Addr avma, #endif #if defined(VGO_aix5) +// GrP fixme use this instead for darwin? /* AIX5: Very similar, except packaged more neatly. The supplied parameters describe a code segment and its associated data segment, that have recently been mapped in -- so we need to read debug info diff --git a/coregrind/pub_core_initimg.h b/coregrind/pub_core_initimg.h index 9e9416b71..4c4e09d01 100644 --- a/coregrind/pub_core_initimg.h +++ b/coregrind/pub_core_initimg.h @@ -60,7 +60,6 @@ IIFinaliseImageInfo VG_(ii_create_image)( IICreateImageInfo ); extern void VG_(ii_finalise_image)( IIFinaliseImageInfo ); - /* Note that both IICreateImageInfo and IIFinaliseImageInfo are OS-specific. We now go on to give instantiations of them for supported OSes. */ @@ -169,6 +168,37 @@ struct _IIFinaliseImageInfo { UInt adler32_exp; }; + +/* ------------------------- Darwin ------------------------- */ + +#elif defined(VGO_darwin) + +struct _IICreateImageInfo { + /* ------ Mandatory fields ------ */ + HChar* toolname; + Addr sp_at_startup; + Addr clstack_top; + /* ------ Per-OS fields ------ */ + HChar** argv; + HChar** envp; + Addr entry; /* &_start */ + Addr init_ip; /* &__dyld_start, or copy of entry */ + Addr stack_start; /* stack segment hot */ + Addr stack_end; /* stack segment cold */ + Addr text; /* executable's Mach header */ + Bool dynamic; /* False iff executable is static */ + HChar* executable_path; /* path passed to execve() */ +}; + +struct _IIFinaliseImageInfo { + /* ------ Mandatory fields ------ */ + SizeT clstack_max_size; + Addr initial_client_SP; + /* ------ Per-OS fields ------ */ + Addr initial_client_IP; +}; + + #else # error "Unknown OS" #endif diff --git a/coregrind/pub_core_mach.h b/coregrind/pub_core_mach.h new file mode 100644 index 000000000..719df440d --- /dev/null +++ b/coregrind/pub_core_mach.h @@ -0,0 +1,48 @@ + +/*--------------------------------------------------------------------*/ +/*--- Mach kernel interface module. pub_core_mach.h ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2005 Apple Inc. + Greg Parker gparker@apple.com + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#ifndef __PUB_CORE_MACH_H +#define __PUB_CORE_MACH_H + +//-------------------------------------------------------------------- +// PURPOSE: This module contains the Mach kernel interface, +// for operating systems like Darwin / Mac OS X that use it. +//-------------------------------------------------------------------- + +#if defined(VGO_darwin) +// Call this early in Valgrind's main(). It depends on nothing. +extern void VG_(mach_init)(void); +#endif + +#endif // __PUB_CORE_MACH_H + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/coregrind/pub_core_machine.h b/coregrind/pub_core_machine.h index 6b6734b0e..be47cdc6f 100644 --- a/coregrind/pub_core_machine.h +++ b/coregrind/pub_core_machine.h @@ -65,6 +65,11 @@ # undef VG_ELF_MACHINE # undef VG_ELF_CLASS # define VG_PLAT_USES_PPCTOC 1 +#elif defined(VGO_darwin) +# undef VG_ELF_DATA2XXX +# undef VG_ELF_MACHINE +# undef VG_ELF_CLASS +# undef VG_PLAT_USES_PPCTOC #else # error Unknown platform #endif diff --git a/coregrind/pub_core_mallocfree.h b/coregrind/pub_core_mallocfree.h index 5bf2088a7..8229ec86b 100644 --- a/coregrind/pub_core_mallocfree.h +++ b/coregrind/pub_core_mallocfree.h @@ -73,15 +73,18 @@ typedef Int ArenaId; defined(VGP_ppc32_linux) || \ defined(VGP_ppc32_aix5) # define VG_MIN_MALLOC_SZB 8 +// Nb: We always use 16 bytes for Darwin, even on 32-bits, so it can be used +// for any AltiVec- or SSE-related type. This matches the Darwin libc. #elif defined(VGP_amd64_linux) || \ defined(VGP_ppc64_linux) || \ - defined(VGP_ppc64_aix5) + defined(VGP_ppc64_aix5) || \ + defined(VGP_x86_darwin) || \ + defined(VGP_amd64_darwin) # define VG_MIN_MALLOC_SZB 16 #else # error Unknown platform #endif - /* This struct definition MUST match the system one. */ /* SVID2/XPG mallinfo structure */ struct vg_mallinfo { diff --git a/coregrind/pub_core_options.h b/coregrind/pub_core_options.h index 486cf94fb..73f4a630b 100644 --- a/coregrind/pub_core_options.h +++ b/coregrind/pub_core_options.h @@ -182,6 +182,10 @@ extern VgSmc VG_(clo_smc_check); so they can be properly handled by m_syswrap. */ extern HChar* VG_(clo_kernel_variant); +/* Darwin-specific: automatically run /usr/bin/dsymutil to update + .dSYM directories as necessary? */ +extern Bool VG_(clo_auto_run_dsymutil); + /* --------- Functions --------- */ /* Call this if the executable is missing. This function prints an diff --git a/coregrind/pub_core_scheduler.h b/coregrind/pub_core_scheduler.h index 029472b70..b295a13ed 100644 --- a/coregrind/pub_core_scheduler.h +++ b/coregrind/pub_core_scheduler.h @@ -59,6 +59,11 @@ extern void VG_(nuke_all_threads_except) ( ThreadId me, thread. */ extern void VG_(acquire_BigLock) ( ThreadId tid, HChar* who ); +/* Simple version, which simply acquires the lock, but does not mess + with the guest state in the same way as the non _LL version + does. */ +extern void VG_(acquire_BigLock_LL) ( HChar* who ); + /* Set a thread into a sleeping state. Before the call, the thread must be runnable, and holding the CPU lock. When this call returns, the thread will be set to the specified sleeping state, @@ -67,9 +72,14 @@ extern void VG_(acquire_BigLock) ( ThreadId tid, HChar* who ); caller must be careful not to touch any shared state. It is also the caller's responsibility to actually block until the thread is ready to run again. */ -extern void VG_(release_BigLock) ( ThreadId tid, ThreadStatus state, HChar* who ); +extern void VG_(release_BigLock) ( ThreadId tid, + ThreadStatus state, HChar* who ); -/* Yield the CPU for a while */ +/* Matching function to acquire_BigLock_LL. */ +extern void VG_(release_BigLock_LL) ( HChar* who ); + +/* Yield the CPU for a while. Drops/acquires the lock using the + normal (non _LL) functions. */ extern void VG_(vg_yield)(void); // The scheduler. diff --git a/coregrind/pub_core_syscall.h b/coregrind/pub_core_syscall.h index b4fd04e81..aa1d68dc7 100644 --- a/coregrind/pub_core_syscall.h +++ b/coregrind/pub_core_syscall.h @@ -75,6 +75,10 @@ extern SysRes VG_(mk_SysRes_ppc32_linux) ( UInt val, UInt cr0so ); extern SysRes VG_(mk_SysRes_ppc64_linux) ( ULong val, ULong cr0so ); extern SysRes VG_(mk_SysRes_ppc32_aix5) ( UInt val, UInt err ); extern SysRes VG_(mk_SysRes_ppc64_aix5) ( ULong val, ULong err ); +extern SysRes VG_(mk_SysRes_x86_darwin) ( UChar scclass, Bool isErr, + UInt wHI, UInt wLO ); +extern SysRes VG_(mk_SysRes_amd64_darwin)( UChar scclass, Bool isErr, + ULong wHI, ULong wLO ); extern SysRes VG_(mk_SysRes_Error) ( UWord val ); extern SysRes VG_(mk_SysRes_Success) ( UWord val ); diff --git a/coregrind/pub_core_syswrap.h b/coregrind/pub_core_syswrap.h index 89f0d8cbb..43428f6b5 100644 --- a/coregrind/pub_core_syswrap.h +++ b/coregrind/pub_core_syswrap.h @@ -40,7 +40,7 @@ // as if the thread had been set up by clone() extern void VG_(main_thread_wrapper_NORETURN)(ThreadId tid); -extern void VG_(client_syscall) ( ThreadId tid ); +extern void VG_(client_syscall) ( ThreadId tid, UInt trc ); extern void VG_(post_syscall) ( ThreadId tid ); diff --git a/coregrind/pub_core_threadstate.h b/coregrind/pub_core_threadstate.h index e7bde55c5..51b4adf94 100644 --- a/coregrind/pub_core_threadstate.h +++ b/coregrind/pub_core_threadstate.h @@ -87,6 +87,9 @@ typedef # error Unknown architecture #endif +/* Forward declarations */ +struct SyscallStatus; +struct SyscallArgs; /* Architecture-specific thread state */ typedef @@ -120,7 +123,7 @@ typedef typedef struct { /* who we are */ - Int lwpid; // PID of kernel task + Int lwpid; // PID of kernel task (Darwin: Mach thread) Int threadgroup; // thread group id ThreadId parent; // parent tid (if any) @@ -146,6 +149,135 @@ typedef cancel_progress; /* Initial state is False, False, Canc_Normal. */ # endif + +# if defined(VGO_darwin) + // Mach trap POST handler as chosen by PRE + void (*post_mach_trap_fn)(ThreadId tid, + struct SyscallArgs *, struct SyscallStatus *); + + // This thread's pthread + Addr pthread; + + // Argument passed when thread started + Addr func_arg; + + // Synchronization between child thread and parent thread's POST wrapper + semaphore_t child_go; + semaphore_t child_done; + + // Workqueue re-entry + // (setjmp in PRE(workq_ops), longjmp in wqthread_hijack) + // DDD: JRS fixme: this comment is no longer correct; wq_jmpbuf is + // never used, and there is no such setjmp or longjmp pair. + // I guess we could leave wq_jmpbuf_valid in place though, since + // it does allow for an assertion in ML_(wqthread_continue_NORETURN). + Bool wq_jmpbuf_valid; + //jmp_buf wq_jmpbuf; + + // Values saved from transient Mach RPC messages + Addr remote_port; // destination for original message + Int msgh_id; // outgoing message id + union { + struct { + Addr port; + } mach_port; + struct { + Int right; + } mach_port_allocate; + struct { + Addr port; + Int right; + Int delta; + } mach_port_mod_refs; + struct { + Addr task; + Addr name; + Int disposition; + } mach_port_insert_right; + struct { + Addr size; + int flags; + } vm_allocate; + struct { + Addr address; + Addr size; + } vm_deallocate; + struct { + Addr src; + Addr dst; + Addr size; + } vm_copy; + struct { + Addr address; + Addr size; + int set_maximum; + UWord new_protection; + } vm_protect; + struct { + Addr addr; + SizeT size; + } vm_read; + struct { + ULong addr; + ULong size; + } mach_vm_read; + struct { + Addr addr; + SizeT size; + Addr data; + } vm_read_overwrite; + struct { + Addr size; + int copy; + UWord protection; + } vm_map; + struct { + Addr size; + } vm_remap; + struct { + ULong size; + int flags; + } mach_vm_allocate; + struct { + ULong address; + ULong size; + } mach_vm_deallocate; + struct { + ULong address; + ULong size; + int set_maximum; + unsigned int new_protection; + } mach_vm_protect; + struct { + ULong size; + int copy; + UWord protection; + } mach_vm_map; + struct { + Addr thread; + UWord flavor; + } thread_get_state; + struct { + Addr address; + } io_connect_unmap_memory; + struct { + int which_port; + } task_get_special_port; + struct { + char *service_name; + } bootstrap_look_up; + struct { + vki_size_t size; + } WindowServer_29828; + struct { + Int access_rights; + } WindowServer_29831; + struct { + char *path; + } io_registry_entry_from_path; + } mach_args; +# endif + } ThreadOSstate; @@ -238,6 +370,7 @@ extern ThreadState VG_(threads)[VG_N_THREADS]; // to write to this. extern ThreadId VG_(running_tid); + /*------------------------------------------------------------*/ /*--- Basic operations on the thread table. ---*/ /*------------------------------------------------------------*/ diff --git a/coregrind/pub_core_trampoline.h b/coregrind/pub_core_trampoline.h index cbda7ab57..c8c652074 100644 --- a/coregrind/pub_core_trampoline.h +++ b/coregrind/pub_core_trampoline.h @@ -112,6 +112,16 @@ extern void VG_(ppctoc_magic_redirect_return_stub); extern void VG_(ppc64_aix5_do_preloads_then_start_client); #endif +#if defined(VGO_darwin) +extern void VG_(x86_darwin_SUBST_FOR_sigreturn); +extern SizeT VG_(darwin_REDIR_FOR_strlen)( void* ); +extern SizeT VG_(darwin_REDIR_FOR_strcmp)( void*, void* ); +extern void* VG_(darwin_REDIR_FOR_strcat)( void*, void * ); +extern char* VG_(darwin_REDIR_FOR_strcpy)( char *s1, char *s2 ); +extern SizeT VG_(darwin_REDIR_FOR_strlcat)( char *s1, const char *s2, SizeT size ); +extern UInt VG_(darwin_REDIR_FOR_arc4random)( void ); +#endif + #endif // __PUB_CORE_TRAMPOLINE_H /*--------------------------------------------------------------------*/ diff --git a/coregrind/pub_core_ume.h b/coregrind/pub_core_ume.h index cf7b1ad33..b6e59e56b 100644 --- a/coregrind/pub_core_ume.h +++ b/coregrind/pub_core_ume.h @@ -43,6 +43,10 @@ #elif defined(VGO_aix5) // The AIX port doesn't use UME. +#elif defined(VGO_darwin) +# define HAVE_MACHO +# define HAVE_SCRIPT + #else #error unknown architecture #endif @@ -60,9 +64,18 @@ typedef Addr exe_base; // INOUT: lowest (allowed) address of exe Addr exe_end; // INOUT: highest (allowed) address +#if !defined(VGO_darwin) Addr phdr; // OUT: address phdr was mapped at Int phnum; // OUT: number of phdrs Addr interp_base; // OUT: where interpreter (ld.so) was mapped +#else + Addr stack_start; // OUT: address of start of stack segment (hot) + Addr stack_end; // OUT: address of end of stack segment (cold) + Addr text; // OUT: address of executable's Mach header + Bool dynamic; // OUT: False iff executable is static + char* executable_path; // OUT: path passed to execve() +#endif + Addr entry; // OUT: entrypoint in main executable Addr init_ip; // OUT: address of first instruction to execute Addr brkbase; // OUT: base address of brk segment diff --git a/coregrind/pub_core_vkiscnums.h b/coregrind/pub_core_vkiscnums.h index 4cedf3752..4ce09d8bd 100644 --- a/coregrind/pub_core_vkiscnums.h +++ b/coregrind/pub_core_vkiscnums.h @@ -54,6 +54,17 @@ successful, False if the name is unknown. */ extern Bool VG_(aix5_register_syscall)( Int, UChar* ); +#elif defined(VGO_darwin) + +/* Convert a syscall number into a nicer form(?) */ +#if defined(VGA_x86) +# define VG_DARWIN_SYSNO_NUM(sysno) VG_DARWIN_SYSNO_PRINT(sysno) +#elif defined(VGA_amd64) +# define VG_DARWIN_SYSNO_NUM(sysno) (sysno) +#else +# error unknown arch +#endif + #else # error Unknown OS #endif // defined(VGO_*) diff --git a/coregrind/vg_preloaded.c b/coregrind/vg_preloaded.c index 353983274..6f0f04916 100644 --- a/coregrind/vg_preloaded.c +++ b/coregrind/vg_preloaded.c @@ -51,6 +51,8 @@ Hook for running __libc_freeres once the program exits. ------------------------------------------------------------------ */ +#if defined(VGO_linux) || defined(VGO_aix5) + void VG_NOTIFY_ON_LOAD(freeres)( void ); void VG_NOTIFY_ON_LOAD(freeres)( void ) { @@ -66,6 +68,94 @@ void VG_NOTIFY_ON_LOAD(freeres)( void ) *(int *)0 = 'x'; } +#elif defined(VGO_darwin) + +/* --------------------------------------------------------------------- + Darwin crash log hints + ------------------------------------------------------------------ */ + +/* This string will be inserted into crash logs, so crashes while + running under Valgrind can be distinguished from other crashes. */ +__private_extern__ char *__crashreporter_info__ = "Instrumented by Valgrind " VERSION; + +/* --------------------------------------------------------------------- + Darwin environment cleanup + ------------------------------------------------------------------ */ + +/* Scrubbing DYLD_INSERT_LIBRARIES from envp during exec is insufficient, + as there are other ways to launch a process with environment that + valgrind can't catch easily (i.e. launchd). + Instead, scrub DYLD_INSERT_LIBRARIES from the parent process once + dyld is done loading vg_preload.so. +*/ +#include +#include + +// GrP fixme copied from m_libcproc +static void env_unsetenv ( Char **env, const Char *varname ) +{ + Char **from; + Char **to = NULL; + Int len = strlen(varname); + + for (from = to = env; from && *from; from++) { + if (!(strncmp(varname, *from, len) == 0 && (*from)[len] == '=')) { + *to = *from; + to++; + } + } + *(to++) = *(from++); + /* fix the 4th "char* apple" pointer (aka. executable path pointer) */ + *(to++) = *(from++); + *to = NULL; +} + +static void vg_cleanup_env(void) __attribute__((constructor)); +static void vg_cleanup_env(void) +{ + Char **envp = (Char**)*_NSGetEnviron(); + env_unsetenv(envp, "VALGRIND_LAUNCHER"); + env_unsetenv(envp, "DYLD_SHARED_REGION"); + // GrP fixme should be more like mash_colon_env() + env_unsetenv(envp, "DYLD_INSERT_LIBRARIES"); +} + +/* --------------------------------------------------------------------- + Darwin arc4random (rdar://6166275) + ------------------------------------------------------------------ */ + +#include + +int VG_REPLACE_FUNCTION_ZU(libSystemZdZaZddylib, arc4random)(void); +int VG_REPLACE_FUNCTION_ZU(libSystemZdZaZddylib, arc4random)(void) +{ + static FILE *rnd = 0; + int result; + + if (!rnd) rnd = fopen("/dev/random", "r"); + + fread(&result, sizeof(result), 1, rnd); + return result; +} + +void VG_REPLACE_FUNCTION_ZU(libSystemZdZaZddylib, arc4random_stir)(void); +void VG_REPLACE_FUNCTION_ZU(libSystemZdZaZddylib, arc4random_stir)(void) +{ + // do nothing +} + +void VG_REPLACE_FUNCTION_ZU(libSystemZdZaZddylib, arc4random_addrandom)(unsigned char *dat, int datlen); +void VG_REPLACE_FUNCTION_ZU(libSystemZdZaZddylib, arc4random_addrandom)(unsigned char *dat, int datlen) +{ + // do nothing + // GrP fixme ought to check [dat..dat+datlen) is defined + // but don't care if it's initialized +} + +#else + +# error Unknown OS +#endif /*--------------------------------------------------------------------*/ /*--- end ---*/ diff --git a/darwin9.supp b/darwin9.supp new file mode 100644 index 000000000..74c31e390 --- /dev/null +++ b/darwin9.supp @@ -0,0 +1,167 @@ + +##----------------------------------------------------------------------## +# +# Suppressions for Darwin 9.x / Mac OS X 10.5 Leopard +# + +{ + mach_msg_trap-1 + Memcheck:Param + mach_msg(msg.msgh_remote_port) + fun:mach_msg_trap + obj:/System/Library/Frameworks/CoreFoundation* + obj:/System/Library/Frameworks/ApplicationServices* +} + +{ + mach_msg_trap-2 + Memcheck:Param + mach_msg(msg.msgh_remote_port) + fun:mach_msg_trap + obj:/System/Library/Frameworks/CoreFoundation* + obj:/System/Library/Frameworks/CoreServices* +} + +{ + mach_msg_trap-3 + Memcheck:Param + mach_msg(msg.msgh_remote_port) + fun:mach_msg_trap + obj:/System/Library/Frameworks/CoreFoundation* + obj:/System/Library/Frameworks/Carbon* +} + +{ + mach_msg_trap-4 + Memcheck:Param + mach_msg(msg.msgh_remote_port) + fun:mach_msg_trap + obj:/System/Library/Frameworks/CoreFoundation* + obj:/System/Library/Frameworks/CoreFoundation* +} + +{ + mach_msg_trap-5 + Memcheck:Param + mach_msg(msg.msgh_remote_port) + fun:mach_msg_trap + obj:/System/Library/Frameworks/CoreFoundation* + obj:/System/Library/Frameworks/AppKit* +} + +{ + macos-Cond-1 + Memcheck:Cond + fun:GetVariationInfoFromName + obj:/System/Library/Frameworks/ApplicationServices* + obj:/System/Library/Frameworks/ApplicationServices* +} + +{ + macos-Cond-2 + Memcheck:Cond + fun:*PMMutex*Lock* + obj:/System/Library/Frameworks/ApplicationServices* + obj:/System/Library/Frameworks/ApplicationServices* +} + +{ + macos-Cond-3 + Memcheck:Cond + fun:sseCGSBlendXXXX8888 + obj:/System/Library/Frameworks/ApplicationServices* + obj:/System/Library/Frameworks/ApplicationServices* +} + +{ + macos-Cond-4 + Memcheck:Cond + fun:*CASettingsStorage*RefreshSettings* + obj:/System/Library/Frameworks/CoreAudio* + obj:/System/Library/Frameworks/CoreAudio* +} + +{ + macos-Cond-5 + Memcheck:Cond + fun:gle* + obj:/System/Library/Frameworks/OpenGL* + obj:/System/Library/Frameworks/OpenGL* +} + +{ + futimes-1 + Memcheck:Param + futimes(tvp[1]) + fun:futimes + obj:/usr/lib/libSystem* + obj:/usr/lib/libSystem* +} + +##----------------------------------------------------------------------## +# +# Suppressions for Helgrind. + +# These ones were necessary to give no errors on a tiny non-threaded +# program. I don't know if they're real problems or false positives (njn). + +# keymgr seems to deliberately do some bogus actions, and if they are bogus, +# it passes the error codes back to the caller. +{ + __keymgr_initializer lock failed + Helgrind:PthAPIerror + fun:pthread_mutex_lock + fun:_dyld_register_func_for_*_image + fun:__keymgr_initializer + fun:libSystem_initializer +} +{ + __keymgr_initializer unlock failed + Helgrind:PthAPIerror + fun:pthread_mutex_unlock + fun:_dyld_register_func_for_*_image + fun:__keymgr_initializer + fun:libSystem_initializer +} +{ + __keymgr_initializer bogus unlock + Helgrind:UnlockBogus + fun:pthread_mutex_unlock + fun:_dyld_register_func_for_*_image + fun:__keymgr_initializer + fun:libSystem_initializer +} + +# These ones were necessary to give no errors on a tiny threaded program. +# I don't know if they're real problems or false positives (njn). + +{ + crude1 + Helgrind:Race + obj:/usr/lib/dyld +} +{ + crude2 + Helgrind:Race + obj:/usr/lib/libSystem.B.dylib +} +# This would be better as "fun:\?\?\?" but string matching doesn't seem to +# allow escaping meta-chars. +{ + crude3 + Helgrind:Race + fun:??? +} +{ + crude4 + Helgrind:Race + fun:mythread_wrapper +} +{ + crude5 + Helgrind:Race + ... + fun:pthread_create_WRK + fun:pthread_create +} + diff --git a/docs/internals/Darwin-notes.txt b/docs/internals/Darwin-notes.txt new file mode 100644 index 000000000..f526c4fe8 --- /dev/null +++ b/docs/internals/Darwin-notes.txt @@ -0,0 +1,136 @@ + +Valgrind-developer notes, re the MacOSX port +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +JRS 22 Mar 09: re these comments in m_libc* and m_debuglog: + +/* IMPORTANT: on Darwin it is essential to use the _nocancel versions + of syscalls rather than the vanilla version, if a _nocancel version + is available. See docs/internals/Darwin-notes.txt for the reason + why. */ + +when Valgrind does (for its own purposes, not for the client) +read/write/open/close etc syscalls, it really is critical to use the +_nocancel versions of syscalls rather than the vanilla versions. This +holds throughout the entire code base: whenever V does a syscall for +its own purposes, we must use the _nocancel version if it exists. +This is of course most prevalent in m_libc* since all of our +own-purpose (non-client) syscalls should get routed through there. + +Why? Because on Darwin, pthread cancellation is done within the +kernel (unlike on Linux, iiuc). And read/write/open/close and a whole +bunch of other syscalls to do with stream I/O are cancellation points. +So what can happen is, client informs the kernel that a given thread +is to be cancelled. Then at the next (eg) VG_(printf) call by that +thread, which leads to a sys_write, the write syscall gets hit by the +cancellation request, and is duly nuked by the kernel. Of course from +the outside it looks as if the thread had mysteriously disappeared off +the radar for no reason. + +In short, we need to use _nocancel versions in order to ensure that +cancellation requests only take effect at the places where the client +does a syscall, and not the places where Valgrind does syscalls. + +How observed: using the standard pipe-based implementation in +coregrind/m_scheduler/sema.c, none/tests/pth_cancel1 would hang +(compared to succeeding using native Darwin semaphores). And if the +"pause()" call in said test is turned into a spin ("while (1) ;") then +the entire Valgrind run mysteriously disappears, rather than spinning +using native Darwin semaphores. + +Because the pipe-based semaphore intensively uses sys_read/sys_write, +it is not surprising that it inadvertantly was eating up cancellation +requests directed to client threads. With abovementioned change in +force the pipe-based semaphore appears to work correctly. + + + +Valgrind-developer notes, things removed from the original MacOSX port +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +There was a broken debugstub implementation. It was removed over several +commits: r9477, which removed most of it, and r9711, r9759, and r10012, +which cleaned up remaining bits. + +There was machinery to read function names from Dwarf3 debug info. But we +already read function names from the symbol tables, so this was duplicated +functionality. Furthermore, a Darwin-specific hack was required in +storage.c to choose between symbol table names vs. Dwarf3 names. So this +machinery was removed in r10155. + + +Valgrind-developer notes, todos re the MacOSX port +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* m_syswrap/syscall-amd64-darwin.S + - correct signal mask is not applied during syscall + - restart-labels are completely bogus + +* m_syswrap/syswrap-darwin.c: + - PRE(sys_posix_spawn) completely ignores signal issues, and + also ignores the file_actions argument + +* env var handling w/ exec on Darwin: is there something odd? Compare + "valgrind env" on Darwin and Linux. On the former there are + settings VALGRIND_LIB and VALGRIND_LIB_INNER, but not for the + former. + There's a suspicious-looking "#if defined(VGO_darwin)" in + VG_(env_remove_valgrind_env_stuff). Maybe related? + +* Cleanups: sort wrappers in syswrap-darwin.c and priv_syswrap-darwin.h + alphabetically. Also, some aren't properly implemented -- check and + print warnings + +* Cleanups: m_scheduler/sema.c: use pipe implementation + (but this apparently causes none/tests/pth_cancel1 to hang. + I have no idea why, despite quite some investigation). + +* Cleanups: m_debugstub: move to attic + +* syswrap-darwin.c: sys_{f,}chmod_extended: handling of ARG5 is way + wrong + +* Cleanups (Linux,AIX5): bogus launcher-path mangling logic in + PRE(sys_execve) + +* Cleanups (ALL PLATFORMS): m_signals.c: are the _MY_SIGRETURN + assembly stubs actually necessary for anything? I don't know. + +* Cleanups: check that changes to VG_(stat) and VG_(stat64) have + not broken 64-bit statting on 32-bit Linux + +* Cleanups: #if !HAVE_PROC in m_main (to do with /proc//cmdline + +-------- + +m_main doesn't read symbols for the valgrind exe itself, which is +annoying. On minimal investigation it seems that the executable isn't +even listed by aspacem. This is very strange and not in accordance +with the Linux or AIX ports. + + +m_main: relatedly, Darwin version does not collect/give out +initial debuginfo handles; hence ptrcheck won't work + + +m_main: Darwin port relies on blocking out big sections of address +space with mmap at startup. We know from history that this is a bad +idea. (It's also really slow on 64-bit builds, taking 3--4 seconds.) +Also, startup is not done on the interim startup stack -- why not? + + +VG_(di_notify_mmap): Linux version is also used for Darwin, and +contains some ifdeffery. Clean up. + + +PRE(sys_fork), #ifdeffery + + +syswrap-generic.c: VG_(init_preopened_fds) is #ifdefd for Darwin + + +scheduler.c: #ifdeffery in VG_(get_thread_out_of_syscall) + + +look at notes in coregrind/Makefile.am re Mach RPC interface +definitions. See if we can get rid of any more stuff now that +m_debugstub is gone. diff --git a/docs/internals/Makefile.am b/docs/internals/Makefile.am index 1f82a6741..76fed0369 100644 --- a/docs/internals/Makefile.am +++ b/docs/internals/Makefile.am @@ -3,6 +3,7 @@ EXTRA_DIST = \ 3_2_BUGSTATUS.txt 3_3_BUGSTATUS.txt \ 3_4_BUGSTATUS.txt \ BIG_APP_NOTES.txt \ + Darwin-notes.txt \ directory-structure.txt \ howto_BUILD_KDE42.txt \ howto_oprofile.txt \ diff --git a/drd/Makefile.am b/drd/Makefile.am index 18a8844db..84655f236 100644 --- a/drd/Makefile.am +++ b/drd/Makefile.am @@ -1,6 +1,7 @@ include $(top_srcdir)/Makefile.tool.am noinst_PROGRAMS = +noinst_DSYMS = if VGCONF_PLATFORMS_INCLUDE_X86_LINUX noinst_PROGRAMS += drd-x86-linux vgpreload_drd-x86-linux.so endif @@ -19,6 +20,14 @@ endif if VGCONF_PLATFORMS_INCLUDE_PPC64_AIX5 noinst_PROGRAMS += drd-ppc64-aix5 vgpreload_drd-ppc64-aix5.so endif +if VGCONF_PLATFORMS_INCLUDE_X86_DARWIN + noinst_PROGRAMS += drd-x86-darwin vgpreload_drd-x86-darwin.so + noinst_DSYMS += vgpreload_drd-x86-darwin.so +endif +if VGCONF_PLATFORMS_INCLUDE_AMD64_DARWIN + noinst_PROGRAMS += drd-amd64-darwin vgpreload_drd-amd64-darwin.so + noinst_DSYMS += vgpreload_drd-amd64-darwin.so +endif VGPRELOAD_DRD_SOURCES = \ @@ -91,6 +100,22 @@ vgpreload_drd_ppc64_aix5_so_DEPENDENCIES = vgpreload_drd_ppc64_aix5_so_LDFLAGS = $(PRELOAD_LDFLAGS_PPC64_AIX5)\ $(LIBREPLACEMALLOC_LDFLAGS_PPC64_AIX5) +vgpreload_drd_x86_darwin_so_SOURCES = $(VGPRELOAD_DRD_SOURCES) +vgpreload_drd_x86_darwin_so_CPPFLAGS = $(AM_CPPFLAGS_X86_DARWIN) +vgpreload_drd_x86_darwin_so_CFLAGS = $(AM_CFLAGS_X86_DARWIN) $(AM_CFLAGS_PIC) +vgpreload_drd_x86_darwin_so_CCASFLAGS = $(AM_CCASFLAGS_X86_DARWIN) +vgpreload_drd_x86_darwin_so_DEPENDENCIES = $(LIBREPLACEMALLOC_X86_DARWIN) +vgpreload_drd_x86_darwin_so_LDFLAGS = $(PRELOAD_LDFLAGS_X86_DARWIN)\ + $(LIBREPLACEMALLOC_LDFLAGS_X86_DARWIN) + +vgpreload_drd_amd64_darwin_so_SOURCES = $(VGPRELOAD_DRD_SOURCES) +vgpreload_drd_amd64_darwin_so_CPPFLAGS = $(AM_CPPFLAGS_AMD64_DARWIN) +vgpreload_drd_amd64_darwin_so_CFLAGS = $(AM_CFLAGS_AMD64_DARWIN) $(AM_CFLAGS_PIC) +vgpreload_drd_amd64_darwin_so_CCASFLAGS = $(AM_CCASFLAGS_AMD64_DARWIN) +vgpreload_drd_amd64_darwin_so_DEPENDENCIES = +vgpreload_drd_amd64_darwin_so_LDFLAGS = $(PRELOAD_LDFLAGS_AMD64_DARWIN)\ + $(LIBREPLACEMALLOC_LDFLAGS_AMD64_DARWIN) + DRD_SOURCES = \ drd_barrier.c \ @@ -175,3 +200,19 @@ drd_ppc64_aix5_CFLAGS = $(AM_CFLAGS_PPC64_AIX5) drd_ppc64_aix5_DEPENDENCIES = $(COREGRIND_LIBS_PPC64_AIX5) drd_ppc64_aix5_LDADD = $(TOOL_LDADD_PPC64_AIX5) drd_ppc64_aix5_LDFLAGS = $(TOOL_LDFLAGS_PPC64_AIX5) + +drd_x86_darwin_SOURCES = $(DRD_SOURCES) +drd_x86_darwin_CPPFLAGS = $(AM_CPPFLAGS_X86_DARWIN) +drd_x86_darwin_CFLAGS = $(AM_CFLAGS_X86_DARWIN) +drd_x86_darwin_DEPENDENCIES = $(COREGRIND_LIBS_X86_DARWIN) +drd_x86_darwin_LDADD = $(TOOL_LDADD_X86_DARWIN) +drd_x86_darwin_LDFLAGS = $(TOOL_LDFLAGS_X86_DARWIN) + +drd_amd64_darwin_SOURCES = $(DRD_SOURCES) +drd_amd64_darwin_CPPFLAGS = $(AM_CPPFLAGS_AMD64_DARWIN) +drd_amd64_darwin_CFLAGS = $(AM_CFLAGS_AMD64_DARWIN) +drd_amd64_darwin_DEPENDENCIES = $(COREGRIND_LIBS_AMD64_DARWIN) +drd_amd64_darwin_LDADD = $(TOOL_LDADD_AMD64_DARWIN) +drd_amd64_darwin_LDFLAGS = $(TOOL_LDFLAGS_AMD64_DARWIN) + + diff --git a/drd/drd_main.c b/drd/drd_main.c index 0dfb00afe..3cc6348ff 100644 --- a/drd/drd_main.c +++ b/drd/drd_main.c @@ -607,8 +607,13 @@ static void DRD_(fini)(Int exitcode) static void drd_pre_clo_init(void) { - // Basic tool stuff. +#if defined(VGO_darwin) + // This makes the (all-failing) regtests run much faster. + VG_(printf)("DRD doesn't work on Darwin yet, sorry.\n"); + VG_(exit)(1); +#endif + // Basic tool stuff. VG_(details_name) ("drd"); VG_(details_version) (NULL); VG_(details_description) ("a thread error detector"); diff --git a/drd/drd_pthread_intercepts.c b/drd/drd_pthread_intercepts.c index f77569d59..db33fbdcf 100644 --- a/drd/drd_pthread_intercepts.c +++ b/drd/drd_pthread_intercepts.c @@ -643,6 +643,7 @@ PTH_FUNC(int, pthreadZucondZubroadcastZa, // pthread_cond_broadcast* } +#if defined(HAVE_PTHREAD_SPIN_LOCK) // pthread_spin_init PTH_FUNC(int, pthreadZuspinZuinit, // pthread_spin_init pthread_spinlock_t *spinlock, @@ -723,7 +724,10 @@ PTH_FUNC(int, pthreadZuspinZuunlock, // pthread_spin_unlock spinlock, 0, 0, 0, 0); return ret; } +#endif // HAVE_PTHREAD_SPIN_LOCK + +#if defined(HAVE_PTHREAD_BARRIER_INIT) // pthread_barrier_init PTH_FUNC(int, pthreadZubarrierZuinit, // pthread_barrier_init pthread_barrier_t* barrier, @@ -775,6 +779,7 @@ PTH_FUNC(int, pthreadZubarrierZuwait, // pthread_barrier_wait ret == PTHREAD_BARRIER_SERIAL_THREAD, 0); return ret; } +#endif // HAVE_PTHREAD_BARRIER_INIT // sem_init diff --git a/drd/tests/recursive_mutex.c b/drd/tests/recursive_mutex.c index ad79b63fd..3ddfc2538 100644 --- a/drd/tests/recursive_mutex.c +++ b/drd/tests/recursive_mutex.c @@ -10,6 +10,7 @@ #include "../../config.h" +#if !defined(VGO_darwin) static void lock_twice(pthread_mutex_t* const p) { pthread_mutex_lock(p); @@ -17,6 +18,7 @@ static void lock_twice(pthread_mutex_t* const p) pthread_mutex_unlock(p); pthread_mutex_unlock(p); } +#endif int main(int argc, char** argv) { @@ -60,6 +62,10 @@ int main(int argc, char** argv) pthread_mutex_destroy(&m); } #endif + +// DDD: Darwin doesn't support signals yet, so the alarm() call doesn't kick +// in, which causes it to hang. +#if !defined(VGO_darwin) { pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; @@ -68,5 +74,6 @@ int main(int argc, char** argv) lock_twice(&m); } printf("Done.\n"); +#endif return 0; } diff --git a/exp-omega/Makefile.am b/exp-omega/Makefile.am index 6af2f3ff5..122d8a69b 100644 --- a/exp-omega/Makefile.am +++ b/exp-omega/Makefile.am @@ -30,6 +30,12 @@ EXTRA_DIST = exp-omega.h o_main.c o_replace_memops.c ##zz if VGCONF_PLATFORMS_INCLUDE_PPC64_AIX5 ##zz noinst_PROGRAMS += exp-omega-ppc64-aix5 vgpreload_exp-omega-ppc64-aix5.so ##zz endif +##zz if VGCONF_PLATFORMS_INCLUDE_X86_DARWIN +##zz noinst_PROGRAMS += exp-omega-x86-darwin vgpreload_exp-omega-x86-darwin.so +##zz endif +##zz if VGCONF_PLATFORMS_INCLUDE_AMD64_DARWIN +##zz noinst_PROGRAMS += exp-omega-amd64-darwin vgpreload_exp-omega-amd64-darwin.so +##zz endif ##zz ##zz VGPRELOAD_OMEGA_SOURCES_COMMON = o_replace_memops.c ##zz @@ -87,6 +93,24 @@ EXTRA_DIST = exp-omega.h o_main.c o_replace_memops.c ##zz $(PRELOAD_LDFLAGS_PPC64_AIX5) \ ##zz $(LIBREPLACEMALLOC_LDFLAGS_PPC64_AIX5) ##zz +##zz vgpreload_exp_omega_x86_darwin_so_SOURCES = $(VGPRELOAD_OMEGA_SOURCES_COMMON) +##zz vgpreload_exp_omega_x86_darwin_so_CPPFLAGS = $(AM_CPPFLAGS_X86_DARWIN) +##zz vgpreload_exp_omega_x86_darwin_so_CFLAGS = $(AM_CFLAGS_X86_DARWIN) $(AM_CFLAGS_PIC) -O2 +##zz vgpreload_exp_omega_x86_darwin_so_CCASFLAGS = $(AM_CCASFLAGS_X86_DARWIN) +##zz vgpreload_exp_omega_x86_darwin_so_DEPENDENCIES = $(LIBREPLACEMALLOC_X86_DARWIN) +##zz vgpreload_exp_omega_x86_darwin_so_LDFLAGS = \ +##zz $(PRELOAD_LDFLAGS_X86_DARWIN) \ +##zz $(LIBREPLACEMALLOC_LDFLAGS_X86_DARWIN) +##zz +##zz vgpreload_exp_omega_amd64_darwin_so_SOURCES = $(VGPRELOAD_OMEGA_SOURCES_COMMON) +##zz vgpreload_exp_omega_amd64_darwin_so_CPPFLAGS = $(AM_CPPFLAGS_AMD64_DARWIN) +##zz vgpreload_exp_omega_amd64_darwin_so_CFLAGS = $(AM_CFLAGS_AMD64_DARWIN) $(AM_CFLAGS_PIC) -O2 +##zz vgpreload_exp_omega_amd64_darwin_so_CCASFLAGS = $(AM_CCASFLAGS_AMD64_DARWIN) +##zz vgpreload_exp_omega_amd64_darwin_so_DEPENDENCIES = $(LIBREPLACEMALLOC_AMD64_DARWIN) +##zz vgpreload_exp_omega_amd64_darwin_so_LDFLAGS = \ +##zz $(PRELOAD_LDFLAGS_AMD64_DARWIN) \ +##zz $(LIBREPLACEMALLOC_LDFLAGS_AMD64_DARWIN) +##zz ##zz OMEGA_SOURCES_COMMON = \ ##zz o_main.c ##zz @@ -138,6 +162,22 @@ EXTRA_DIST = exp-omega.h o_main.c o_replace_memops.c ##zz exp_omega_ppc64_aix5_LDADD = $(TOOL_LDADD_PPC64_AIX5) ##zz exp_omega_ppc64_aix5_LDFLAGS = $(TOOL_LDFLAGS_PPC64_AIX5) ##zz +##zz exp_omega_x86_darwin_SOURCES = $(OMEGA_SOURCES_COMMON) +##zz exp_omega_x86_darwin_CPPFLAGS = $(AM_CPPFLAGS_X86_DARWIN) +##zz exp_omega_x86_darwin_CFLAGS = $(AM_CFLAGS_X86_DARWIN) -O2 +##zz exp_omega_x86_darwin_CCASFLAGS = $(AM_CCASFLAGS_X86_DARWIN) +##zz exp_omega_x86_darwin_DEPENDENCIES = $(COREGRIND_LIBS_X86_DARWIN) +##zz exp_omega_x86_darwin_LDADD = $(TOOL_LDADD_X86_DARWIN) +##zz exp_omega_x86_darwin_LDFLAGS = $(TOOL_LDFLAGS_X86_DARWIN) +##zz +##zz exp_omega_amd64_darwin_SOURCES = $(OMEGA_SOURCES_COMMON) +##zz exp_omega_amd64_darwin_CPPFLAGS = $(AM_CPPFLAGS_AMD64_DARWIN) +##zz exp_omega_amd64_darwin_CFLAGS = $(AM_CFLAGS_AMD64_DARWIN) -g -O0 #-O2 +##zz exp_omega_amd64_darwin_CCASFLAGS = $(AM_CCASFLAGS_AMD64_DARWIN) +##zz exp_omega_amd64_darwin_DEPENDENCIES = $(COREGRIND_LIBS_AMD64_DARWIN) +##zz exp_omega_amd64_darwin_LDADD = $(TOOL_LDADD_AMD64_DARWIN) +##zz exp_omega_amd64_darwin_LDFLAGS = $(TOOL_LDFLAGS_AMD64_DARWIN) +##zz ##zz oincludedir = $(includedir)/valgrind ##zz ##zz oinclude_HEADERS = exp-omega.h diff --git a/exp-ptrcheck/Makefile.am b/exp-ptrcheck/Makefile.am index d4591e3df..37b3ef55b 100644 --- a/exp-ptrcheck/Makefile.am +++ b/exp-ptrcheck/Makefile.am @@ -1,6 +1,7 @@ include $(top_srcdir)/Makefile.tool.am noinst_PROGRAMS = +noinst_DSYMS = if VGCONF_PLATFORMS_INCLUDE_X86_LINUX noinst_PROGRAMS += exp-ptrcheck-x86-linux vgpreload_exp-ptrcheck-x86-linux.so endif @@ -19,6 +20,14 @@ endif if VGCONF_PLATFORMS_INCLUDE_PPC64_AIX5 noinst_PROGRAMS += exp-ptrcheck-ppc64-aix5 vgpreload_exp-ptrcheck-ppc64-aix5.so endif +if VGCONF_PLATFORMS_INCLUDE_X86_DARWIN +noinst_PROGRAMS += exp-ptrcheck-x86-darwin vgpreload_exp-ptrcheck-x86-darwin.so +noinst_DSYMS += vgpreload_exp-ptrcheck-x86-darwin.so +endif +if VGCONF_PLATFORMS_INCLUDE_AMD64_DARWIN +noinst_PROGRAMS += exp-ptrcheck-amd64-darwin vgpreload_exp-ptrcheck-amd64-darwin.so +noinst_DSYMS += vgpreload_exp-ptrcheck-amd64-darwin.so +endif VGPRELOAD_EXP_PTRCHECK_SOURCES_COMMON = h_intercepts.c @@ -77,6 +86,24 @@ vgpreload_exp_ptrcheck_ppc64_aix5_so_LDFLAGS = \ $(PRELOAD_LDFLAGS_PPC64_AIX5) \ $(LIBREPLACEMALLOC_LDFLAGS_PPC64_AIX5) +vgpreload_exp_ptrcheck_x86_darwin_so_SOURCES = $(VGPRELOAD_EXP_PTRCHECK_SOURCES_COMMON) +vgpreload_exp_ptrcheck_x86_darwin_so_CPPFLAGS = $(AM_CPPFLAGS_X86_DARWIN) +vgpreload_exp_ptrcheck_x86_darwin_so_CFLAGS = $(AM_CFLAGS_X86_DARWIN) $(AM_CFLAGS_PIC) -O2 +vgpreload_exp_ptrcheck_x86_darwin_so_CCASFLAGS = $(AM_CCASFLAGS_X86_DARWIN) +vgpreload_exp_ptrcheck_x86_darwin_so_DEPENDENCIES = $(LIBREPLACEMALLOC_X86_DARWIN) +vgpreload_exp_ptrcheck_x86_darwin_so_LDFLAGS = \ + $(PRELOAD_LDFLAGS_X86_DARWIN) \ + $(LIBREPLACEMALLOC_LDFLAGS_X86_DARWIN) + +vgpreload_exp_ptrcheck_amd64_darwin_so_SOURCES = $(VGPRELOAD_EXP_PTRCHECK_SOURCES_COMMON) +vgpreload_exp_ptrcheck_amd64_darwin_so_CPPFLAGS = $(AM_CPPFLAGS_AMD64_DARWIN) +vgpreload_exp_ptrcheck_amd64_darwin_so_CFLAGS = $(AM_CFLAGS_AMD64_DARWIN) $(AM_CFLAGS_PIC) -O2 +vgpreload_exp_ptrcheck_amd64_darwin_so_CCASFLAGS = $(AM_CCASFLAGS_AMD64_DARWIN) +vgpreload_exp_ptrcheck_amd64_darwin_so_DEPENDENCIES = $(LIBREPLACEMALLOC_AMD64_DARWIN) +vgpreload_exp_ptrcheck_amd64_darwin_so_LDFLAGS = \ + $(PRELOAD_LDFLAGS_AMD64_DARWIN) \ + $(LIBREPLACEMALLOC_LDFLAGS_AMD64_DARWIN) + EXP_PTRCHECK_SOURCES_COMMON = \ @@ -124,6 +151,20 @@ exp_ptrcheck_ppc64_aix5_DEPENDENCIES = $(COREGRIND_LIBS_PPC64_AIX5) exp_ptrcheck_ppc64_aix5_LDADD = $(TOOL_LDADD_PPC64_AIX5) exp_ptrcheck_ppc64_aix5_LDFLAGS = $(TOOL_LDFLAGS_PPC64_AIX5) +exp_ptrcheck_x86_darwin_SOURCES = $(EXP_PTRCHECK_SOURCES_COMMON) +exp_ptrcheck_x86_darwin_CPPFLAGS = $(AM_CPPFLAGS_X86_DARWIN) +exp_ptrcheck_x86_darwin_CFLAGS = $(AM_CFLAGS_X86_DARWIN) +exp_ptrcheck_x86_darwin_DEPENDENCIES = $(COREGRIND_LIBS_X86_DARWIN) +exp_ptrcheck_x86_darwin_LDADD = $(TOOL_LDADD_X86_DARWIN) +exp_ptrcheck_x86_darwin_LDFLAGS = $(TOOL_LDFLAGS_X86_DARWIN) + +exp_ptrcheck_amd64_darwin_SOURCES = $(EXP_PTRCHECK_SOURCES_COMMON) +exp_ptrcheck_amd64_darwin_CPPFLAGS = $(AM_CPPFLAGS_AMD64_DARWIN) +exp_ptrcheck_amd64_darwin_CFLAGS = $(AM_CFLAGS_AMD64_DARWIN) +exp_ptrcheck_amd64_darwin_DEPENDENCIES = $(COREGRIND_LIBS_AMD64_DARWIN) +exp_ptrcheck_amd64_darwin_LDADD = $(TOOL_LDADD_AMD64_DARWIN) +exp_ptrcheck_amd64_darwin_LDFLAGS = $(TOOL_LDFLAGS_AMD64_DARWIN) + noinst_HEADERS = h_main.h sg_main.h pc_common.h EXTRA_DIST = diff --git a/exp-ptrcheck/h_main.c b/exp-ptrcheck/h_main.c index 50f4154cd..c4342a323 100644 --- a/exp-ptrcheck/h_main.c +++ b/exp-ptrcheck/h_main.c @@ -1957,6 +1957,8 @@ static void post_reg_write_nonptr ( ThreadId tid, PtrdiffT offset, SizeT size ) if (is_integer_guest_reg( (Int)offset, (Int)size )) { put_guest_intreg( tid, 1, offset, size, (UWord)NONPTR ); } else { + // DDD: on Darwin, this assertion fails because we currently do a + // 'post_reg_write' on the 'guest_CC_DEP1' pseudo-register. tl_assert(0); } // VG_(set_thread_shadow_archreg)( tid, reg, (UInt)NONPTR ); @@ -2127,16 +2129,16 @@ void h_pre_syscall ( ThreadId tid, UInt sysno ) syscall-specific handling is is required. No further details of it are stored in the table. - On Linux, 'number' is a __NR_xxx constant. + On Linux and Darwin, 'number' is a __NR_xxx constant. On AIX5, 'number' is an Int*, which points to the Int variable holding the currently assigned number for this syscall. When querying the table, we compare the supplied syscall number - with the 'number' field (directly on Linux, after dereferencing on - AIX5), to find the relevant entry. This requires a linear search - of the table. To stop the costs getting too high, the table is - incrementally rearranged after each search, to move commonly + with the 'number' field (directly on Linux and Darwin, after + dereferencing on AIX5), to find the relevant entry. This requires a + linear search of the table. To stop the costs getting too high, the + table is incrementally rearranged after each search, to move commonly requested items a bit closer to the front. The table is built once, the first time it is used. After that we @@ -2439,6 +2441,30 @@ static void setup_post_syscall_table ( void ) ADD(1, __NR_AIX5_kload); /* not sure what to do here */ ADD(0, __NR_AIX5_kwrite); + /* --------------- DARWIN ------------- */ + +# elif defined(VGO_darwin) + +# define ADD(_flag, _syscallname) \ + do { UWordPair p; p.uw1 = (_syscallname); p.uw2 = (_flag); \ + VG_(addToXA)( post_syscall_table, &p ); \ + } while (0) + + // DDD: a desultory attempt thus far... + + // Unix/BSD syscalls. + + // Mach traps. + ADD(0, __NR_host_self_trap); + ADD(0, __NR_mach_msg_trap); + ADD(0, __NR_mach_reply_port); + ADD(0, __NR_task_self_trap); + + // Machine-dependent syscalls. + ADD(0, __NR_pthread_set_self); + + /* ------------------------------------ */ + # else # error "Unsupported OS" # endif @@ -2459,7 +2485,7 @@ void h_post_syscall ( ThreadId tid, UInt sysno, SysRes res ) n = VG_(sizeXA)( post_syscall_table ); for (i = 0; i < n; i++) { pair = VG_(indexXA)( post_syscall_table, i ); -# if defined(VGO_linux) +# if defined(VGO_linux) || defined(VGO_darwin) if (pair->uw1 == (UWord)sysno) break; # elif defined(VGO_aix5) @@ -2473,10 +2499,17 @@ void h_post_syscall ( ThreadId tid, UInt sysno, SysRes res ) tl_assert(i >= 0 && i <= n); if (i == n) { +// DDD: genericise this +# if defined(VGO_linux) + VG_(printf)("sysno == %u\n", sysno); +# elif defined(VGO_aix5) VG_(printf)("sysno == %u\n", sysno); -# if defined(VGO_aix5) VG_(printf)("syscallnm == %s\n", VG_(aix5_sysno_to_sysname)(sysno)); +# elif defined(VGO_darwin) + VG_(printf)("sysno == %d\n", VG_DARWIN_SYSNO_PRINT(sysno)); +# else +# error "Unsupported OS" # endif VG_(tool_panic)("unhandled syscall"); } diff --git a/exp-ptrcheck/pc_main.c b/exp-ptrcheck/pc_main.c index 1dfd9a5d7..6f44bbfd3 100644 --- a/exp-ptrcheck/pc_main.c +++ b/exp-ptrcheck/pc_main.c @@ -144,6 +144,12 @@ static void pc_post_clo_init ( void ) static void pc_pre_clo_init(void) { +#if defined(VGO_darwin) + // This makes the (all-failing) regtests run much faster. + VG_(printf)("Ptrcheck doesn't work on Darwin yet, sorry.\n"); + VG_(exit)(1); +#endif + VG_(details_name) ("exp-ptrcheck"); VG_(details_version) (NULL); VG_(details_description) ("a heap, stack & global array " diff --git a/exp-ptrcheck/tests/Makefile.am b/exp-ptrcheck/tests/Makefile.am index 5c27f35b9..6a1d884d3 100644 --- a/exp-ptrcheck/tests/Makefile.am +++ b/exp-ptrcheck/tests/Makefile.am @@ -65,7 +65,7 @@ EXTRA_DIST = $(noinst_SCRIPTS) \ zero.vgtest zero.stderr.exp check_PROGRAMS = \ - add and arith bad_percentify base ccc cmp fp \ + add and arith bad_percentify base cmp fp \ globalerr hackedbz2 \ hp_bounds hp_dangle idiv imul \ justify mm not neg or partial \ @@ -74,6 +74,14 @@ check_PROGRAMS = \ stackerr \ strcpy strlen sub supp syscall tricky unaligned xor zero +# DDD: not sure if these ones should work on Darwin or not... if not, should +# be moved into x86-linux/. +if ! VGCONF_OS_IS_DARWIN + check_PROGRAMS += \ + ccc +endif + + AM_CFLAGS += $(AM_FLAG_M3264_PRI) AM_CXXFLAGS += $(AM_FLAG_M3264_PRI) @@ -97,12 +105,17 @@ else if VGCONF_PLATFORMS_INCLUDE_PPC32_AIX5 preen_invars_LDADD = -ldl preen_invars_LDFLAGS = $(AM_FLAG_M3264_PRI) -Wl,-G -Wl,-bnogc +else +if VGCONF_OS_IS_DARWIN + preen_invars_LDADD = -ldl + preen_invars_LDFLAGS = $(AM_FLAG_M3264_PRI) else preen_invars_LDADD = -ldl preen_invars_LDFLAGS = $(AM_FLAG_M3264_PRI) \ -Wl,-rpath,$(top_builddir)/memcheck/tests endif endif +endif preen_invars_so_so_CFLAGS = $(AM_CFLAGS) -fpic if VGCONF_PLATFORMS_INCLUDE_PPC64_AIX5 @@ -111,9 +124,14 @@ else if VGCONF_PLATFORMS_INCLUDE_PPC32_AIX5 preen_invars_so_so_LDFLAGS = -fpic $(AM_FLAG_M3264_PRI) -shared \ -Wl,-G -Wl,-bnogc +else +if VGCONF_OS_IS_DARWIN + preen_invars_so_so_LDFLAGS = -fpic $(AM_FLAG_M3264_PRI) -dynamic \ + -dynamiclib -all_load else preen_invars_so_so_LDFLAGS = -fpic $(AM_FLAG_M3264_PRI) -shared \ -Wl,-soname -Wl,preen_invars_so.so endif endif +endif diff --git a/glibc-2.34567-NPTL-helgrind.supp b/glibc-2.34567-NPTL-helgrind.supp index 0a4aeaa1e..616309ede 100644 --- a/glibc-2.34567-NPTL-helgrind.supp +++ b/glibc-2.34567-NPTL-helgrind.supp @@ -137,6 +137,7 @@ { helgrind-glibc2X-112 Helgrind:Race + fun:pthread_create_WRK fun:pthread_create@* } { diff --git a/helgrind/Makefile.am b/helgrind/Makefile.am index 02f96bab1..55379c2f9 100644 --- a/helgrind/Makefile.am +++ b/helgrind/Makefile.am @@ -1,6 +1,7 @@ include $(top_srcdir)/Makefile.tool.am noinst_PROGRAMS = +noinst_DSYMS = if VGCONF_PLATFORMS_INCLUDE_X86_LINUX noinst_PROGRAMS += helgrind-x86-linux vgpreload_helgrind-x86-linux.so endif @@ -19,6 +20,14 @@ endif if VGCONF_PLATFORMS_INCLUDE_PPC64_AIX5 noinst_PROGRAMS += helgrind-ppc64-aix5 vgpreload_helgrind-ppc64-aix5.so endif +if VGCONF_PLATFORMS_INCLUDE_X86_DARWIN +noinst_PROGRAMS += helgrind-x86-darwin vgpreload_helgrind-x86-darwin.so +noinst_DSYMS += vgpreload_helgrind-x86-darwin.so +endif +if VGCONF_PLATFORMS_INCLUDE_AMD64_DARWIN +noinst_PROGRAMS += helgrind-amd64-darwin vgpreload_helgrind-amd64-darwin.so +noinst_DSYMS += vgpreload_helgrind-amd64-darwin.so +endif VGPRELOAD_HELGRIND_SOURCES_COMMON = hg_intercepts.c @@ -70,6 +79,22 @@ vgpreload_helgrind_ppc64_aix5_so_LDFLAGS = \ $(PRELOAD_LDFLAGS_PPC64_AIX5) \ $(LIBREPLACEMALLOC_LDFLAGS_PPC64_AIX5) +vgpreload_helgrind_x86_darwin_so_SOURCES = $(VGPRELOAD_HELGRIND_SOURCES_COMMON) +vgpreload_helgrind_x86_darwin_so_CPPFLAGS = $(AM_CPPFLAGS_X86_DARWIN) +vgpreload_helgrind_x86_darwin_so_CFLAGS = $(AM_CFLAGS_X86_DARWIN) $(AM_CFLAGS_PIC) +vgpreload_helgrind_x86_darwin_so_DEPENDENCIES = $(LIBREPLACEMALLOC_X86_DARWIN) +vgpreload_helgrind_x86_darwin_so_LDFLAGS = \ + $(PRELOAD_LDFLAGS_X86_DARWIN) \ + $(LIBREPLACEMALLOC_LDFLAGS_X86_DARWIN) + +vgpreload_helgrind_amd64_darwin_so_SOURCES = $(VGPRELOAD_HELGRIND_SOURCES_COMMON) +vgpreload_helgrind_amd64_darwin_so_CPPFLAGS = $(AM_CPPFLAGS_AMD64_DARWIN) +vgpreload_helgrind_amd64_darwin_so_CFLAGS = $(AM_CFLAGS_AMD64_DARWIN) $(AM_CFLAGS_PIC) +vgpreload_helgrind_amd64_darwin_so_DEPENDENCIES = $(LIBREPLACEMALLOC_AMD64_DARWIN) +vgpreload_helgrind_amd64_darwin_so_LDFLAGS = \ + $(PRELOAD_LDFLAGS_AMD64_DARWIN) \ + $(LIBREPLACEMALLOC_LDFLAGS_AMD64_DARWIN) + HELGRIND_SOURCES_COMMON = \ hg_basics.c hg_lock_n_thread.c hg_wordset.c libhb_core.c \ hg_errors.c hg_main.c @@ -116,6 +141,20 @@ helgrind_ppc64_aix5_DEPENDENCIES = $(COREGRIND_LIBS_PPC64_AIX5) helgrind_ppc64_aix5_LDADD = $(TOOL_LDADD_PPC64_AIX5) helgrind_ppc64_aix5_LDFLAGS = $(TOOL_LDFLAGS_PPC64_AIX5) +helgrind_x86_darwin_SOURCES = $(HELGRIND_SOURCES_COMMON) +helgrind_x86_darwin_CPPFLAGS = $(AM_CPPFLAGS_X86_DARWIN) +helgrind_x86_darwin_CFLAGS = $(AM_CFLAGS_X86_DARWIN) -O2 +helgrind_x86_darwin_DEPENDENCIES = $(COREGRIND_LIBS_X86_DARWIN) +helgrind_x86_darwin_LDADD = $(TOOL_LDADD_X86_DARWIN) +helgrind_x86_darwin_LDFLAGS = $(TOOL_LDFLAGS_X86_DARWIN) + +helgrind_amd64_darwin_SOURCES = $(HELGRIND_SOURCES_COMMON) +helgrind_amd64_darwin_CPPFLAGS = $(AM_CPPFLAGS_AMD64_DARWIN) +helgrind_amd64_darwin_CFLAGS = $(AM_CFLAGS_AMD64_DARWIN) -O2 +helgrind_amd64_darwin_DEPENDENCIES = $(COREGRIND_LIBS_AMD64_DARWIN) +helgrind_amd64_darwin_LDADD = $(TOOL_LDADD_AMD64_DARWIN) +helgrind_amd64_darwin_LDFLAGS = $(TOOL_LDFLAGS_AMD64_DARWIN) + hgincludedir = $(includedir)/valgrind hginclude_HEADERS = helgrind.h diff --git a/helgrind/hg_intercepts.c b/helgrind/hg_intercepts.c index 698a0829c..47cb3af76 100644 --- a/helgrind/hg_intercepts.c +++ b/helgrind/hg_intercepts.c @@ -195,9 +195,13 @@ static void* mythread_wrapper ( void* xargsV ) } // pthread_create -PTH_FUNC(int, pthreadZucreateZAZa, // pthread_create@* - pthread_t *thread, const pthread_attr_t *attr, - void *(*start) (void *), void *arg) +// glibc-2.8.90 has "pthread_create@@GLIBC_2.2.5". +// Darwin has "pthread_create". +// +// DDD: for Darwin, need to have non-"@*"-suffixed versions for all pthread +// functions that currently have them. +static int pthread_create_WRK(pthread_t *thread, const pthread_attr_t *attr, + void *(*start) (void *), void *arg) { int ret; OrigFn fn; @@ -234,6 +238,16 @@ PTH_FUNC(int, pthreadZucreateZAZa, // pthread_create@* } return ret; } +PTH_FUNC(int, pthreadZucreate, // pthread_create + pthread_t *thread, const pthread_attr_t *attr, + void *(*start) (void *), void *arg) { + return pthread_create_WRK(thread, attr, start, arg); +} +PTH_FUNC(int, pthreadZucreateZAZa, // pthread_create@* + pthread_t *thread, const pthread_attr_t *attr, + void *(*start) (void *), void *arg) { + return pthread_create_WRK(thread, attr, start, arg); +} // pthread_join PTH_FUNC(int, pthreadZujoin, // pthread_join @@ -756,6 +770,8 @@ PTH_FUNC(int, pthreadZucondZudestroyZAZa, // pthread_cond_destroy@* /*--- pthread_barrier_t functions ---*/ /*----------------------------------------------------------------*/ +#if defined(HAVE_PTHREAD_BARRIER_INIT) + /* Handled: pthread_barrier_init pthread_barrier_wait pthread_barrier_destroy @@ -861,6 +877,8 @@ PTH_FUNC(int, pthreadZubarrierZudestroy, // pthread_barrier_destroy return ret; } +#endif // defined(HAVE_PTHREAD_BARRIER_INIT) + /*----------------------------------------------------------------*/ /*--- pthread_rwlock_t functions ---*/ /*----------------------------------------------------------------*/ diff --git a/helgrind/hg_main.c b/helgrind/hg_main.c index 796d96693..bff2fd59e 100644 --- a/helgrind/hg_main.c +++ b/helgrind/hg_main.c @@ -11,6 +11,8 @@ Copyright (C) 2007-2009 OpenWorks LLP info@open-works.co.uk + Copyright (C) 2007-2009 Apple, Inc. + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the @@ -4201,6 +4203,7 @@ ExeContext* for_libhb__get_EC ( Thr* hbt ) static void hg_pre_clo_init ( void ) { Thr* hbthr_root; + VG_(details_name) ("Helgrind"); VG_(details_version) (NULL); VG_(details_description) ("a thread error detector"); diff --git a/helgrind/tests/Makefile.am b/helgrind/tests/Makefile.am index c594f3ba6..5f68d4094 100644 --- a/helgrind/tests/Makefile.am +++ b/helgrind/tests/Makefile.am @@ -92,10 +92,15 @@ check_PROGRAMS = \ tc18_semabuse \ tc19_shadowmem \ tc21_pthonce \ - tc22_exit_w_lock \ tc23_bogus_condwait \ tc24_nonzero_sem +# DDD: it seg faults, and then the Valgrind exit path hangs +if ! VGCONF_PLATFORMS_INCLUDE_X86_DARWIN + check_PROGRAMS += \ + tc22_exit_w_lock +endif + if HAVE_PTHREAD_BARRIER check_PROGRAMS += bar_bad bar_trivial endif diff --git a/helgrind/tests/tc20_verifywrap.c b/helgrind/tests/tc20_verifywrap.c index 667aafa44..6a8b30c37 100644 --- a/helgrind/tests/tc20_verifywrap.c +++ b/helgrind/tests/tc20_verifywrap.c @@ -18,7 +18,7 @@ #include #include -#if !defined(_AIX) +#if !defined(_AIX) && !defined(__APPLE__) #if !defined(__GLIBC_PREREQ) # error "This program needs __GLIBC_PREREQ (in /usr/include/features.h)" diff --git a/include/pub_tool_basics.h b/include/pub_tool_basics.h index cda74ab61..734391c1d 100644 --- a/include/pub_tool_basics.h +++ b/include/pub_tool_basics.h @@ -112,7 +112,15 @@ typedef Word PtrdiffT; // 32 64 // - off_t is "used for file sizes". // At one point we were using it for memory offsets, but PtrdiffT should be // used in those cases. +// Nb: on Linux and AIX, off_t is a signed word-sized int. On Darwin it's +// always a signed 64-bit int. So we defined our own Off64T as well. +#if defined(VGO_linux) || defined(VGO_aix5) typedef Word OffT; // 32 64 +#elif defined(VGO_darwin) +typedef Long OffT; // 64 64 +#else +# error Unknown OS +#endif typedef Long Off64T; // 64 64 #if !defined(NULL) @@ -183,6 +191,19 @@ typedef Bool _isError; } SysRes; +#elif defined(VGO_darwin) +typedef + struct { + UWord _wLO; + UWord _wHI; + enum { + SysRes_MACH=40, // MACH, result is _wLO + SysRes_MDEP, // MDEP, result is _wLO + SysRes_UNIX_OK, // UNIX, success, result is _wHI:_wLO + SysRes_UNIX_ERR // UNIX, error, error is _wHI:_wLO + } _mode; + } + SysRes; #else # error "Unknown OS" #endif @@ -214,6 +235,44 @@ static inline Bool sr_EQ ( SysRes sr1, SysRes sr2 ) { # error "need to define SysRes accessors on AIX5 (copy from 3.4.1 sources)" +#elif defined(VGO_darwin) + +static inline Bool sr_isError ( SysRes sr ) { + switch (sr._mode) { + case SysRes_UNIX_ERR: return True; + default: return False; + /* should check tags properly and assert here, but we can't here */ + } +} + +static inline UWord sr_Res ( SysRes sr ) { + switch (sr._mode) { + case SysRes_MACH: + case SysRes_MDEP: + case SysRes_UNIX_OK: return sr._wLO; + default: return 0; /* should assert, but we can't here */ + } +} + +static inline UWord sr_ResHI ( SysRes sr ) { + switch (sr._mode) { + case SysRes_UNIX_OK: return sr._wHI; + default: return 0; /* should assert, but we can't here */ + } +} + +static inline UWord sr_Err ( SysRes sr ) { + switch (sr._mode) { + case SysRes_UNIX_ERR: return sr._wLO; + default: return 0; /* should assert, but we can't here */ + } +} + +static inline Bool sr_EQ ( SysRes sr1, SysRes sr2 ) { + return sr1._mode == sr2._mode + && sr1._wLO == sr2._wLO && sr1._wHI == sr2._wHI; +} + #else # error "Unknown OS" #endif diff --git a/include/pub_tool_basics_asm.h b/include/pub_tool_basics_asm.h index 201c1b5af..42a8a3159 100644 --- a/include/pub_tool_basics_asm.h +++ b/include/pub_tool_basics_asm.h @@ -48,8 +48,15 @@ #define VGAPPEND(str1,str2) str1##str2 -#define VG_(str) VGAPPEND(vgPlain_, str) -#define ML_(str) VGAPPEND(vgModuleLocal_, str) +#if defined(VGO_linux) || defined(VGO_aix5) +# define VG_(str) VGAPPEND( vgPlain_, str) +# define ML_(str) VGAPPEND( vgModuleLocal_, str) +#elif defined(VGO_darwin) +# define VG_(str) VGAPPEND(_vgPlain_, str) +# define ML_(str) VGAPPEND(_vgModuleLocal_, str) +#else +# error Unknown OS +#endif #endif /* __PUB_TOOL_BASICS_ASM_H */ diff --git a/include/pub_tool_libcbase.h b/include/pub_tool_libcbase.h index c2cab1926..046011251 100644 --- a/include/pub_tool_libcbase.h +++ b/include/pub_tool_libcbase.h @@ -37,6 +37,7 @@ extern Bool VG_(isspace) ( Char c ); extern Bool VG_(isdigit) ( Char c ); +extern Char VG_(tolower) ( Char c ); /* --------------------------------------------------------------------- Converting strings to numbers @@ -86,8 +87,11 @@ extern Char* VG_(strpbrk) ( const Char* s, const Char* accpt ); extern Char* VG_(strcpy) ( Char* dest, const Char* src ); extern Char* VG_(strncpy) ( Char* dest, const Char* src, SizeT ndest ); extern Int VG_(strcmp) ( const Char* s1, const Char* s2 ); +extern Int VG_(strcasecmp) ( const Char* s1, const Char* s2 ); extern Int VG_(strncmp) ( const Char* s1, const Char* s2, SizeT nmax ); +extern Int VG_(strncasecmp) ( const Char* s1, const Char* s2, SizeT nmax ); extern Char* VG_(strstr) ( const Char* haystack, Char* needle ); +extern Char* VG_(strcasestr) ( const Char* haystack, Char* needle ); extern Char* VG_(strchr) ( const Char* s, Char c ); extern Char* VG_(strrchr) ( const Char* s, Char c ); extern SizeT VG_(strspn) ( const Char* s, const Char* accpt ); @@ -115,6 +119,7 @@ extern Int VG_(memcmp) ( const void* s1, const void* s2, SizeT n ); #define VG_IS_4_ALIGNED(aaa_p) (0 == (((Addr)(aaa_p)) & ((Addr)0x3))) #define VG_IS_8_ALIGNED(aaa_p) (0 == (((Addr)(aaa_p)) & ((Addr)0x7))) #define VG_IS_16_ALIGNED(aaa_p) (0 == (((Addr)(aaa_p)) & ((Addr)0xf))) +#define VG_IS_32_ALIGNED(aaa_p) (0 == (((Addr)(aaa_p)) & ((Addr)0x1f))) #define VG_IS_WORD_ALIGNED(aaa_p) (0 == (((Addr)(aaa_p)) & ((Addr)(sizeof(Addr)-1)))) #define VG_IS_PAGE_ALIGNED(aaa_p) (0 == (((Addr)(aaa_p)) & ((Addr)(VKI_PAGE_SIZE-1)))) diff --git a/include/pub_tool_libcfile.h b/include/pub_tool_libcfile.h index 916b431b5..5e04c96fb 100644 --- a/include/pub_tool_libcfile.h +++ b/include/pub_tool_libcfile.h @@ -85,6 +85,9 @@ extern Int VG_(unlink) ( const Char* file_name ); extern Int VG_(readlink)( const Char* path, Char* buf, UInt bufsize ); extern Int VG_(getdents)( Int fd, struct vki_dirent *dirp, UInt count ); +extern Char* VG_(basename)( const Char* path ); +extern Char* VG_(dirname) ( const Char* path ); + /* Copy the working directory at startup into buf[0 .. size-1], or return False if buf is too small. */ extern Bool VG_(get_startup_wd) ( Char* buf, SizeT size ); diff --git a/include/pub_tool_machine.h b/include/pub_tool_machine.h index 7301d5135..d7789a3fc 100644 --- a/include/pub_tool_machine.h +++ b/include/pub_tool_machine.h @@ -68,6 +68,17 @@ # define VG_MAX_INSTR_SZB 4 # define VG_CLREQ_SZB 20 # define VG_STACK_REDZONE_SZB 288 // is this right? +#elif defined(VGP_x86_darwin) +# define VG_MIN_INSTR_SZB 1 // min length of native instruction +# define VG_MAX_INSTR_SZB 16 // max length of native instruction +# define VG_CLREQ_SZB 14 // length of a client request, may + // be larger than VG_MAX_INSTR_SZB +# define VG_STACK_REDZONE_SZB 0 // number of addressable bytes below %RSP +#elif defined(VGP_amd64_darwin) +# define VG_MIN_INSTR_SZB 1 +# define VG_MAX_INSTR_SZB 16 +# define VG_CLREQ_SZB 19 +# define VG_STACK_REDZONE_SZB 128 #else # error Unknown platform #endif diff --git a/include/pub_tool_redir.h b/include/pub_tool_redir.h index ca40bc4f5..5a97bff62 100644 --- a/include/pub_tool_redir.h +++ b/include/pub_tool_redir.h @@ -178,6 +178,8 @@ # define VG_Z_LIBC_SONAME libcZaZdaZLshrZdoZR // libc*.a(shr.o) #elif defined(VGP_ppc64_aix5) # define VG_Z_LIBC_SONAME libcZaZdaZLshrZu64ZdoZR // libc*.a(shr_64.o) +#elif defined(VGO_darwin) +# define VG_Z_LIBC_SONAME libSystemZdZaZddylib // libSystem.*.dylib #else # error "Unknown platform" #endif @@ -202,6 +204,8 @@ #if defined(VGO_linux) || defined(VGO_aix5) # define VG_Z_LIBPTHREAD_SONAME libpthreadZdsoZd0 // libpthread.so.0 +#elif defined(VGO_darwin) +# define VG_Z_LIBPTHREAD_SONAME libSystemZdZaZddylib // libSystem.*.dylib #else # error "Unknown platform" #endif @@ -215,6 +219,13 @@ #define VG_Z_LD_SO_1 ldZdsoZd1 // ld.so.1 #endif +/* --- Executable name for Darwin Mach-O linker. --- */ + +#if defined(VGO_darwin) +#define VG_Z_DYLD dyld // dyld +#endif + + #endif // __PUB_TOOL_REDIR_H /*--------------------------------------------------------------------*/ diff --git a/include/pub_tool_tooliface.h b/include/pub_tool_tooliface.h index 3a7c391b3..5b1725c2c 100644 --- a/include/pub_tool_tooliface.h +++ b/include/pub_tool_tooliface.h @@ -439,8 +439,14 @@ extern void VG_(needs_final_IR_tidy_pass) ( IRSB*(*final_tidy)(IRSB*) ); what kind of error message should be emitted. */ typedef enum { Vg_CoreStartup=1, Vg_CoreSignal, Vg_CoreSysCall, - Vg_CoreTranslate, Vg_CoreClientReq } - CorePart; + // This is for platforms where syscall args are passed on the + // stack; although pre_mem_read is the callback that will be + // called, such an arg should be treated (with respect to + // presenting information to the user) as if it was passed in a + // register, ie. like pre_reg_read. + Vg_CoreSysCallArgInMem, + Vg_CoreTranslate, Vg_CoreClientReq + } CorePart; /* Events happening in core to track. To be notified, pass a callback function to the appropriate function. To ignore an event, don't do diff --git a/include/pub_tool_vki.h b/include/pub_tool_vki.h index 75da646be..f6c9792b9 100644 --- a/include/pub_tool_vki.h +++ b/include/pub_tool_vki.h @@ -51,6 +51,8 @@ # include "vki/vki-ppc32-aix5.h" #elif defined(VGP_ppc64_aix5) # include "vki/vki-ppc64-aix5.h" +#elif defined(VGO_darwin) +# include "vki/vki-darwin.h" #else # error Unknown Plat/OS #endif diff --git a/include/pub_tool_vkiscnums.h b/include/pub_tool_vkiscnums.h index 061b36e58..adb6cc013 100644 --- a/include/pub_tool_vkiscnums.h +++ b/include/pub_tool_vkiscnums.h @@ -42,6 +42,10 @@ different for each process (in principle; in practice they rarely change). 32- and 64-bit AIX5 share a common "implementation". + On Darwin the __NR_name consts are #define'd constants which are + computed using various macros. 32- and 64-bit Darwin share a common + "implementation" also. + This file is merely a top-level "steering" file, which pulls in the correct bits for the relevant platform. You should not directly #include any file in include/vki; instead #include only this one or @@ -72,10 +76,31 @@ /* Look up the name of a syscall, using the bindings previously created by VG_(aix5_register_syscall), for the purposes of making error messages. */ +// DDD: should combine this and VG_DARWIN_SYSNO_PRINT into a single generic +// function which returns a string, something like VG_(pprint_sysno)(). extern UChar* VG_(aix5_sysno_to_sysname)( Int sysno ); #endif /* !defined(VG_IN_ASSEMBLY_SOURCE) */ +#elif defined(VGP_x86_darwin) || defined(VGP_amd64_darwin) +# include "vki/vki-scnums-darwin.h" + +// Convert a syscall number into a nice form for printing. Unix syscalls +// get positive numbers (0..400-odd), Mach traps get negative numbers +// (-10..-127). +// DDD: Machine-dependent ones get positive numbers which will overlap with +// Unix ones! So eg. both 'pthread_set_self' and 'read' are reported as +// "3". +#define VG_DARWIN_SYSNO_PRINT(sysno) \ + ((VG_DARWIN_SYSNO_CLASS(sysno) == VG_DARWIN_SYSCALL_CLASS_MACH) \ + ? -VG_DARWIN_SYSNO_INDEX(sysno) \ + : VG_DARWIN_SYSNO_INDEX(sysno) \ + ) + +/* Macros for working out which syscall a syscall number refers to. */ +#define VG_DARWIN_SYSNO_INDEX(sysno) ((sysno) & VG_DARWIN_SYSCALL_NUMBER_MASK) +#define VG_DARWIN_SYSNO_CLASS(sysno) ((sysno) >> VG_DARWIN_SYSCALL_CLASS_SHIFT) + #else # error Unknown platform #endif diff --git a/include/valgrind.h b/include/valgrind.h index 679f1591d..da2427486 100644 --- a/include/valgrind.h +++ b/include/valgrind.h @@ -92,26 +92,26 @@ #undef PLAT_ppc32_aix5 #undef PLAT_ppc64_aix5 -#if !defined(_AIX) && defined(__i386__) -# define PLAT_x86_linux 1 -#elif !defined(_AIX) && defined(__x86_64__) -# define PLAT_amd64_linux 1 -#elif !defined(_AIX) && defined(__powerpc__) && !defined(__powerpc64__) -# define PLAT_ppc32_linux 1 -#elif !defined(_AIX) && defined(__powerpc__) && defined(__powerpc64__) -# define PLAT_ppc64_linux 1 -#elif defined(_AIX) && defined(__64BIT__) + +#if defined(_AIX) && defined(__64BIT__) # define PLAT_ppc64_aix5 1 #elif defined(_AIX) && !defined(__64BIT__) # define PLAT_ppc32_aix5 1 -#endif - - +#elif defined(__APPLE__) && defined(__i386__) +# define PLAT_x86_darwin 1 +#elif defined(__APPLE__) && defined(__x86_64__) +# define PLAT_amd64_darwin 1 +#elif defined(__i386__) +# define PLAT_x86_linux 1 +#elif defined(__x86_64__) +# define PLAT_amd64_linux 1 +#elif defined(__powerpc__) && !defined(__powerpc64__) +# define PLAT_ppc32_linux 1 +#elif defined(__powerpc__) && defined(__powerpc64__) +# define PLAT_ppc64_linux 1 +#else /* If we're not compiling for our target platform, don't generate any inline asms. */ -#if !defined(PLAT_x86_linux) && !defined(PLAT_amd64_linux) \ - && !defined(PLAT_ppc32_linux) && !defined(PLAT_ppc64_linux) \ - && !defined(PLAT_ppc32_aix5) && !defined(PLAT_ppc64_aix5) # if !defined(NVALGRIND) # define NVALGRIND 1 # endif @@ -172,9 +172,9 @@ inline asm stuff to be useful. */ -/* ------------------------- x86-linux ------------------------- */ +/* ------------------------- x86-{linux,darwin} ---------------- */ -#if defined(PLAT_x86_linux) +#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) typedef struct { @@ -224,11 +224,11 @@ typedef __SPECIAL_INSTRUCTION_PREAMBLE \ /* call-noredir *%EAX */ \ "xchgl %%edx,%%edx\n\t" -#endif /* PLAT_x86_linux */ +#endif /* PLAT_x86_linux || PLAT_x86_darwin */ -/* ------------------------ amd64-linux ------------------------ */ +/* ------------------------ amd64-{linux,darwin} --------------- */ -#if defined(PLAT_amd64_linux) +#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) typedef struct { @@ -278,7 +278,7 @@ typedef __SPECIAL_INSTRUCTION_PREAMBLE \ /* call-noredir *%RAX */ \ "xchgq %%rdx,%%rdx\n\t" -#endif /* PLAT_amd64_linux */ +#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */ /* ------------------------ ppc32-linux ------------------------ */ @@ -632,9 +632,9 @@ typedef do { volatile unsigned long _junk; \ CALL_FN_W_7W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6,arg7); } while (0) -/* ------------------------- x86-linux ------------------------- */ +/* ------------------------- x86-{linux,darwin} ---------------- */ -#if defined(PLAT_x86_linux) +#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) /* These regs are trashed by the hidden call. No need to mention eax as gcc can already see that, plus causes gcc to bomb. */ @@ -1027,11 +1027,11 @@ typedef lval = (__typeof__(lval)) _res; \ } while (0) -#endif /* PLAT_x86_linux */ +#endif /* PLAT_x86_linux || PLAT_x86_darwin */ -/* ------------------------ amd64-linux ------------------------ */ +/* ------------------------ amd64-{linux,darwin} --------------- */ -#if defined(PLAT_amd64_linux) +#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) /* ARGREGS: rdi rsi rdx rcx r8 r9 (the rest on stack in R-to-L order) */ @@ -1465,7 +1465,7 @@ typedef lval = (__typeof__(lval)) _res; \ } while (0) -#endif /* PLAT_amd64_linux */ +#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */ /* ------------------------ ppc32-linux ------------------------ */ diff --git a/include/vki/Makefile.am b/include/vki/Makefile.am index 137fe1d4a..81b40e58c 100644 --- a/include/vki/Makefile.am +++ b/include/vki/Makefile.am @@ -3,6 +3,7 @@ incincdir = $(includedir)/valgrind/vki incinc_HEADERS = \ vki-linux.h \ + vki-darwin.h \ vki-posixtypes-amd64-linux.h \ vki-posixtypes-ppc32-linux.h \ vki-posixtypes-ppc64-linux.h \ @@ -14,7 +15,8 @@ incinc_HEADERS = \ vki-scnums-amd64-linux.h \ vki-scnums-ppc32-linux.h \ vki-scnums-ppc64-linux.h \ - vki-scnums-x86-linux.h + vki-scnums-x86-linux.h \ + vki-scnums-darwin.h noinst_HEADERS = \ vki-ppc32-aix5.h \ diff --git a/include/vki/vki-darwin.h b/include/vki/vki-darwin.h new file mode 100644 index 000000000..cf5e5422b --- /dev/null +++ b/include/vki/vki-darwin.h @@ -0,0 +1,1023 @@ + +/*--------------------------------------------------------------------*/ +/*--- Darwin-specific kernel interface. vki-darwin.h ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2007-2009 Apple Inc. + Greg Parker gparker@apple.com + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +/* Unlike vki-linux, this Darwin kernel interface includes system headers + directly, to avoid copyright complexity. */ + +#ifndef __VKI_DARWIN_H +#define __VKI_DARWIN_H + +#include + +#define vki_int8_t int8_t +#define vki_uint8_t uint8_t +#define vki_int16_t int16_t +#define vki_uint16_t uint16_t +#define vki_int32_t int32_t +#define vki_uint32_t uint32_t +#define vki_int64_t int64_t +#define vki_uint64_t uint64_t +#define vki_intptr_t intptr_t +#define vki_uintptr_t uintptr_t + +#include + +#define vki_dev_t dev_t +#define vki_mode_t mode_t +#define vki_ino_t ino_t +#define vki_ino64_t ino64_t +#define vki_nlink_t nlink_t +#define vki_uid_t uid_t +#define vki_gid_t gid_t +#define vki_time_t time_t +#define vki_off_t off_t +#define vki_blkcnt_t blkcnt_t +#define vki_blksize_t blksize_t +#define vki_size_t size_t +#define vki_ssize_t ssize_t +#define vki_pid_t pid_t +#define vki_socklen_t socklen_t +#define vki_suseconds_t suseconds_t +#define vki_caddr_t caddr_t +#define vki_u_long u_long +#define vki_u_short u_short +#define vki_clock_t clock_t +#define vki_u_int32_t u_int32_t +#define vki_u_int16_t u_int16_t + + +// valgrind special + +// magic mmap() flags +#define VKI_MAP_ANONYMOUS MAP_ANON // linux synonym + +// fds for mmap(MAP_ANON), displayed by vmmap +#define VM_TAG_VALGRIND VM_MAKE_TAG(239) // SkAnonV + +// page sizes +#define VKI_MAX_PAGE_SHIFT VKI_PAGE_SHIFT +#define VKI_MAX_PAGE_SIZE VKI_PAGE_SIZE + +// types +typedef uint32_t vki_u32; + +// linux-like ioctl flags +#define _VKI_IOC_DIR(x) ((x) & IOC_DIRMASK) +#define _VKI_IOC_SIZE(x) IOCPARM_LEN(x) +#define _VKI_IOC_NONE IOC_VOID /* GrP fixme correct? */ +#define _VKI_IOC_READ IOC_OUT +#define _VKI_IOC_WRITE IOC_IN + + +#include + +#define vki_malloc_zone_t malloc_zone_t + + +#include + +#define vki_timeval timeval +#define vki_timeval32 timeval32 +#define vki_timespec timespec +#define vki_itimerval itimerval +#define vki_timezone timezone + + +#include + +#define VKI_S_ISBLK(m) S_ISBLK(m) +#define VKI_S_ISCHR(m) S_ISCHR(m) +#define VKI_S_ISDIR(m) S_ISDIR(m) +#define VKI_S_ISFIFO(m) S_ISFIFO(m) +#define VKI_S_ISREG(m) S_ISREG(m) +#define VKI_S_ISLNK(m) S_ISLNK(m) +#define VKI_S_ISSOCK(m) S_ISSOCK(m) +#define VKI_S_ISWHT(m) S_ISWHT(m) +#define VKI_S_ISXATTR(m) S_ISXATTR(m) + +#define VKI_S_IRWXU S_IRWXU +#define VKI_S_IRUSR S_IRUSR +#define VKI_S_IWUSR S_IWUSR +#define VKI_S_IXUSR S_IXUSR +#define VKI_S_IRWXG S_IRWXG +#define VKI_S_IRGRP S_IRGRP +#define VKI_S_IWGRP S_IWGRP +#define VKI_S_IXGRP S_IXGRP +#define VKI_S_IRWXO S_IRWXO +#define VKI_S_IROTH S_IROTH +#define VKI_S_IWOTH S_IWOTH +#define VKI_S_IXOTH S_IXOTH +#define VKI_S_ISUID S_ISUID +#define VKI_S_ISGID S_ISGID +#define VKI_S_ISVTX S_ISVTX + +#define vki_stat stat +#define vki_stat64 stat64 + +#define st_atime st_atimespec.tv_sec +#define st_atime_nsec st_atimespec.tv_nsec +#define st_mtime st_mtimespec.tv_sec +#define st_mtime_nsec st_mtimespec.tv_nsec +#define st_ctime st_ctimespec.tv_sec +#define st_ctime_nsec st_ctimespec.tv_nsec + + +#include + +#define VKI_MAXNAMLEN MAXNAMLEN +#define vki_dirent dirent + + +#include +#define VKI_SOCK_STREAM SOCK_STREAM +#define VKI_SOCK_DGRAM SOCK_DGRAM +#define VKI_SOCK_RAW SOCK_RAW + +#define VKI_AF_UNIX AF_UNIX +#define VKI_AF_INET AF_INET +#define VKI_AF_INET6 AF_INET6 + +#define VKI_SOL_SOCKET SOL_SOCKET + +#define VKI_SO_REUSEADDR SO_REUSEADDR + +#define VKI_SO_SNDBUF SO_SNDBUF +#define VKI_SO_RCVBUF SO_RCVBUF +#define VKI_SO_SNDLOWAT SO_SNDLOWAT +#define VKI_SO_RCVLOWAT SO_RCVLOWAT +#define VKI_SO_SNDTIMEO SO_SNDTIMEO +#define VKI_SO_RCVTIMEO SO_RCVTIMEO +#define VKI_SO_ERROR SO_ERROR +#define VKI_SO_TYPE SO_TYPE +#define VKI_SO_NREAD SO_NREAD +#define VKI_SO_NKE SO_NKE +#define VKI_SO_NOSIGPIPE SO_NOSIGPIPE +#define VKI_SO_NOADDRERR SO_NOADDRERR +#define VKI_SO_NWRITE SO_NWRITE +#define VKI_SO_LINGER_SEC SO_LINGER_SEC + +#define vki_sa_family_t sa_family_t +#define vki_sockaddr sockaddr +#define vki_iovec iovec +#define vki_msghdr msghdr +#define vki_cmsghdr cmsghdr + + +#define VKI_CMSG_ALIGN(a) ALIGN(a) +#define VKI_CMSG_DATA(cmsg) CMSG_DATA(cmsg) +#define VKI_CMSG_FIRSTHDR(mhdr) CMSG_FIRSTHDR(mhdr) +#define VKI_CMSG_NXTHDR(mhdr, cmsg) CMSG_NXTHDR(mhdr, cmsg) + +#define VKI_SCM_RIGHTS SCM_RIGHTS +#define VKI_SCM_TIMESTAMP SCM_TIMESTAMP +#define VKI_SCM_CREDS SCM_CREDS + + +#include + +#define vki_sockaddr_un sockaddr_un + + +#include + +#define vki_in_addr_t in_addr_t +#define vki_in_port_t in_port_t +#define vki_in_addr in_addr +#define vki_sockaddr_in sockaddr_in + +#define VKI_INADDR_LOOPBACK INADDR_LOOPBACK + + +// #include + +#define vki_in6_addr in6_addr +#define vki_sockaddr_in6 sockaddr_in6 + + +#include + +#define VKI_IFNAMSIZ IFNAMSIZ + +#define vki_ifdevmtu ifdevmtu +#define vki_ifreq ifreq +#define vki_ifr_name ifr_name +#define vki_ifr_addr ifr_addr +#define vki_ifr_dstaddr ifr_dstaddr +#define vki_ifr_broadaddr ifr_broadaddr +#define vki_ifr_flags ifr_flags +#define vki_ifr_metric ifr_metric +#define vki_ifr_mtu ifr_mtu +#define vki_ifr_phys ifr_phys +#define vki_ifr_media ifr_media +#define vki_ifr_data ifr_data +#define vki_ifr_devmtu ifr_devmtu +#define vki_ifr_intval ifr_intval + +#define vki_ifconf ifconf +#define vki_ifc_buf ifc_buf +#define vki_ifc_req ifc_req + + +#include + +#define VKI_SEEK_SET SEEK_SET +#define VKI_SEEK_CUR SEEK_CUR +#define VKI_SEEK_END SEEK_END + +#define VKI_O_RDONLY O_RDONLY +#define VKI_O_WRONLY O_WRONLY +#define VKI_O_RDWR O_RDWR +#define VKI_O_ACCMODE O_ACCMODE +#define VKI_O_NONBLOCK O_NONBLOCK +#define VKI_O_APPEND O_APPEND +#define VKI_O_SYNC O_SYN +#define VKI_O_SHLOCK O_SHLOCK +#define VKI_O_EXLOCK O_EXLOCK +#define VKI_O_ASYNC O_ASYNC +#define VKI_O_NOFOLLOW O_NOFOLLOW +#define VKI_O_CREAT O_CREAT +#define VKI_O_TRUNC O_TRUNC +#define VKI_O_EXCL O_EXCL +#define VKI_O_EVTONLY O_EVTONLY + +#define VKI_F_DUPFD F_DUPFD +#define VKI_F_GETFD F_GETFD +#define VKI_F_SETFD F_SETFD +#define VKI_F_GETFL F_GETFL +#define VKI_F_SETFL F_SETFL +#define VKI_F_GETOWN F_GETOWN +#define VKI_F_SETOWN F_SETOWN +#define VKI_F_GETLK F_GETLK +#define VKI_F_SETLK F_SETLK +#define VKI_F_SETLKW F_SETLKW + +#define VKI_F_CHKCLEAN F_CHKCLEAN +#define VKI_F_PREALLOCATE F_PREALLOCATE +#define VKI_F_SETSIZE F_SETSIZE +#define VKI_F_RDADVISE F_RDADVISE +#define VKI_F_RDAHEAD F_RDAHEAD +#define VKI_F_READBOOTSTRAP F_READBOOTSTRAP +#define VKI_F_WRITEBOOTSTRAP F_WRITEBOOTSTRAP +#define VKI_F_NOCACHE F_NOCACHE +#define VKI_F_LOG2PHYS F_LOG2PHYS +#define VKI_F_GETPATH F_GETPATH +#define VKI_F_ADDSIGS F_ADDSIGS +#define VKI_F_FULLFSYNC F_FULLFSYNC +#define VKI_F_PATHPKG_CHECK F_PATHPKG_CHECK +#define VKI_F_FREEZE_FS F_FREEZE_FS +#define VKI_F_THAW_FS F_THAW_FS +#define VKI_F_GLOBAL_NOCACHE F_GLOBAL_NOCACHE + +#define VKI_FD_CLOEXEC FD_CLOEXEC + +#define vki_radvisory radvisory +#define vki_fstore fstore +#define vki_fbootstraptransfer fbootstraptransfer +#define vki_log2phys log2phys +#define vki_fsignatures_t fsignatures_t + +// These constants aren't in a standard header, they are from the kernel code: +// xnu-1228.3.13/bsd/sys/codesign.h +// Mac OS X 10.5.6 - Darwin 9.6 +#define VKI_CS_OPS_STATUS 0 /* return status */ +#define VKI_CS_OPS_MARKINVALID 1 /* invalidate process */ +#define VKI_CS_OPS_MARKHARD 2 /* set HARD flag */ +#define VKI_CS_OPS_MARKKILL 3 /* set KILL flag (sticky) */ +#define VKI_CS_OPS_PIDPATH 4 /* get executable's pathname */ +#define VKI_CS_OPS_CDHASH 5 /* get code directory hash */ + +#include + +#define VKI_PROT_NONE PROT_NONE +#define VKI_PROT_READ PROT_READ +#define VKI_PROT_WRITE PROT_WRITE +#define VKI_PROT_EXEC PROT_EXEC + +#define VKI_MAP_SHARED MAP_SHARED +#define VKI_MAP_PRIVATE MAP_PRIVATE +#define VKI_MAP_FIXED MAP_FIXED +#define VKI_MAP_RENAME MAP_RENAME +#define VKI_MAP_NORESERVE MAP_NORESERVE +#define VKI_MAP_RESERVED0080 MAP_RESERVED0080 +#define VKI_MAP_NOEXTEND MAP_NOEXTEND +#define VKI_MAP_HASSEMAPHORE MAP_HASSEMAPHORE +#define VKI_MAP_FILE MAP_FILE +#define VKI_MAP_ANON MAP_ANON +#define VKI_MAP_FAILED MAP_FAILED + + +#include + +#define VKI_PAGE_SHIFT PAGE_SHIFT +#define VKI_PAGE_SIZE PAGE_SIZE +#define VKI_PAGE_MASK PAGE_MASK + + +#include + +#define VKI_USRSTACK USRSTACK +#define VKI_USRSTACK64 USRSTACK64 + + +#include + +#define vki_mach_timebase_info mach_timebase_info + + +#include + +#define VKI_PATH_MAX PATH_MAX + + +#include + +#define VKI_MAXPATHLEN MAXPATHLEN + + +#include + +/* While we fully intend to make 'vki_sigset_t' match the native + Darwin 'sigset_t', we can't just clone the Darwin sigset_t type, + because it isn't an array, and the VG_(sigfillset) etc functions + assume it is. So instead define another isomorphic type, and check + in VG_(vki_do_initial_consistency_checks) that it really is + correct. */ +/* #define vki_sigset_t sigset_t */ +#define _VKI_NSIG_BPW 32 +#define _VKI_NSIG 32 +#define _VKI_NSIG_WORDS (_VKI_NSIG / _VKI_NSIG_BPW) +typedef struct { + UInt sig[_VKI_NSIG_WORDS]; +} vki_sigset_t; +/* and now let VG_(vki_do_initial_consistency_checks) make sure it + matches 'sigset_t'. */ + + +#define VKI_SS_ONSTACK SS_ONSTACK +#define VKI_SS_DISABLE SS_DISABLE +#define VKI_MINSIGSTKSZ MINSIGSTKSZ +#define VKI_SIGSTKSZ SIGSTKSZ + +#define vki_stack_t stack_t +#define vki_siginfo_t siginfo_t + +/* There are two versions of this. 'struct __sigaction' is used for + passing sigactions to the kernel interface, and has the added + complexity of requiring an extra pointer to a new demultiplexing + function to be run in user space. 'struct sigaction' is used for + receiving old sigactions from the kernel, and lacks this + demux-function pointer. So the type of the second and third + parameters in Darwin's sys_sigaction appear to be different, + respectively 'struct __sigaction*' and 'struct sigaction*'. +*/ +//#define vki_sigaction __sigaction +//#define vki_user_sigaction sigaction +//#define vki_sigaltstack sigaltstack +//#define vki_sigval sigval +//#define vki_sigaction_u sigaction_u +//#define vki_sigaction sigaction + +//typedef struct __sigaction vki_sigaction_toK_t; +//typedef struct sigaction vki_sigaction_fromK_t; + +typedef + struct { + void* ksa_handler; + void (*sa_tramp)(void*,UWord,UWord,void*,void*); + vki_sigset_t sa_mask; + int sa_flags; + } + vki_sigaction_toK_t; + +typedef + struct { + void* ksa_handler; + vki_sigset_t sa_mask; + int sa_flags; + } + vki_sigaction_fromK_t; + + + +/* and /usr/include/sys/signal.c in turn defines 'sa_handler' to + be '__sigaction_u.__sa_handler' */ +//#define ksa_handler sa_handler + +//#define vki_sa_sigaction sa_sigaction + +#define VKI_SA_ONSTACK SA_ONSTACK +#define VKI_SA_RESTART SA_RESTART +#define VKI_SA_DISABLE SA_DISABLE +#define VKI_SA_RESETHAND SA_RESETHAND +#define VKI_SA_NOCLDSTOP SA_NOCLDSTOP +#define VKI_SA_NODEFER SA_NODEFER +#define VKI_SA_NOCLDWAIT SA_NOCLDWAIT +#define VKI_SA_SIGINFO SA_SIGINFO +#define VKI_SA_USERTRAMP SA_USERTRAMP +#define VKI_SA_64REGSET SA_64REGSET +#define VKI_SA_RESTORER 0 /* Darwin doesn't have this */ + +#define VKI_SIG_BLOCK SIG_BLOCK +#define VKI_SIG_UNBLOCK SIG_UNBLOCK +#define VKI_SIG_SETMASK SIG_SETMASK + +#define VKI_SIGHUP SIGHUP +#define VKI_SIGINT SIGINT +#define VKI_SIGQUIT SIGQUIT +#define VKI_SIGILL SIGILL +#define VKI_SIGTRAP SIGTRAP +#define VKI_SIGABRT SIGABRT +#define VKI_SIGPOLL SIGPOLL +#define VKI_SIGFPE SIGFPE +#define VKI_SIGKILL SIGKILL +#define VKI_SIGBUS SIGBUS +#define VKI_SIGSEGV SIGSEGV +#define VKI_SIGSYS SIGSYS +#define VKI_SIGPIPE SIGPIPE +#define VKI_SIGALRM SIGALRM +#define VKI_SIGTERM SIGTERM +#define VKI_SIGURG SIGURG +#define VKI_SIGSTOP SIGSTOP +#define VKI_SIGTSTP SIGTSTP +#define VKI_SIGCONT SIGCONT +#define VKI_SIGCHLD SIGCHLD +#define VKI_SIGTTIN SIGTTIN +#define VKI_SIGTTOU SIGTTOU +#define VKI_SIGIO SIGIO +#define VKI_SIGXCPU SIGXCPU +#define VKI_SIGXFSZ SIGXFSZ +#define VKI_SIGVTALRM SIGVTALRM +#define VKI_SIGPROF SIGPROF +#define VKI_SIGWINCH SIGWINCH +#define VKI_SIGINFO SIGINFO +#define VKI_SIGUSR1 SIGUSR1 +#define VKI_SIGUSR2 SIGUSR2 + +#define VKI_SIG_DFL SIG_DFL +#define VKI_SIG_IGN SIG_IGN + + +#define VKI_SI_USER SI_USER +#define VKI_SEGV_MAPERR SEGV_MAPERR +#define VKI_SEGV_ACCERR SEGV_ACCERR +#define VKI_ILL_ILLOPC ILL_ILLOPC +#define VKI_ILL_ILLOPN ILL_ILLOPN +#define VKI_ILL_ILLADR ILL_ILLADR +#define VKI_ILL_ILLTRP ILL_ILLTRP +#define VKI_ILL_PRVOPC ILL_PRVOPC +#define VKI_ILL_PRVREG ILL_PRVREG +#define VKI_ILL_COPROC ILL_COPROC +#define VKI_ILL_BADSTK ILL_BADSTK +#define VKI_FPE_INTDIV FPE_INTDIV +#define VKI_FPE_INTOVF FPE_INTOVF +#define VKI_FPE_FLTDIV FPE_FLTDIV +#define VKI_FPE_FLTOVF FPE_FLTOVF +#define VKI_FPE_FLTUND FPE_FLTUND +#define VKI_FPE_FLTRES FPE_FLTRES +#define VKI_FPE_FLTINV FPE_FLTINV +#define VKI_FPE_FLTSUB FPE_FLTSUB +#define VKI_BUS_ADRALN BUS_ADRALN +#define VKI_BUS_ADRERR BUS_ADRERR +#define VKI_BUS_OBJERR BUS_OBJERR +#define VKI_TRAP_BRKPT TRAP_BRKPT + +/* JRS: not 100% sure, but I think these two are correct */ +#define VKI_SA_ONESHOT SA_RESETHAND +#define VKI_SA_NOMASK SA_NODEFER + +#define VKI_UC_SET_ALT_STACK 0x40000000 +#define VKI_UC_RESET_ALT_STACK 0x80000000 + + +#include + +#define VKI_EPERM EPERM +#define VKI_ENOENT ENOENT +#define VKI_ESRCH ESRCH +#define VKI_EINTR EINTR +#define VKI_EIO EIO +#define VKI_ENXIO ENXIO +#define VKI_E2BIG E2BIG +#define VKI_ENOEXEC ENOEXEC +#define VKI_EBADF EBADF +#define VKI_ECHILD ECHILD +#define VKI_EDEADLK EDEADLK +#define VKI_ENOMEM ENOMEM +#define VKI_EACCES EACCES +#define VKI_EFAULT EFAULT +#define VKI_ENOTBLK ENOTBLK +#define VKI_EBUSY EBUSY +#define VKI_EEXIST EEXIST +#define VKI_EXDEV EXDEV +#define VKI_ENODEV ENODEV +#define VKI_ENOTDIR ENOTDIR +#define VKI_EISDIR EISDIR +#define VKI_EINVAL EINVAL +#define VKI_ENFILE ENFILE +#define VKI_EMFILE EMFILE +#define VKI_ENOTTY ENOTTY +#define VKI_ETXTBSY ETXTBSY +#define VKI_EFBIG EFBIG +#define VKI_ENOSPC ENOSPC +#define VKI_ESPIPE ESPIPE +#define VKI_EROFS EROFS +#define VKI_EMLINK EMLINK +#define VKI_EPIPE EPIPE +#define VKI_EDOM EDOM +#define VKI_ERANGE ERANGE +#define VKI_EAGAIN EAGAIN +#define VKI_EWOULDBLOCK EAGAIN +#define VKI_EINPROGRESS EINPROGRESS +#define VKI_EALREADY EALREADY +#define VKI_ENOTSOCK ENOTSOCK +#define VKI_EDESTADDRREQ EDESTADDRREQ +#define VKI_EMSGSIZE EMSGSIZE +#define VKI_EPROTOTYPE EPROTOTYPE +#define VKI_ENOPROTOOPT ENOPROTOOPT +#define VKI_EPROTONOSUPPORT EPROTONOSUPPORT +#define VKI_ESOCKTNOSUPPORT ESOCKTNOSUPPORT +#define VKI_ENOTSUP ENOTSUP +#define VKI_EPFNOSUPPORT EPFNOSUPPORT +#define VKI_EAFNOSUPPORT EAFNOSUPPORT +#define VKI_EADDRINUSE EADDRINUSE +#define VKI_EADDRNOTAVAIL EADDRNOTAVAIL +#define VKI_ENETDOWN ENETDOWN +#define VKI_ENETUNREACH ENETUNREACH +#define VKI_ENETRESET ENETRESET +#define VKI_ECONNABORTED ECONNABORTED +#define VKI_ECONNRESET ECONNRESET +#define VKI_ENOBUFS ENOBUFS +#define VKI_EISCONN EISCONN +#define VKI_ENOTCONN ENOTCONN +#define VKI_ESHUTDOWN ESHUTDOWN +#define VKI_ETOOMANYREFS ETOOMANYREFS +#define VKI_ETIMEDOUT ETIMEDOUT +#define VKI_ECONNREFUSED ECONNREFUSED +#define VKI_ELOOP ELOOP +#define VKI_ENAMETOOLONG ENAMETOOLONG +#define VKI_EHOSTDOWN EHOSTDOWN +#define VKI_EHOSTUNREACH EHOSTUNREACH +#define VKI_ENOTEMPTY ENOTEMPTY +#define VKI_EPROCLIM EPROCLIM +#define VKI_EUSERS EUSERS +#define VKI_EDQUOT EDQUOT +#define VKI_ESTALE ESTALE +#define VKI_EREMOTE EREMOTE +#define VKI_EBADRPC EBADRPC +#define VKI_ERPCMISMATCH ERPCMISMATCH +#define VKI_EPROGUNAVAIL EPROGUNAVAIL +#define VKI_EPROGMISMATCH EPROGMISMATCH +#define VKI_EPROCUNAVAIL EPROCUNAVAIL +#define VKI_ENOLCK ENOLCK +#define VKI_ENOSYS ENOSYS +#define VKI_EFTYPE EFTYPE +#define VKI_EAUTH EAUTH +#define VKI_ENEEDAUTH ENEEDAUTH +#define VKI_EPWROFF EPWROFF +#define VKI_EDEVERR EDEVERR +#define VKI_EOVERFLOW EOVERFLOW +#define VKI_EBADEXEC EBADEXEC +#define VKI_EBADARCH EBADARCH +#define VKI_ESHLIBVERS ESHLIBVERS +#define VKI_EBADMACHO EBADMACHO +#define VKI_ECANCELED ECANCELED +#define VKI_EIDRM EIDRM +#define VKI_ENOMSG ENOMSG +#define VKI_EILSEQ EILSEQ +#define VKI_ENOATTR ENOATTR +#define VKI_EBADMSG EBADMSG +#define VKI_EMULTIHOP EMULTIHOP +#define VKI_ENODATA ENODATA +#define VKI_ENOLINK ENOLINK +#define VKI_ENOSR ENOSR +#define VKI_ENOSTR ENOSTR +#define VKI_EPROTO EPROTO +#define VKI_ETIME ETIME +#define VKI_EOPNOTSUPP EOPNOTSUPP +#define VKI_ELAST ELAST + + +#include + +#define VKI_RLIMIT_CPU RLIMIT_CPU +#define VKI_RLIMIT_FSIZE RLIMIT_FSIZE +#define VKI_RLIMIT_DATA RLIMIT_DATA +#define VKI_RLIMIT_STACK RLIMIT_STACK +#define VKI_RLIMIT_CORE RLIMIT_CORE +#define VKI_RLIMIT_AS RLIMIT_AD +#define VKI_RLIMIT_RSS RLIMIT_AS +#define VKI_RLIMIT_MEMLOCK RLIMIT_MEMLOCK +#define VKI_RLIMIT_NPROC RLIMIT_NPROC +#define VKI_RLIMIT_NOFILE RLIMIT_NOFILE +#define VKI_RLIM_NLIMITS RLIM_NLIMITS + +#define vki_rlim_t rlim_t +#define vki_rlimit rlimit +#define vki_rusage rusage + + +#include + +#define vki_pollfd pollfd + + +#include + +#define VKI_IPC_RMID IPC_RMID +#define VKI_IPC_SET IPC_SET +#define VKI_IPC_STAT IPC_STAT + +#define vki_key_t key_t +#define vki_ipc_perm ipc_perm + + +#include + +#define VKI_GETNCNT GETNCNT +#define VKI_GETPID GETPID +#define VKI_GETVAL GETVAL +#define VKI_GETALL GETALL +#define VKI_GETZCNT GETZCNT +#define VKI_SETVAL SETVAL +#define VKI_SETALL SETALL + +#define vki_sembuf sembuf +#define vki_semid_ds semid_ds +#define vki_semun semun + + +#include + +#define vki_sem_t sem_t + + +#include + +#define VKI_MFSNAMELEN MFSNAMELEN +#define VKI_MNAMELEN MNAMELEN + +#define vki_fsid fsid +#define vki_fsid_t fsid_t +#define vki_statfs statfs +#define vki_statfs64 statfs64 + + +#include + +#define vki_fd_set fd_set + + +#include + +#define VKI_MSG_BSIZE MSG_BSIZE +#define VKI_MSG_MAGIC MSG_MAGIC +#define vki_msgbuf msgbuf + + +#include + +#define VKI_SHM_RDONLY SHM_RDONLY +#define VKI_SHM_RND SHM_RND + +#define vki_shmid_ds shmid_ds + + +#include + +#define vki_tms tms + + +#include + +#define _VKI_SYS_NAMELEN _SYS_NAMELEN +#define vki_new_utsname utsname + + +#include + +#define VKI_F_OK F_OK +#define VKI_X_OK X_OK +#define VKI_W_OK W_OK +#define VKI_R_OK R_OK + + +#include + +#define VKI_CTL_MAXNAME CTL_MAXNAME + +#define VKI_CTL_UNSPEC CTL_UNSPEC +#define VKI_CTL_KERN CTL_KERN +#define VKI_CTL_VM CTL_VM +#define VKI_CTL_VFS CTL_VFS +#define VKI_CTL_NET CTL_NET +#define VKI_CTL_DEBUG CTL_DEBUG +#define VKI_CTL_HW CTL_HW +#define VKI_CTL_MACHDEP CTL_MACHDEP +#define VKI_CTL_USER CTL_USER +#define VKI_CTL_MAXID CTL_MAXID + +#define VKI_HW_MACHINE HW_MACHINE +#define VKI_HW_MODEL HW_MODEL +#define VKI_HW_NCPU HW_NCPU +#define VKI_HW_BYTEORDER HW_BYTEORDER +#define VKI_HW_PHYSMEM HW_PHYSMEM +#define VKI_HW_USERMEM HW_USERMEM +#define VKI_HW_PAGESIZE HW_PAGESIZE +#define VKI_HW_DISKNAMES HW_DISKNAMES +#define VKI_HW_DISKSTATS HW_DISKSTATS +#define VKI_HW_EPOCH HW_EPOCH +#define VKI_HW_FLOATINGPT HW_FLOATINGPT +#define VKI_HW_MACHINE_ARCH HW_MACHINE_ARCH +#define VKI_HW_VECTORUNIT HW_VECTORUNIT +#define VKI_HW_BUS_FREQ HW_BUS_FREQ +#define VKI_HW_CPU_FREQ HW_CPU_FREQ +#define VKI_HW_CACHELINE HW_CACHELINE +#define VKI_HW_L1ICACHESIZE HW_L1ICACHESIZE +#define VKI_HW_L1DCACHESIZE HW_L1DCACHESIZE +#define VKI_HW_L2SETTINGS HW_L2SETTINGS +#define VKI_HW_L2CACHESIZE HW_L2CACHESIZE +#define VKI_HW_L3SETTINGS HW_L3SETTINGS +#define VKI_HW_L3CACHESIZE HW_L3CACHESIZE +#define VKI_HW_TB_FREQ HW_TB_FREQ +#define VKI_HW_MEMSIZE HW_MEMSIZE +#define VKI_HW_AVAILCPU MW_AVAILCPU +#define VKI_HW_MAXID MW_MAXID + +#define VKI_KERN_USRSTACK32 KERN_USRSTACK32 +#define VKI_KERN_USRSTACK64 KERN_USRSTACK64 + + +#include + +#define vki_attrlist attrlist + + +#include + +#define vki_kevent kevent + + +#include + +typedef struct eventreq vki_eventreq; + + +#include + +#define VKI_PTRACE_TRACEME PT_TRACE_ME +#define VKI_PTRACE_DETACH PT_DETACH + + +// sqlite/src/os_unix.c + +struct ByteRangeLockPB2 +{ + unsigned long long offset; /* offset to first byte to lock */ + unsigned long long length; /* nbr of bytes to lock */ + unsigned long long retRangeStart; /* nbr of 1st byte locked if successful */ + unsigned char unLockFlag; /* 1 = unlock, 0 = lock */ + unsigned char startEndFlag; /* 1=rel to end of fork, 0=rel to start */ + int fd; /* file desc to assoc this lock with */ +}; + +#define afpfsByteRangeLock2FSCTL _IOWR('z', 23, struct ByteRangeLockPB2) + +#define vki_ByteRangeLockPB2 ByteRangeLockPB2 +#define VKI_afpfsByteRangeLock2FSCTL afpfsByteRangeLock2FSCTL + + +// xnu/bsd/sys/fsctl.h + +#define VKI_FSIOC_SYNC_VOLUME _IOW('A', 1, uint32_t) + + +// Libc/pthreads/pthread.c + +#define VKI_WQOPS_QUEUE_ADD 1 +#define VKI_WQOPS_QUEUE_REMOVE 2 +#define VKI_WQOPS_THREAD_RETURN 4 + + +#include + +#define vki_winsize winsize + +#define VKI_TIOCMODG TIOCMODG +#define VKI_TIOCMODS TIOCMODS +#define VKI_TIOCEXCL TIOCEXCL +#define VKI_TIOCNXCL TIOCNXCL +#define VKI_TIOCFLUSH TIOCFLUSH +#define VKI_TIOCGETA TIOCGETA +#define VKI_TIOCSETA TIOCSETA +#define VKI_TIOCSETAW TIOCSETAW +#define VKI_TIOCSETAF TIOCSETAF +#define VKI_TIOCGETD TIOCGETD +#define VKI_TIOCSETD TIOCSETD +#define VKI_TIOCSBRK TIOCSBRK +#define VKI_TIOCCBRK TIOCCBRK +#define VKI_TIOCSDTR TIOCSDTR +#define VKI_TIOCCDTR TIOCCDTR +#define VKI_TIOCGPGRP TIOCGPGRP +#define VKI_TIOCSPGRP TIOCSPGRP +#define VKI_TIOCOUTQ TIOCOUTQ +#define VKI_TIOCSTI TIOCSTI +#define VKI_TIOCNOTTY TIOCNOTTY +#define VKI_TIOCPKT TIOCPKT +#define VKI_TIOCSTOP TIOCSTOP +#define VKI_TIOCSTART TIOCSTART +#define VKI_TIOCMSET TIOCMSET +#define VKI_TIOCMBIS TIOCMBIS +#define VKI_TIOCMBIC TIOCMBIC +#define VKI_TIOCMGET TIOCMGET +#define VKI_TIOCREMOTE TIOCREMOTE +#define VKI_TIOCGWINSZ TIOCGWINSZ +#define VKI_TIOCSWINSZ TIOCSWINSZ +#define VKI_TIOCUCNTL TIOCUCNTL +#define VKI_TIOCSTAT TIOCSTAT +#define VKI_UIOCCMD(n) UIOCCMD(n) +#define VKI_TIOCSCONS TIOCSCONS +#define VKI_TIOCCONS TIOCCONS +#define VKI_TIOCSCTTY TIOCSCTTY +#define VKI_TIOCEXT TIOCEXT +#define VKI_TIOCSIG TIOCSIG +#define VKI_TIOCDRAIN TIOCDRAIN +#define VKI_TIOCMSDTRWAIT TIOCMSDTRWAIT +#define VKI_TIOCMGDTRWAIT TIOCMGDTRWAIT +#define VKI_TIOCTIMESTAMP TIOCTIMESTAMP +#define VKI_TIOCDCDTIMESTAMP TIOCDCDTIMESTAMP +#define VKI_TIOCSDRAINWAIT TIOCSDRAINWAIT +#define VKI_TIOCGDRAINWAIT TIOCGDRAINWAIT +#define VKI_TIOCDSIMICROCODE TIOCDSIMICROCODE +#define VKI_TIOCPTYGRANT TIOCPTYGRANT +#define VKI_TIOCPTYGNAME TIOCPTYGNAME +#define VKI_TIOCPTYUNLK TIOCPTYUNLK + + +#include + +#define VKI_FIOCLEX FIOCLEX +#define VKI_FIONCLEX FIONCLEX +#define VKI_FIONREAD FIONREAD +#define VKI_FIONBIO FIONBIO +#define VKI_FIOASYNC FIOASYNC +#define VKI_FIOSETOWN FIOSETOWN +#define VKI_FIOGETOWN FIOGETOWN +#define VKI_FIODTYPE FIODTYPE + + +#include + +#define VKI_SIOCSHIWAT SIOCSHIWAT +#define VKI_SIOCGHIWAT SIOCGHIWAT +#define VKI_SIOCSLOWAT SIOCSLOWAT +#define VKI_SIOCGLOWAT SIOCGLOWAT +#define VKI_SIOCATMARK SIOCATMARK +#define VKI_SIOCSPGRP SIOCSPGRP +#define VKI_SIOCGPGRP SIOCGPGRP + +#define VKI_SIOCSIFADDR SIOCSIFADDR +#define VKI_OSIOCGIFADDR OSIOCGIFADDR +#define VKI_SIOCSIFDSTADDR SIOCSIFDSTADDR +#define VKI_OSIOCGIFDSTADDR OSIOCGIFDSTADDR +#define VKI_SIOCSIFFLAGS SIOCSIFFLAGS +#define VKI_SIOCGIFFLAGS SIOCGIFFLAGS +#define VKI_OSIOCGIFBRDADDR OSIOCGIFBRDADDR +#define VKI_SIOCSIFBRDADDR SIOCSIFBRDADDR +#define VKI_OSIOCGIFCONF OSIOCGIFCONF +#define VKI_OSIOCGIFNETMASK OSIOCGIFNETMASK +#define VKI_SIOCSIFNETMASK SIOCSIFNETMASK +#define VKI_SIOCGIFMETRIC SIOCGIFMETRIC +#define VKI_SIOCSIFMETRIC SIOCSIFMETRIC +#define VKI_SIOCDIFADDR SIOCDIFADDR +#define VKI_SIOCAIFADDR SIOCAIFADDR +#define VKI_SIOCGETVIFCNT SIOCGETVIFCNT +#define VKI_SIOCGETSGCNT SIOCGETSGCNT +#define VKI_SIOCALIFADDR SIOCALIFADDR +#define VKI_SIOCGLIFADDR SIOCGLIFADDR +#define VKI_SIOCDLIFADDR SIOCDLIFADDR + +#define VKI_SIOCGIFADDR SIOCGIFADDR +#define VKI_SIOCGIFDSTADDR SIOCGIFDSTADDR +#define VKI_SIOCGIFBRDADDR SIOCGIFBRDADDR +#define VKI_SIOCGIFCONF SIOCGIFCONF +#define VKI_SIOCGIFNETMASK SIOCGIFNETMASK +#define VKI_SIOCAUTOADDR SIOCAUTOADDR +#define VKI_SIOCAUTONETMASK SIOCAUTONETMASK +#define VKI_SIOCARPIPLL SIOCARPIPLL + +#define VKI_SIOCADDMULTI SIOCADDMULTI +#define VKI_SIOCDELMULTI SIOCDELMULTI +#define VKI_SIOCGIFMTU SIOCGIFMTU +#define VKI_SIOCSIFMTU SIOCSIFMTU +#define VKI_SIOCGIFPHYS SIOCGIFPHYS +#define VKI_SIOCSIFPHYS SIOCSIFPHYS +#define VKI_SIOCSIFMEDIA SIOCSIFMEDIA +#define VKI_SIOCGIFMEDIA SIOCGIFMEDIA +#define VKI_SIOCSIFGENERIC SIOCSIFGENERIC +#define VKI_SIOCGIFGENERIC SIOCGIFGENERIC +#define VKI_SIOCRSLVMULTI SIOCRSLVMULTI + +#define VKI_SIOCSIFLLADDR SIOCSIFLLADDR +#define VKI_SIOCGIFSTATUS SIOCGIFSTATUS +#define VKI_SIOCSIFPHYADDR SIOCSIFPHYADDR +#define VKI_SIOCGIFPSRCADDR SIOCGIFPSRCADDR +#define VKI_SIOCGIFPDSTADDR SIOCGIFPDSTADDR +#define VKI_SIOCDIFPHYADDR SIOCDIFPHYADDR +#define VKI_SIOCSLIFPHYADDR SIOCSLIFPHYADDR +#define VKI_SIOCGLIFPHYADDR SIOCGLIFPHYADDR + +#define VKI_SIOCGIFDEVMTU SIOCGIFDEVMTU +#define VKI_SIOCSIFALTMTU SIOCSIFALTMTU +#define VKI_SIOCGIFALTMTU SIOCGIFALTMTU +#define VKI_SIOCSIFBOND SIOCSIFBOND +#define VKI_SIOCGIFBOND SIOCGIFBOND +#define VKI_SIOCIFCREATE SIOCIFCREATE +#define VKI_SIOCIFDESTROY SIOCIFDESTROY +#define VKI_SIOCSIFVLAN SIOCSIFVLAN +#define VKI_SIOCGIFVLAN SIOCGIFVLAN + +#define VKI_SIOCSETVLAN SIOCSIFVLAN +#define VKI_SIOCGETVLAN SIOCGIFVLAN + +#define VKI_SIOCGIFASYNCMAP SIOCGIFASYNCMAP +#define VKI_SIOCSIFASYNCMAP SIOCSIGASYNCMAP + + +#include + +#define VKI_DTRACEHIOC_REMOVE DTRACEHIOC_REMOVE +#define VKI_DTRACEHIOC_ADDDOF DTRACEHIOC_ADDDOF + + +#include + +/* quite why sys/ucontext.h provides a 'struct __darwin_ucontext' + but no 'struct ucontext' beats me. -- JRS */ +#define vki_ucontext __darwin_ucontext + + +#include + +#define vki_termios termios + + +#include + +#define vki_uuid_t uuid_t + + +#include + +#define VKI_A_GETPOLICY A_GETPOLICY +#define VKI_A_SETPOLICY A_SETPOLICY +#define VKI_A_GETKMASK A_GETKMASK +#define VKI_A_SETKMASK A_SETKMASK +#define VKI_A_GETQCTRL A_GETQCTRL +#define VKI_A_SETQCTRL A_SETQCTRL +#define VKI_A_GETCWD A_GETCWD +#define VKI_A_GETCAR A_GETCAR +#define VKI_A_GETSTAT A_GETSTAT +#define VKI_A_SETSTAT A_SETSTAT +#define VKI_A_SETUMASK A_SETUMASK +#define VKI_A_SETSMASK A_SETSMASK +#define VKI_A_GETCOND A_GETCOND +#define VKI_A_SETCOND A_SETCOND +#define VKI_A_GETCLASS A_GETCLASS +#define VKI_A_SETCLASS A_SETCLASS +#define VKI_A_GETPINFO A_GETPINFO +#define VKI_A_SETPMASK A_SETPMASK +#define VKI_A_SETFSIZE A_SETFSIZE +#define VKI_A_GETFSIZE A_GETFSIZE +#define VKI_A_GETPINFO_ADDR A_GETPINFO_ADDR +#define VKI_A_GETKAUDIT A_GETKAUDIT +#define VKI_A_SETKAUDIT A_SETKAUDIT + + +#endif diff --git a/include/vki/vki-scnums-darwin.h b/include/vki/vki-scnums-darwin.h new file mode 100644 index 000000000..281ccc30c --- /dev/null +++ b/include/vki/vki-scnums-darwin.h @@ -0,0 +1,580 @@ + +/*--------------------------------------------------------------------*/ +/*--- System call numbers for Darwin. vki-scnums-darwin.h ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2007-2009 Apple Inc. + Greg Parker gparker@apple.com + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#ifndef __VKI_SCNUMS_DARWIN_H +#define __VKI_SCNUMS_DARWIN_H + + +// osfmk/mach/i386/syscall_sw.h + +// x86_64's syscall numbering system is used for all architectures. +// Don't pass __NR_something directly to any syscall instruction. +// Hack: x86 `int $0x80` (unix, 64-bit result) are special. + +// Encoding: the top 8-bits are the syscall class. The low 24 are the +// syscall number (index) within that class. + +#define VG_DARWIN_SYSCALL_CLASS_SHIFT 24 +#define VG_DARWIN_SYSCALL_CLASS_MASK (0xFF << VG_DARWIN_SYSCALL_CLASS_SHIFT) +#define VG_DARWIN_SYSCALL_NUMBER_MASK (~VG_DARWIN_SYSCALL_CLASS_MASK) + +#define VG_DARWIN_SYSCALL_CLASS_NONE 0 /* Invalid */ +#define VG_DARWIN_SYSCALL_CLASS_MACH 1 /* Mach */ +#define VG_DARWIN_SYSCALL_CLASS_UNIX 2 /* Unix/BSD */ +#define VG_DARWIN_SYSCALL_CLASS_MDEP 3 /* Machine-dependent */ +#define VG_DARWIN_SYSCALL_CLASS_DIAG 4 /* Diagnostics */ + +#define VG_DARWIN_SYSCALL_CONSTRUCT_MACH(syscall_number) \ + ((VG_DARWIN_SYSCALL_CLASS_MACH << VG_DARWIN_SYSCALL_CLASS_SHIFT) | \ + (VG_DARWIN_SYSCALL_NUMBER_MASK & (syscall_number))) + +#define VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(syscall_number) \ + ((VG_DARWIN_SYSCALL_CLASS_UNIX << VG_DARWIN_SYSCALL_CLASS_SHIFT) | \ + (VG_DARWIN_SYSCALL_NUMBER_MASK & (syscall_number))) + +#define VG_DARWIN_SYSCALL_CONSTRUCT_MDEP(syscall_number) \ + ((VG_DARWIN_SYSCALL_CLASS_MDEP << VG_DARWIN_SYSCALL_CLASS_SHIFT) | \ + (VG_DARWIN_SYSCALL_NUMBER_MASK & (syscall_number))) + +#define VG_DARWIN_SYSCALL_CONSTRUCT_DIAG(syscall_number) \ + ((VG_DARWIN_SYSCALL_CLASS_DIAG << VG_DARWIN_SYSCALL_CLASS_SHIFT) | \ + (VG_DARWIN_SYSCALL_NUMBER_MASK & (syscall_number))) + + + +// mdep syscalls + +#if defined(VGA_x86) + +// osfmk/i386/machdep_call.c +// # define __NR_thread_get_cthread_self VG_DARWIN_SYSCALL_CONSTRUCT_MDEP(0) +// # define __NR_thread_set_cthread_self VG_DARWIN_SYSCALL_CONSTRUCT_MDEP(1) +// # define __NR_2 VG_DARWIN_SYSCALL_CONSTRUCT_MDEP(2) +# define __NR_pthread_set_self VG_DARWIN_SYSCALL_CONSTRUCT_MDEP(3) +// # define __NR_thread_set_user_ldt VG_DARWIN_SYSCALL_CONSTRUCT_MDEP(4) +// # define __NR_i386_set_ldt VG_DARWIN_SYSCALL_CONSTRUCT_MDEP(5) +// # define __NR_i386_get_ldt VG_DARWIN_SYSCALL_CONSTRUCT_MDEP(6) + +#elif defined(VGA_amd64) + +// osfmk/i386/machdep_call.c +# define __NR_pthread_set_self VG_DARWIN_SYSCALL_CONSTRUCT_MDEP(3) + +#else +# error unknown architecture +#endif + + +// osfmk/mach/syscall_sw.h + +#define __NR_mach_reply_port VG_DARWIN_SYSCALL_CONSTRUCT_MACH(26) +#define __NR_thread_self_trap VG_DARWIN_SYSCALL_CONSTRUCT_MACH(27) +#define __NR_task_self_trap VG_DARWIN_SYSCALL_CONSTRUCT_MACH(28) +#define __NR_host_self_trap VG_DARWIN_SYSCALL_CONSTRUCT_MACH(29) + +#define __NR_mach_msg_trap VG_DARWIN_SYSCALL_CONSTRUCT_MACH(31) +#define __NR_mach_msg_overwrite_trap VG_DARWIN_SYSCALL_CONSTRUCT_MACH(32) +#define __NR_semaphore_signal_trap VG_DARWIN_SYSCALL_CONSTRUCT_MACH(33) +#define __NR_semaphore_signal_all_trap VG_DARWIN_SYSCALL_CONSTRUCT_MACH(34) +#define __NR_semaphore_signal_thread_trap VG_DARWIN_SYSCALL_CONSTRUCT_MACH(35) +#define __NR_semaphore_wait_trap VG_DARWIN_SYSCALL_CONSTRUCT_MACH(36) +#define __NR_semaphore_wait_signal_trap VG_DARWIN_SYSCALL_CONSTRUCT_MACH(37) +#define __NR_semaphore_timedwait_trap VG_DARWIN_SYSCALL_CONSTRUCT_MACH(38) +#define __NR_semaphore_timedwait_signal_trap VG_DARWIN_SYSCALL_CONSTRUCT_MACH(39) + +#if defined(VGA_x86) +#define __NR_init_process VG_DARWIN_SYSCALL_CONSTRUCT_MACH(41) +#define __NR_map_fd VG_DARWIN_SYSCALL_CONSTRUCT_MACH(43) +#endif + +#define __NR_task_name_for_pid VG_DARWIN_SYSCALL_CONSTRUCT_MACH(44) +#define __NR_task_for_pid VG_DARWIN_SYSCALL_CONSTRUCT_MACH(45) +#define __NR_pid_for_task VG_DARWIN_SYSCALL_CONSTRUCT_MACH(46) + +#if defined(VGA_x86) +#define __NR_macx_swapon VG_DARWIN_SYSCALL_CONSTRUCT_MACH(48) +#define __NR_macx_swapoff VG_DARWIN_SYSCALL_CONSTRUCT_MACH(49) +#define __NR_macx_triggers VG_DARWIN_SYSCALL_CONSTRUCT_MACH(51) +#define __NR_macx_backing_store_suspend VG_DARWIN_SYSCALL_CONSTRUCT_MACH(52) +#define __NR_macx_backing_store_recovery VG_DARWIN_SYSCALL_CONSTRUCT_MACH(53) +#endif + +#define __NR_swtch_pri VG_DARWIN_SYSCALL_CONSTRUCT_MACH(59) +#define __NR_swtch VG_DARWIN_SYSCALL_CONSTRUCT_MACH(60) +#define __NR_sched_yield __NR_swtch /* linux-alike name */ +#define __NR_syscall_thread_switch VG_DARWIN_SYSCALL_CONSTRUCT_MACH(61) +#define __NR_clock_sleep_trap VG_DARWIN_SYSCALL_CONSTRUCT_MACH(62) + +#define __NR_mach_timebase_info VG_DARWIN_SYSCALL_CONSTRUCT_MACH(89) +#define __NR_mach_wait_until VG_DARWIN_SYSCALL_CONSTRUCT_MACH(90) +#define __NR_mk_timer_create VG_DARWIN_SYSCALL_CONSTRUCT_MACH(91) +#define __NR_mk_timer_destroy VG_DARWIN_SYSCALL_CONSTRUCT_MACH(92) +#define __NR_mk_timer_arm VG_DARWIN_SYSCALL_CONSTRUCT_MACH(93) +#define __NR_mk_timer_cancel VG_DARWIN_SYSCALL_CONSTRUCT_MACH(94) + +#define __NR_iokit_user_client_trap VG_DARWIN_SYSCALL_CONSTRUCT_MACH(100) + + +// bsd/sys/syscall.h + +#define __NR_syscall VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(0) +#define __NR_exit VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(1) +#define __NR_fork VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(2) // was UX64 +#define __NR_read VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(3) +#define __NR_write VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(4) +#define __NR_open VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(5) +#define __NR_close VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(6) +#define __NR_wait4 VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(7) + /* 8 old creat */ +#define __NR_link VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(9) +#define __NR_unlink VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(10) + /* 11 old execv */ +#define __NR_chdir VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(12) +#define __NR_fchdir VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(13) +#define __NR_mknod VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(14) +#define __NR_chmod VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(15) +#define __NR_chown VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(16) + /* 17 old break */ +#define __NR_getfsstat VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(18) + /* 19 old lseek */ +#define __NR_getpid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(20) + /* 21 old mount */ + /* 22 old umount */ +#define __NR_setuid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(23) +#define __NR_getuid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(24) +#define __NR_geteuid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(25) +#define __NR_ptrace VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(26) +#define __NR_recvmsg VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(27) +#define __NR_sendmsg VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(28) +#define __NR_recvfrom VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(29) +#define __NR_accept VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(30) +#define __NR_getpeername VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(31) +#define __NR_getsockname VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(32) +#define __NR_access VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(33) +#define __NR_chflags VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(34) +#define __NR_fchflags VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(35) +#define __NR_sync VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(36) +#define __NR_kill VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(37) + /* 38 old stat */ +#define __NR_getppid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(39) + /* 40 old lstat */ +#define __NR_dup VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(41) +#define __NR_pipe VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(42) // was UX64 +#define __NR_getegid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(43) +#define __NR_profil VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(44) + /* 45 old ktrace */ +#define __NR_sigaction VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(46) +#define __NR_getgid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(47) +#define __NR_sigprocmask VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(48) +#define __NR_getlogin VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(49) +#define __NR_setlogin VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(50) +#define __NR_acct VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(51) +#define __NR_sigpending VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(52) +#define __NR_sigaltstack VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(53) +#define __NR_ioctl VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(54) +#define __NR_reboot VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(55) +#define __NR_revoke VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(56) +#define __NR_symlink VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(57) +#define __NR_readlink VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(58) +#define __NR_execve VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(59) +#define __NR_umask VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(60) +#define __NR_chroot VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(61) + /* 62 old fstat */ + /* 63 used internally , reserved */ + /* 64 old getpagesize */ +#define __NR_msync VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(65) +#define __NR_vfork VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(66) + /* 67 old vread */ + /* 68 old vwrite */ + /* 69 old sbrk */ + /* 70 old sstk */ + /* 71 old mmap */ + /* 72 old vadvise */ +#define __NR_munmap VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(73) +#define __NR_mprotect VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(74) +#define __NR_madvise VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(75) + /* 76 old vhangup */ + /* 77 old vlimit */ +#define __NR_mincore VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(78) +#define __NR_getgroups VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(79) +#define __NR_setgroups VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(80) +#define __NR_getpgrp VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(81) +#define __NR_setpgid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(82) +#define __NR_setitimer VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(83) + /* 84 old wait */ +#define __NR_swapon VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(85) +#define __NR_getitimer VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(86) + /* 87 old gethostname */ + /* 88 old sethostname */ +#define __NR_getdtablesize VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(89) +#define __NR_dup2 VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(90) + /* 91 old getdopt */ +#define __NR_fcntl VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(92) +#define __NR_select VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(93) + /* 94 old setdopt */ +#define __NR_fsync VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(95) +#define __NR_setpriority VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(96) +#define __NR_socket VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(97) +#define __NR_connect VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(98) + /* 99 old accept */ +#define __NR_getpriority VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(100) + /* 101 old send */ + /* 102 old recv */ + /* 103 old sigreturn */ +#define __NR_bind VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(104) +#define __NR_setsockopt VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(105) +#define __NR_listen VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(106) + /* 107 old vtimes */ + /* 108 old sigvec */ + /* 109 old sigblock */ + /* 110 old sigsetmask */ +#define __NR_sigsuspend VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(111) + /* 112 old sigstack */ + /* 113 old recvmsg */ + /* 114 old sendmsg */ + /* 115 old vtrace */ +#define __NR_gettimeofday VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(116) +#define __NR_getrusage VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(117) +#define __NR_getsockopt VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(118) + /* 119 old resuba */ +#define __NR_readv VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(120) +#define __NR_writev VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(121) +#define __NR_settimeofday VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(122) +#define __NR_fchown VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(123) +#define __NR_fchmod VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(124) + /* 125 old recvfrom */ +#define __NR_setreuid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(126) +#define __NR_setregid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(127) +#define __NR_rename VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(128) + /* 129 old truncate */ + /* 130 old ftruncate */ +#define __NR_flock VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(131) +#define __NR_mkfifo VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(132) +#define __NR_sendto VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(133) +#define __NR_shutdown VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(134) +#define __NR_socketpair VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(135) +#define __NR_mkdir VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(136) +#define __NR_rmdir VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(137) +#define __NR_utimes VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(138) +#define __NR_futimes VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(139) +#define __NR_adjtime VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(140) + /* 141 old getpeername */ +#define __NR_gethostuuid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(142) + /* 143 old sethostid */ + /* 144 old getrlimit */ + /* 145 old setrlimit */ + /* 146 old killpg */ +#define __NR_setsid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(147) + /* 148 old setquota */ + /* 149 old qquota */ + /* 150 old getsockname */ +#define __NR_getpgid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(151) +#define __NR_setprivexec VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(152) +#define __NR_pread VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(153) +#define __NR_pwrite VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(154) +#define __NR_nfssvc VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(155) + /* 156 old getdirentries */ +#define __NR_statfs VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(157) +#define __NR_fstatfs VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(158) +#define __NR_unmount VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(159) + /* 160 old async_daemon */ +#define __NR_getfh VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(161) + /* 162 old getdomainname */ + /* 163 old setdomainname */ + /* 164 */ +#define __NR_quotactl VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(165) + /* 166 old exportfs */ +#define __NR_mount VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(167) + /* 168 old ustat */ +#define __NR_csops VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(169) + /* 170 old table */ + /* 171 old wait3 */ + /* 172 old rpause */ +#define __NR_waitid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(173) + /* 174 old getdents */ + /* 175 old gc_control */ +#define __NR_add_profil VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(176) + /* 177 */ + /* 178 */ + /* 179 */ +#define __NR_kdebug_trace VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(180) +#define __NR_setgid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(181) +#define __NR_setegid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(182) +#define __NR_seteuid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(183) +#define __NR_sigreturn VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(184) +#define __NR_chud VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(185) + /* 186 */ + /* 187 */ +#define __NR_stat VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(188) +#define __NR_fstat VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(189) +#define __NR_lstat VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(190) +#define __NR_pathconf VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(191) +#define __NR_fpathconf VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(192) + /* 193 */ +#define __NR_getrlimit VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(194) +#define __NR_setrlimit VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(195) +#define __NR_getdirentries VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(196) +#define __NR_mmap VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(197) + /* 198 __syscall */ +#define __NR_lseek VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(199) // was UX64 +#define __NR_truncate VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(200) +#define __NR_ftruncate VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(201) +#define __NR___sysctl VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(202) +#define __NR_mlock VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(203) +#define __NR_munlock VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(204) +#define __NR_undelete VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(205) +#define __NR_ATsocket VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(206) +#define __NR_ATgetmsg VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(207) +#define __NR_ATputmsg VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(208) +#define __NR_ATPsndreq VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(209) +#define __NR_ATPsndrsp VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(210) +#define __NR_ATPgetreq VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(211) +#define __NR_ATPgetrsp VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(212) + /* 213 Reserved for AppleTalk */ +#define __NR_kqueue_from_portset_np VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(214) +#define __NR_kqueue_portset_np VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(215) +#define __NR_mkcomplex VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(216) +#define __NR_statv VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(217) +#define __NR_lstatv VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(218) +#define __NR_fstatv VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(219) +#define __NR_getattrlist VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(220) +#define __NR_setattrlist VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(221) +#define __NR_getdirentriesattr VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(222) +#define __NR_exchangedata VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(223) + /* 224 checkuseraccess */ +#define __NR_searchfs VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(225) +#define __NR_delete VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(226) +#define __NR_copyfile VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(227) + /* 228 */ + /* 229 */ +#define __NR_poll VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(230) +#define __NR_watchevent VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(231) +#define __NR_waitevent VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(232) +#define __NR_modwatch VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(233) +#define __NR_getxattr VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(234) +#define __NR_fgetxattr VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(235) +#define __NR_setxattr VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(236) +#define __NR_fsetxattr VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(237) +#define __NR_removexattr VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(238) +#define __NR_fremovexattr VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(239) +#define __NR_listxattr VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(240) +#define __NR_flistxattr VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(241) +#define __NR_fsctl VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(242) +#define __NR_initgroups VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(243) +#define __NR_posix_spawn VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(244) + /* 245 */ + /* 246 */ +#define __NR_nfsclnt VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(247) +#define __NR_fhopen VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(248) + /* 249 */ +#define __NR_minherit VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(250) +#define __NR_semsys VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(251) +#define __NR_msgsys VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(252) +#define __NR_shmsys VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(253) +#define __NR_semctl VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(254) +#define __NR_semget VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(255) +#define __NR_semop VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(256) + /* 257 */ +#define __NR_msgctl VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(258) +#define __NR_msgget VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(259) +#define __NR_msgsnd VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(260) +#define __NR_msgrcv VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(261) +#define __NR_shmat VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(262) +#define __NR_shmctl VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(263) +#define __NR_shmdt VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(264) +#define __NR_shmget VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(265) +#define __NR_shm_open VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(266) +#define __NR_shm_unlink VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(267) +#define __NR_sem_open VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(268) +#define __NR_sem_close VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(269) +#define __NR_sem_unlink VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(270) +#define __NR_sem_wait VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(271) +#define __NR_sem_trywait VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(272) +#define __NR_sem_post VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(273) +#define __NR_sem_getvalue VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(274) +#define __NR_sem_init VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(275) +#define __NR_sem_destroy VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(276) +#define __NR_open_extended VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(277) +#define __NR_umask_extended VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(278) +#define __NR_stat_extended VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(279) +#define __NR_lstat_extended VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(280) +#define __NR_fstat_extended VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(281) +#define __NR_chmod_extended VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(282) +#define __NR_fchmod_extended VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(283) +#define __NR_access_extended VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(284) +#define __NR_settid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(285) +#define __NR_gettid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(286) +#define __NR_setsgroups VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(287) +#define __NR_getsgroups VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(288) +#define __NR_setwgroups VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(289) +#define __NR_getwgroups VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(290) +#define __NR_mkfifo_extended VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(291) +#define __NR_mkdir_extended VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(292) +#define __NR_identitysvc VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(293) +#define __NR_shared_region_check_np VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(294) +#define __NR_shared_region_map_np VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(295) + /* 296 old load_shared_file */ + /* 297 old reset_shared_file */ + /* 298 old new_system_shared_regions */ + /* 299 old shared_region_map_file_np */ + /* 300 old shared_region_make_private_np */ +#define __NR___pthread_mutex_destroy VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(301) +#define __NR___pthread_mutex_init VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(302) +#define __NR___pthread_mutex_lock VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(303) +#define __NR___pthread_mutex_trylock VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(304) +#define __NR___pthread_mutex_unlock VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(305) +#define __NR___pthread_cond_init VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(306) +#define __NR___pthread_cond_destroy VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(307) +#define __NR___pthread_cond_broadcast VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(308) +#define __NR___pthread_cond_signal VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(309) +#define __NR_getsid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(310) +#define __NR_settid_with_pid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(311) +#define __NR___pthread_cond_timedwait VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(312) +#define __NR_aio_fsync VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(313) +#define __NR_aio_return VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(314) +#define __NR_aio_suspend VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(315) +#define __NR_aio_cancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(316) +#define __NR_aio_error VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(317) +#define __NR_aio_read VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(318) +#define __NR_aio_write VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(319) +#define __NR_lio_listio VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(320) +#define __NR___pthread_cond_wait VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(321) +#define __NR_iopolicysys VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(322) + /* 323 */ +#define __NR_mlockall VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(324) +#define __NR_munlockall VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(325) + /* 326 */ +#define __NR_issetugid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(327) +#define __NR___pthread_kill VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(328) +#define __NR___pthread_sigmask VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(329) +#define __NR___sigwait VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(330) +#define __NR_sigwait VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(330) // GrP fixme hack +#define __NR___disable_threadsignal VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(331) +#define __NR___pthread_markcancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(332) +#define __NR___pthread_canceled VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(333) +#define __NR___semwait_signal VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(334) + /* 335 old utrace */ +#define __NR_proc_info VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(336) +#define __NR_sendfile VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(337) +#define __NR_stat64 VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(338) +#define __NR_fstat64 VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(339) +#define __NR_lstat64 VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(340) +#define __NR_stat64_extended VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(341) +#define __NR_lstat64_extended VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(342) +#define __NR_fstat64_extended VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(343) +#define __NR_getdirentries64 VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(344) +#define __NR_statfs64 VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(345) +#define __NR_fstatfs64 VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(346) +#define __NR_getfsstat64 VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(347) +#define __NR___pthread_chdir VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(348) +#define __NR___pthread_fchdir VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(349) + +#define __NR_audit VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(350) +#define __NR_auditon VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(351) + /* 352 */ +#define __NR_getauid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(353) +#define __NR_setauid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(354) +#define __NR_getaudit VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(355) +#define __NR_setaudit VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(356) +#define __NR_getaudit_addr VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(357) +#define __NR_setaudit_addr VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(358) +#define __NR_auditctl VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(359) +#define __NR_bsdthread_create VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(360) +#define __NR_bsdthread_terminate VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(361) +#define __NR_kqueue VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(362) +#define __NR_kevent VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(363) +#define __NR_lchown VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(364) +#define __NR_stack_snapshot VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(365) +#define __NR_bsdthread_register VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(366) +#define __NR_workq_open VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(367) +#define __NR_workq_ops VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(368) + /* 369 */ + /* 370 */ + /* 371 */ + /* 372 */ + /* 373 */ + /* 374 */ + /* 375 */ + /* 376 */ + /* 377 */ + /* 378 */ + /* 379 */ +#define __NR___mac_execve VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(380) +#define __NR___mac_syscall VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(381) +#define __NR___mac_get_file VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(382) +#define __NR___mac_set_file VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(383) +#define __NR___mac_get_link VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(384) +#define __NR___mac_set_link VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(385) +#define __NR___mac_get_proc VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(386) +#define __NR___mac_set_proc VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(387) +#define __NR___mac_get_fd VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(388) +#define __NR___mac_set_fd VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(389) +#define __NR___mac_get_pid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(390) +#define __NR___mac_get_lcid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(391) +#define __NR___mac_get_lctx VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(392) +#define __NR___mac_set_lctx VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(393) +#define __NR_setlcid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(394) +#define __NR_getlcid VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(395) +#define __NR_read_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(396) +#define __NR_write_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(397) +#define __NR_open_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(398) +#define __NR_close_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(399) +#define __NR_wait4_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(400) +#define __NR_recvmsg_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(401) +#define __NR_sendmsg_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(402) +#define __NR_recvfrom_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(403) +#define __NR_accept_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(404) +#define __NR_msync_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(405) +#define __NR_fcntl_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(406) +#define __NR_select_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(407) +#define __NR_fsync_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(408) +#define __NR_connect_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(409) +#define __NR_sigsuspend_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(410) +#define __NR_readv_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(411) +#define __NR_writev_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(412) +#define __NR_sendto_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(413) +#define __NR_pread_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(414) +#define __NR_pwrite_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(415) +#define __NR_waitid_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(416) +#define __NR_poll_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(417) +#define __NR_msgsnd_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(418) +#define __NR_msgrcv_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(419) +#define __NR_sem_wait_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(420) +#define __NR_aio_suspend_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(421) +#define __NR___sigwait_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(422) +#define __NR___semwait_signal_nocancel VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(423) +#define __NR___mac_mount VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(424) +#define __NR___mac_get_mount VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(425) +#define __NR___mac_getfsstat VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(426) +#define __NR_MAXSYSCALL VG_DARWIN_SYSCALL_CONSTRUCT_UNIX(427) + +#define __NR_DARWIN_FAKE_SIGRETURN (1 + __NR_MAXSYSCALL) + +#endif diff --git a/lackey/Makefile.am b/lackey/Makefile.am index 69a1a8236..6a2ff3d95 100644 --- a/lackey/Makefile.am +++ b/lackey/Makefile.am @@ -19,6 +19,12 @@ endif if VGCONF_PLATFORMS_INCLUDE_PPC64_AIX5 noinst_PROGRAMS += lackey-ppc64-aix5 endif +if VGCONF_PLATFORMS_INCLUDE_X86_DARWIN +noinst_PROGRAMS += lackey-x86-darwin +endif +if VGCONF_PLATFORMS_INCLUDE_AMD64_DARWIN +noinst_PROGRAMS += lackey-amd64-darwin +endif LACKEY_SOURCES_COMMON = lk_main.c @@ -63,3 +69,17 @@ lackey_ppc64_aix5_CFLAGS = $(AM_CFLAGS_PPC64_AIX5) lackey_ppc64_aix5_DEPENDENCIES = $(COREGRIND_LIBS_PPC64_AIX5) lackey_ppc64_aix5_LDADD = $(TOOL_LDADD_PPC64_AIX5) lackey_ppc64_aix5_LDFLAGS = $(TOOL_LDFLAGS_PPC64_AIX5) + +lackey_x86_darwin_SOURCES = $(LACKEY_SOURCES_COMMON) +lackey_x86_darwin_CPPFLAGS = $(AM_CPPFLAGS_X86_DARWIN) +lackey_x86_darwin_CFLAGS = $(AM_CFLAGS_X86_DARWIN) +lackey_x86_darwin_DEPENDENCIES = $(COREGRIND_LIBS_X86_DARWIN) +lackey_x86_darwin_LDADD = $(TOOL_LDADD_X86_DARWIN) +lackey_x86_darwin_LDFLAGS = $(TOOL_LDFLAGS_X86_DARWIN) + +lackey_amd64_darwin_SOURCES = $(LACKEY_SOURCES_COMMON) +lackey_amd64_darwin_CPPFLAGS = $(AM_CPPFLAGS_AMD64_DARWIN) +lackey_amd64_darwin_CFLAGS = $(AM_CFLAGS_AMD64_DARWIN) +lackey_amd64_darwin_DEPENDENCIES = $(COREGRIND_LIBS_AMD64_DARWIN) +lackey_amd64_darwin_LDADD = $(TOOL_LDADD_AMD64_DARWIN) +lackey_amd64_darwin_LDFLAGS = $(TOOL_LDFLAGS_AMD64_DARWIN) diff --git a/massif/Makefile.am b/massif/Makefile.am index 6ff1f51ab..3033df1eb 100644 --- a/massif/Makefile.am +++ b/massif/Makefile.am @@ -5,6 +5,7 @@ SUBDIRS += perf bin_SCRIPTS = ms_print noinst_PROGRAMS = +noinst_DSYMS = if VGCONF_PLATFORMS_INCLUDE_X86_LINUX noinst_PROGRAMS += massif-x86-linux vgpreload_massif-x86-linux.so endif @@ -23,6 +24,14 @@ endif if VGCONF_PLATFORMS_INCLUDE_PPC64_AIX5 noinst_PROGRAMS += massif-ppc64-aix5 vgpreload_massif-ppc64-aix5.so endif +if VGCONF_PLATFORMS_INCLUDE_X86_DARWIN +noinst_PROGRAMS += massif-x86-darwin vgpreload_massif-x86-darwin.so +noinst_DSYMS += vgpreload_massif-x86-darwin.so +endif +if VGCONF_PLATFORMS_INCLUDE_AMD64_DARWIN +noinst_PROGRAMS += massif-amd64-darwin vgpreload_massif-amd64-darwin.so +noinst_DSYMS += vgpreload_massif-amd64-darwin.so +endif vgpreload_massif_x86_linux_so_SOURCES = vgpreload_massif_x86_linux_so_CPPFLAGS = $(AM_CPPFLAGS_X86_LINUX) @@ -72,6 +81,22 @@ vgpreload_massif_ppc64_aix5_so_LDFLAGS = \ $(PRELOAD_LDFLAGS_PPC64_AIX5) \ $(LIBREPLACEMALLOC_LDFLAGS_PPC64_AIX5) +vgpreload_massif_x86_darwin_so_SOURCES = +vgpreload_massif_x86_darwin_so_CPPFLAGS = $(AM_CPPFLAGS_X86_DARWIN) +vgpreload_massif_x86_darwin_so_CFLAGS = $(AM_CFLAGS_X86_DARWIN) $(AM_CFLAGS_PIC) +vgpreload_massif_x86_darwin_so_DEPENDENCIES = $(LIBREPLACEMALLOC_X86_DARWIN) +vgpreload_massif_x86_darwin_so_LDFLAGS = \ + $(PRELOAD_LDFLAGS_X86_DARWIN) \ + $(LIBREPLACEMALLOC_LDFLAGS_X86_DARWIN) + +vgpreload_massif_amd64_darwin_so_SOURCES = +vgpreload_massif_amd64_darwin_so_CPPFLAGS = $(AM_CPPFLAGS_AMD64_DARWIN) +vgpreload_massif_amd64_darwin_so_CFLAGS = $(AM_CFLAGS_AMD64_DARWIN) $(AM_CFLAGS_PIC) +vgpreload_massif_amd64_darwin_so_DEPENDENCIES = $(LIBREPLACEMALLOC_AMD64_DARWIN) +vgpreload_massif_amd64_darwin_so_LDFLAGS = \ + $(PRELOAD_LDFLAGS_AMD64_DARWIN) \ + $(LIBREPLACEMALLOC_LDFLAGS_AMD64_DARWIN) + MASSIF_SOURCES_COMMON = ms_main.c massif_x86_linux_SOURCES = $(MASSIF_SOURCES_COMMON) @@ -115,3 +140,17 @@ massif_ppc64_aix5_CFLAGS = $(AM_CFLAGS_PPC64_AIX5) massif_ppc64_aix5_DEPENDENCIES = $(COREGRIND_LIBS_PPC64_AIX5) massif_ppc64_aix5_LDADD = $(TOOL_LDADD_PPC64_AIX5) massif_ppc64_aix5_LDFLAGS = $(TOOL_LDFLAGS_PPC64_AIX5) + +massif_x86_darwin_SOURCES = $(MASSIF_SOURCES_COMMON) +massif_x86_darwin_CPPFLAGS = $(AM_CPPFLAGS_X86_DARWIN) +massif_x86_darwin_CFLAGS = $(AM_CFLAGS_X86_DARWIN) +massif_x86_darwin_DEPENDENCIES = $(COREGRIND_LIBS_X86_DARWIN) +massif_x86_darwin_LDADD = $(TOOL_LDADD_X86_DARWIN) +massif_x86_darwin_LDFLAGS = $(TOOL_LDFLAGS_X86_DARWIN) + +massif_amd64_darwin_SOURCES = $(MASSIF_SOURCES_COMMON) +massif_amd64_darwin_CPPFLAGS = $(AM_CPPFLAGS_AMD64_DARWIN) +massif_amd64_darwin_CFLAGS = $(AM_CFLAGS_AMD64_DARWIN) +massif_amd64_darwin_DEPENDENCIES = $(COREGRIND_LIBS_AMD64_DARWIN) +massif_amd64_darwin_LDADD = $(TOOL_LDADD_AMD64_DARWIN) +massif_amd64_darwin_LDFLAGS = $(TOOL_LDFLAGS_AMD64_DARWIN) diff --git a/massif/ms_main.c b/massif/ms_main.c index 318fc11ac..f3d46feb9 100644 --- a/massif/ms_main.c +++ b/massif/ms_main.c @@ -1288,8 +1288,10 @@ static Time get_time(void) // Take a snapshot, and only that -- decisions on whether to take a // snapshot, or what kind of snapshot, are made elsewhere. +// Nb: we call the arg "my_time" because "time" shadows a global declaration +// in /usr/include/time.h on Darwin. static void -take_snapshot(Snapshot* snapshot, SnapshotKind kind, Time time, +take_snapshot(Snapshot* snapshot, SnapshotKind kind, Time my_time, Bool is_detailed) { tl_assert(!is_snapshot_in_use(snapshot)); @@ -1314,7 +1316,7 @@ take_snapshot(Snapshot* snapshot, SnapshotKind kind, Time time, // Rest of snapshot. snapshot->kind = kind; - snapshot->time = time; + snapshot->time = my_time; sanity_check_snapshot(snapshot); // Update stats. @@ -1341,12 +1343,14 @@ maybe_take_snapshot(SnapshotKind kind, Char* what) Snapshot* snapshot; Bool is_detailed; - Time time = get_time(); + // Nb: we call this variable "my_time" because "time" shadows a global + // declaration in /usr/include/time.h on Darwin. + Time my_time = get_time(); switch (kind) { case Normal: // Only do a snapshot if it's time. - if (time < earliest_possible_time_of_next_snapshot) { + if (my_time < earliest_possible_time_of_next_snapshot) { n_skipped_snapshots++; n_skipped_snapshots_since_last_snapshot++; return; @@ -1376,7 +1380,7 @@ maybe_take_snapshot(SnapshotKind kind, Char* what) // Take the snapshot. snapshot = & snapshots[next_snapshot_i]; - take_snapshot(snapshot, kind, time, is_detailed); + take_snapshot(snapshot, kind, my_time, is_detailed); // Record if it was detailed. if (is_detailed) { @@ -1422,7 +1426,7 @@ maybe_take_snapshot(SnapshotKind kind, Char* what) } // Work out the earliest time when the next snapshot can happen. - earliest_possible_time_of_next_snapshot = time + min_time_interval; + earliest_possible_time_of_next_snapshot = my_time + min_time_interval; } diff --git a/massif/tests/malloc_usable.c b/massif/tests/malloc_usable.c index 4a0e470d8..25781732a 100644 --- a/massif/tests/malloc_usable.c +++ b/massif/tests/malloc_usable.c @@ -5,7 +5,7 @@ int main(void) { -# if !defined(VGO_aix5) +# if !defined(VGO_aix5) && !defined(VGO_darwin) // Because our allocations are in multiples of 8 or 16, 99 will round up // to 104 or 112. int* x = malloc(99); diff --git a/memcheck/Makefile.am b/memcheck/Makefile.am index e042a6b6c..e11e84144 100644 --- a/memcheck/Makefile.am +++ b/memcheck/Makefile.am @@ -3,6 +3,7 @@ include $(top_srcdir)/Makefile.tool.am SUBDIRS += perf noinst_PROGRAMS = +noinst_DSYMS = if VGCONF_PLATFORMS_INCLUDE_X86_LINUX noinst_PROGRAMS += memcheck-x86-linux vgpreload_memcheck-x86-linux.so endif @@ -21,6 +22,14 @@ endif if VGCONF_PLATFORMS_INCLUDE_PPC64_AIX5 noinst_PROGRAMS += memcheck-ppc64-aix5 vgpreload_memcheck-ppc64-aix5.so endif +if VGCONF_PLATFORMS_INCLUDE_X86_DARWIN +noinst_PROGRAMS += memcheck-x86-darwin vgpreload_memcheck-x86-darwin.so +noinst_DSYMS += vgpreload_memcheck-x86-darwin.so +endif +if VGCONF_PLATFORMS_INCLUDE_AMD64_DARWIN +noinst_PROGRAMS += memcheck-amd64-darwin vgpreload_memcheck-amd64-darwin.so +noinst_DSYMS += vgpreload_memcheck-amd64-darwin.so +endif VGPRELOAD_MEMCHECK_SOURCES_COMMON = mc_replace_strmem.c @@ -78,6 +87,24 @@ vgpreload_memcheck_ppc64_aix5_so_LDFLAGS = \ $(PRELOAD_LDFLAGS_PPC64_AIX5) \ $(LIBREPLACEMALLOC_LDFLAGS_PPC64_AIX5) +vgpreload_memcheck_x86_darwin_so_SOURCES = $(VGPRELOAD_MEMCHECK_SOURCES_COMMON) +vgpreload_memcheck_x86_darwin_so_CPPFLAGS = $(AM_CPPFLAGS_X86_DARWIN) +vgpreload_memcheck_x86_darwin_so_CFLAGS = $(AM_CFLAGS_X86_DARWIN) $(AM_CFLAGS_PIC) -O2 +vgpreload_memcheck_x86_darwin_so_CCASFLAGS = $(AM_CCASFLAGS_X86_DARWIN) +vgpreload_memcheck_x86_darwin_so_DEPENDENCIES = $(LIBREPLACEMALLOC_X86_DARWIN) +vgpreload_memcheck_x86_darwin_so_LDFLAGS = \ + $(PRELOAD_LDFLAGS_X86_DARWIN) \ + $(LIBREPLACEMALLOC_LDFLAGS_X86_DARWIN) + +vgpreload_memcheck_amd64_darwin_so_SOURCES = $(VGPRELOAD_MEMCHECK_SOURCES_COMMON) +vgpreload_memcheck_amd64_darwin_so_CPPFLAGS = $(AM_CPPFLAGS_AMD64_DARWIN) +vgpreload_memcheck_amd64_darwin_so_CFLAGS = $(AM_CFLAGS_AMD64_DARWIN) $(AM_CFLAGS_PIC) -O2 +vgpreload_memcheck_amd64_darwin_so_CCASFLAGS = $(AM_CCASFLAGS_AMD64_DARWIN) +vgpreload_memcheck_amd64_darwin_so_DEPENDENCIES = $(LIBREPLACEMALLOC_AMD64_DARWIN) +vgpreload_memcheck_amd64_darwin_so_LDFLAGS = \ + $(PRELOAD_LDFLAGS_AMD64_DARWIN) \ + $(LIBREPLACEMALLOC_LDFLAGS_AMD64_DARWIN) + MEMCHECK_SOURCES_COMMON = \ mc_leakcheck.c \ mc_malloc_wrappers.c \ @@ -134,6 +161,22 @@ memcheck_ppc64_aix5_DEPENDENCIES = $(COREGRIND_LIBS_PPC64_AIX5) memcheck_ppc64_aix5_LDADD = $(TOOL_LDADD_PPC64_AIX5) memcheck_ppc64_aix5_LDFLAGS = $(TOOL_LDFLAGS_PPC64_AIX5) +memcheck_x86_darwin_SOURCES = $(MEMCHECK_SOURCES_COMMON) +memcheck_x86_darwin_CPPFLAGS = $(AM_CPPFLAGS_X86_DARWIN) +memcheck_x86_darwin_CFLAGS = $(AM_CFLAGS_X86_DARWIN) -O2 +memcheck_x86_darwin_CCASFLAGS = $(AM_CCASFLAGS_X86_DARWIN) +memcheck_x86_darwin_DEPENDENCIES = $(COREGRIND_LIBS_X86_DARWIN) +memcheck_x86_darwin_LDADD = $(TOOL_LDADD_X86_DARWIN) +memcheck_x86_darwin_LDFLAGS = $(TOOL_LDFLAGS_X86_DARWIN) + +memcheck_amd64_darwin_SOURCES = $(MEMCHECK_SOURCES_COMMON) +memcheck_amd64_darwin_CPPFLAGS = $(AM_CPPFLAGS_AMD64_DARWIN) +memcheck_amd64_darwin_CFLAGS = $(AM_CFLAGS_AMD64_DARWIN) -O2 +memcheck_amd64_darwin_CCASFLAGS = $(AM_CCASFLAGS_AMD64_DARWIN) +memcheck_amd64_darwin_DEPENDENCIES = $(COREGRIND_LIBS_AMD64_DARWIN) +memcheck_amd64_darwin_LDADD = $(TOOL_LDADD_AMD64_DARWIN) +memcheck_amd64_darwin_LDFLAGS = $(TOOL_LDFLAGS_AMD64_DARWIN) + mcincludedir = $(includedir)/valgrind mcinclude_HEADERS = \ diff --git a/memcheck/mc_errors.c b/memcheck/mc_errors.c index e56a16311..edc123ae0 100644 --- a/memcheck/mc_errors.c +++ b/memcheck/mc_errors.c @@ -268,7 +268,7 @@ static void mc_pp_AddrInfo ( Addr a, AddrInfo* ai, Bool maybe_gcc ) if (maybe_gcc) { VG_(message)(Vg_UserMsg, "%sAddress 0x%llx is just below the stack ptr. " - "To suppress, use: --workaround-gcc296-bugs=yes%s", + "To suppress, use: --workaround-gcc296-bugs=yes%s", xpre, (ULong)a, xpost ); } else { diff --git a/memcheck/mc_machine.c b/memcheck/mc_machine.c index 4072b8ada..05f6da4f4 100644 --- a/memcheck/mc_machine.c +++ b/memcheck/mc_machine.c @@ -491,6 +491,7 @@ static Int get_otrack_shadow_offset_wrk ( Int offset, Int szB ) if (o == GOF(IP_AT_SYSCALL) && sz == 8) return -1; /* slot unused */ if (o == GOF(IDFLAG) && sz == 8) return -1; /* slot used for %DH */ if (o == GOF(FS_ZERO) && sz == 8) return -1; /* slot unused */ + if (o == GOF(GS_0x60) && sz == 8) return -1; /* slot unused */ if (o == GOF(TISTART) && sz == 8) return -1; /* slot unused */ if (o == GOF(TILEN) && sz == 8) return -1; /* slot unused */ diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c index 8cf7a7bc6..a6660ad4b 100644 --- a/memcheck/mc_main.c +++ b/memcheck/mc_main.c @@ -92,8 +92,9 @@ static void ocache_sarp_Clear_Origins ( Addr, UWord ); /* fwds */ /* Conceptually, every byte value has 8 V bits, which track whether Memcheck thinks the corresponding value bit is defined. And every memory byte has an A bit, which tracks whether Memcheck thinks the program can access - it safely. So every N-bit register is shadowed with N V bits, and every - memory byte is shadowed with 8 V bits and one A bit. + it safely (ie. it's mapped, and has at least one of the RWX permission bits + set). So every N-bit register is shadowed with N V bits, and every memory + byte is shadowed with 8 V bits and one A bit. In the implementation, we use two forms of compression (compressed V bits and distinguished secondary maps) to avoid the 9-bit-per-byte overhead @@ -3667,6 +3668,10 @@ void check_mem_is_defined ( CorePart part, ThreadId tid, Char* s, isAddrErr ? 0 : otag ); break; + case Vg_CoreSysCallArgInMem: + MC_(record_regparam_error) ( tid, s, otag ); + break; + /* If we're being asked to jump to a silly address, record an error message before potentially crashing the entire system. */ case Vg_CoreTranslate: @@ -3696,11 +3701,20 @@ void check_mem_is_defined_asciiz ( CorePart part, ThreadId tid, } } +static +void mc_new_mem_mmap ( Addr a, SizeT len, Bool rr, Bool ww, Bool xx, + ULong di_handle ) +{ + if (rr || ww || xx) + MC_(make_mem_defined)(a, len); + else + MC_(make_mem_noaccess)(a, len); +} + static void mc_new_mem_startup( Addr a, SizeT len, Bool rr, Bool ww, Bool xx, ULong di_handle ) { - /* Ignore the permissions, just make it defined. Seems to work... */ // Because code is defined, initialised variables get put in the data // segment and are defined, and uninitialised variables get put in the // bss segment and are auto-zeroed (and so defined). @@ -3711,16 +3725,14 @@ void mc_new_mem_startup( Addr a, SizeT len, // false negative, but it's a grey area -- the behaviour is defined (the // padding is zeroed) but it's probably not what the user intended. And // we can't avoid it. + // + // Note: we generally ignore RWX permissions, because we can't track them + // without requiring more than one A bit which would slow things down a + // lot. But on Darwin the 0th page is mapped but !R and !W and !X. + // So we mark any such pages as "unaddressable". DEBUG("mc_new_mem_startup(%#lx, %llu, rr=%u, ww=%u, xx=%u)\n", a, (ULong)len, rr, ww, xx); - MC_(make_mem_defined)(a, len); -} - -static -void mc_new_mem_mmap ( Addr a, SizeT len, Bool rr, Bool ww, Bool xx, - ULong di_handle ) -{ - MC_(make_mem_defined)(a, len); + mc_new_mem_mmap(a, len, rr, ww, xx, di_handle); } static @@ -5110,6 +5122,7 @@ static Bool mc_handle_client_request ( ThreadId tid, UWord* arg, UWord* ret ) return True; } + /*------------------------------------------------------------*/ /*--- Crude profiling machinery. ---*/ /*------------------------------------------------------------*/ diff --git a/memcheck/mc_replace_strmem.c b/memcheck/mc_replace_strmem.c index b7c30fc60..6c5e31bc8 100644 --- a/memcheck/mc_replace_strmem.c +++ b/memcheck/mc_replace_strmem.c @@ -117,6 +117,9 @@ STRRCHR(VG_Z_LIBC_SONAME, strrchr) STRRCHR(VG_Z_LIBC_SONAME, rindex) #if defined(VGO_linux) STRRCHR(VG_Z_LD_LINUX_SO_2, rindex) +#elif defined(VGO_darwin) +STRRCHR(VG_Z_DYLD, strrchr) +STRRCHR(VG_Z_DYLD, rindex) #endif @@ -141,6 +144,9 @@ STRCHR(VG_Z_LD_LINUX_SO_2, strchr) STRCHR(VG_Z_LD_LINUX_SO_2, index) STRCHR(VG_Z_LD_LINUX_X86_64_SO_2, strchr) STRCHR(VG_Z_LD_LINUX_X86_64_SO_2, index) +#elif defined(VGO_darwin) +STRCHR(VG_Z_DYLD, strchr) +STRCHR(VG_Z_DYLD, index) #endif @@ -194,6 +200,51 @@ STRCAT(VG_Z_LIBC_SONAME, strcat) } STRNCAT(VG_Z_LIBC_SONAME, strncat) +#if defined(VGO_darwin) +STRNCAT(VG_Z_DYLD, strncat) +#endif + + +/* Append src to dst. n is the size of dst's buffer. dst is guaranteed + to be nul-terminated after the copy, unless n <= strlen(dst_orig). + Returns min(n, strlen(dst_orig)) + strlen(src_orig). + Truncation occurred if retval >= n. +*/ +#define STRLCAT(soname, fnname) \ + SizeT VG_REPLACE_FUNCTION_ZU(soname,fnname) \ + ( char* dst, const char* src, SizeT n ); \ + SizeT VG_REPLACE_FUNCTION_ZU(soname,fnname) \ + ( char* dst, const char* src, SizeT n ) \ + { \ + const Char* src_orig = src; \ + Char* dst_orig = dst; \ + SizeT m = 0; \ +\ + while (m < n && *dst) { m++; dst++; } \ + if (m < n) { \ + /* Fill as far as dst_orig[n-2], then nul-terminate. */ \ + while (m < n-1 && *src) { m++; *dst++ = *src++; } \ + *dst = 0; \ + } else { \ + /* No space to copy anything to dst. m == n */ \ + } \ + /* Finish counting min(n, strlen(dst_orig)) + strlen(src_orig) */ \ + while (*src) { m++; src++; } \ + /* This checks for overlap after copying, unavoidable without */ \ + /* pre-counting lengths... should be ok */ \ + if (is_overlap(dst_orig, \ + src_orig, \ + (Addr)dst-(Addr)dst_orig+1, \ + (Addr)src-(Addr)src_orig+1)) \ + RECORD_OVERLAP_ERROR("strlcat", dst_orig, src_orig, n); \ +\ + return m; \ + } + +#if defined(VGO_darwin) +STRLCAT(VG_Z_LIBC_SONAME, strlcat) +STRLCAT(VG_Z_DYLD, strlcat) +#endif #define STRNLEN(soname, fnname) \ @@ -250,6 +301,9 @@ STRLEN(VG_Z_LD_LINUX_X86_64_SO_2, strlen) } STRCPY(VG_Z_LIBC_SONAME, strcpy) +#if defined(VGO_darwin) +STRCPY(VG_Z_DYLD, strcpy) +#endif #define STRNCPY(soname, fnname) \ @@ -273,6 +327,40 @@ STRCPY(VG_Z_LIBC_SONAME, strcpy) } STRNCPY(VG_Z_LIBC_SONAME, strncpy) +#if defined(VGO_darwin) +STRNCPY(VG_Z_DYLD, strncpy) +#endif + + +/* Copy up to n-1 bytes from src to dst. Then nul-terminate dst if n > 0. + Returns strlen(src). Does not zero-fill the remainder of dst. */ +#define STRLCPY(soname, fnname) \ + SizeT VG_REPLACE_FUNCTION_ZU(soname, fnname) \ + ( char* dst, const char* src, SizeT n ); \ + SizeT VG_REPLACE_FUNCTION_ZU(soname, fnname) \ + ( char* dst, const char* src, SizeT n ) \ + { \ + const char* src_orig = src; \ + char* dst_orig = dst; \ + SizeT m = 0; \ +\ + while (m < n-1 && *src) { m++; *dst++ = *src++; } \ + /* m non-nul bytes have now been copied, and m <= n-1. */ \ + /* Check for overlap after copying; all n bytes of dst are relevant, */ \ + /* but only m+1 bytes of src if terminator was found */ \ + if (is_overlap(dst_orig, src_orig, n, (m < n) ? m+1 : n)) \ + RECORD_OVERLAP_ERROR("strlcpy", dst, src, n); \ + /* Nul-terminate dst. */ \ + if (n > 0) *dst = 0; \ + /* Finish counting strlen(src). */ \ + while (*src) src++; \ + return src - src_orig; \ + } + +#if defined(VGO_darwin) +STRLCPY(VG_Z_LIBC_SONAME, strlcpy) +STRLCPY(VG_Z_DYLD, strlcpy) +#endif #define STRNCMP(soname, fnname) \ @@ -296,6 +384,9 @@ STRNCPY(VG_Z_LIBC_SONAME, strncpy) } STRNCMP(VG_Z_LIBC_SONAME, strncmp) +#if defined(VGO_darwin) +STRNCMP(VG_Z_DYLD, strncmp) +#endif #define STRCMP(soname, fnname) \ @@ -338,6 +429,9 @@ STRCMP(VG_Z_LD64_SO_1, strcmp) } MEMCHR(VG_Z_LIBC_SONAME, memchr) +#if defined(VGO_darwin) +MEMCHR(VG_Z_DYLD, memchr) +#endif #define MEMCPY(soname, fnname) \ @@ -389,6 +483,8 @@ MEMCPY(VG_Z_LIBC_SONAME, memcpy) #if defined(VGO_linux) MEMCPY(VG_Z_LD_SO_1, memcpy) /* ld.so.1 */ MEMCPY(VG_Z_LD64_SO_1, memcpy) /* ld64.so.1 */ +#elif defined(VGO_darwin) +MEMCPY(VG_Z_DYLD, memcpy) #endif /* icc9 blats these around all over the place. Not only in the main executable but various .so's. They are highly tuned and read @@ -430,6 +526,9 @@ MEMCMP(VG_Z_LIBC_SONAME, memcmp) MEMCMP(VG_Z_LIBC_SONAME, bcmp) #if defined(VGO_linux) MEMCMP(VG_Z_LD_SO_1, bcmp) +#elif defined(VGO_darwin) +MEMCMP(VG_Z_DYLD, memcmp) +MEMCMP(VG_Z_DYLD, bcmp) #endif @@ -460,6 +559,8 @@ STPCPY(VG_Z_LIBC_SONAME, stpcpy) #if defined(VGO_linux) STPCPY(VG_Z_LD_LINUX_SO_2, stpcpy) STPCPY(VG_Z_LD_LINUX_X86_64_SO_2, stpcpy) +#elif defined(VGO_darwin) +STPCPY(VG_Z_DYLD, stpcpy) #endif @@ -483,6 +584,9 @@ STPCPY(VG_Z_LD_LINUX_X86_64_SO_2, stpcpy) } MEMSET(VG_Z_LIBC_SONAME, memset) +#if defined(VGO_darwin) +MEMSET(VG_Z_DYLD, memset) +#endif #define MEMMOVE(soname, fnname) \ @@ -507,6 +611,35 @@ MEMSET(VG_Z_LIBC_SONAME, memset) } MEMMOVE(VG_Z_LIBC_SONAME, memmove) +#if defined(VGO_darwin) +MEMMOVE(VG_Z_DYLD, memmove) +#endif + + +#define BCOPY(soname, fnname) \ + void VG_REPLACE_FUNCTION_ZU(soname,fnname) \ + (const void *srcV, void *dstV, SizeT n); \ + void VG_REPLACE_FUNCTION_ZU(soname,fnname) \ + (const void *srcV, void *dstV, SizeT n) \ + { \ + SizeT i; \ + Char* dst = (Char*)dstV; \ + Char* src = (Char*)srcV; \ + if (dst < src) { \ + for (i = 0; i < n; i++) \ + dst[i] = src[i]; \ + } \ + else \ + if (dst > src) { \ + for (i = 0; i < n; i++) \ + dst[n-i-1] = src[n-i-1]; \ + } \ + } + +#if defined(VGO_darwin) +BCOPY(VG_Z_LIBC_SONAME, bcopy) +BCOPY(VG_Z_DYLD, bcopy) +#endif /* glibc 2.5 variant of memmove which checks the dest is big enough. diff --git a/memcheck/tests/Makefile.am b/memcheck/tests/Makefile.am index b5be28db5..96101cb66 100644 --- a/memcheck/tests/Makefile.am +++ b/memcheck/tests/Makefile.am @@ -15,13 +15,16 @@ endif if VGCONF_OS_IS_LINUX SUBDIRS += linux endif +if VGCONF_OS_IS_DARWIN +SUBDIRS += darwin +endif # Platform-specific tests if VGCONF_PLATFORMS_INCLUDE_X86_LINUX SUBDIRS += x86-linux endif -DIST_SUBDIRS = x86 amd64 linux x86-linux . +DIST_SUBDIRS = x86 amd64 linux darwin x86-linux . noinst_SCRIPTS = \ filter_addressable \ @@ -105,7 +108,7 @@ EXTRA_DIST = $(noinst_SCRIPTS) \ noisy_child.vgtest noisy_child.stderr.exp noisy_child.stdout.exp \ null_socket.stderr.exp null_socket.vgtest \ origin1-yes.vgtest origin1-yes.stdout.exp \ - origin1-yes.stderr.exp \ + origin1-yes.stderr.exp origin1-yes.stderr.exp-darwin \ origin2-not-quite.vgtest origin2-not-quite.stdout.exp \ origin2-not-quite.stderr.exp \ origin3-no.vgtest origin3-no.stdout.exp \ @@ -138,12 +141,13 @@ EXTRA_DIST = $(noinst_SCRIPTS) \ sh-mem-random.stderr.exp sh-mem-random.stdout.exp64 \ sh-mem-random.stdout.exp sh-mem-random.vgtest \ sigaltstack.stderr.exp sigaltstack.vgtest \ - sigkill.stderr.exp sigkill.vgtest \ + sigkill.stderr.exp sigkill.stderr.exp-darwin sigkill.vgtest \ signal2.stderr.exp signal2.stdout.exp signal2.vgtest \ sigprocmask.stderr.exp sigprocmask.stderr.exp2 sigprocmask.vgtest \ stack_changes.stderr.exp stack_changes.stdout.exp \ stack_changes.stdout.exp2 stack_changes.vgtest \ - strchr.stderr.exp strchr.stderr.exp2 strchr.vgtest \ + strchr.stderr.exp strchr.stderr.exp2 strchr.stderr.exp-darwin \ + strchr.vgtest \ str_tester.stderr.exp str_tester.vgtest \ supp_unknown.stderr.exp supp_unknown.vgtest supp_unknown.supp \ supp1.stderr.exp supp1.vgtest \ @@ -297,6 +301,10 @@ else if VGCONF_PLATFORMS_INCLUDE_PPC32_AIX5 varinfo5_LDADD = `pwd`/varinfo5so.so varinfo5_LDFLAGS = $(AM_FLAG_M3264_PRI) -Wl,-G -Wl,-bnogc +else +if VGCONF_OS_IS_DARWIN + varinfo5_LDADD = `pwd`/varinfo5so.so + varinfo5_LDFLAGS = $(AM_FLAG_M3264_PRI) else varinfo5_LDADD = varinfo5so.so varinfo5_LDFLAGS = $(AM_FLAG_M3264_PRI) \ @@ -312,11 +320,17 @@ else if VGCONF_PLATFORMS_INCLUDE_PPC32_AIX5 varinfo5so_so_LDFLAGS = -fpic $(AM_FLAG_M3264_PRI) -shared \ -Wl,-G -Wl,-bnogc +else +if VGCONF_OS_IS_DARWIN + varinfo5so_so_LDFLAGS = -fpic $(AM_FLAG_M3264_PRI) -dynamic \ + -dynamiclib -all_load else varinfo5so_so_LDFLAGS = -fpic $(AM_FLAG_M3264_PRI) -shared \ -Wl,-soname -Wl,varinfo5so.so endif endif +endif + # Build shared object for wrap7 wrap7_SOURCES = wrap7.c wrap7_DEPENDENCIES = wrap7so.so @@ -327,12 +341,17 @@ else if VGCONF_PLATFORMS_INCLUDE_PPC32_AIX5 wrap7_LDADD = `pwd`/wrap7so.so wrap7_LDFLAGS = $(AM_FLAG_M3264_PRI) -Wl,-G -Wl,-bnogc +else +if VGCONF_OS_IS_DARWIN + wrap7_LDADD = `pwd`/wrap7so.so + wrap7_LDFLAGS = $(AM_FLAG_M3264_PRI) else wrap7_LDADD = wrap7so.so wrap7_LDFLAGS = $(AM_FLAG_M3264_PRI) \ -Wl,-rpath,$(top_builddir)/memcheck/tests endif endif +endif wrap7so_so_SOURCES = wrap7so.c wrap7so_so_CFLAGS = $(AM_CFLAGS) -fpic @@ -342,10 +361,16 @@ else if VGCONF_PLATFORMS_INCLUDE_PPC32_AIX5 wrap7so_so_LDFLAGS = -fpic $(AM_FLAG_M3264_PRI) -shared \ -Wl,-G -Wl,-bnogc +else +if VGCONF_OS_IS_DARWIN + wrap7so_so_LDFLAGS = -fpic $(AM_FLAG_M3264_PRI) -dynamic \ + -dynamiclib -all_load else wrap7so_so_LDFLAGS = -fpic $(AM_FLAG_M3264_PRI) -shared \ -Wl,-soname -Wl,wrap7so.so endif endif +endif +endif diff --git a/memcheck/tests/amd64/Makefile.am b/memcheck/tests/amd64/Makefile.am index bb1c8032a..5dedb0d23 100644 --- a/memcheck/tests/amd64/Makefile.am +++ b/memcheck/tests/amd64/Makefile.am @@ -22,10 +22,18 @@ EXTRA_DIST = $(noinst_SCRIPTS) \ xor-undef-amd64.stderr.exp xor-undef-amd64.stdout.exp \ xor-undef-amd64.vgtest -check_PROGRAMS = bt_everything bug132146 defcfaexpr fxsave-amd64 \ - int3-amd64 \ - more_x87_fp sse_memory xor-undef-amd64 +check_PROGRAMS = bt_everything bug132146 fxsave-amd64 \ + xor-undef-amd64 +# DDD: not sure if these ones should work on Darwin or not... if not, should +# be moved into amd64-linux/. +if ! VGCONF_OS_IS_DARWIN + check_PROGRAMS += \ + defcfaexpr \ + int3-amd64 \ + more_x87_fp \ + sse_memory +endif AM_CFLAGS += @FLAG_M64@ AM_CXXFLAGS += @FLAG_M64@ diff --git a/memcheck/tests/badjump2.c b/memcheck/tests/badjump2.c index d91283cda..a4ba8036a 100644 --- a/memcheck/tests/badjump2.c +++ b/memcheck/tests/badjump2.c @@ -24,7 +24,7 @@ int main(void) /* Install own SIGSEGV handler */ sigsegv_new.sa_handler = SIGSEGV_handler; sigsegv_new.sa_flags = 0; -#if !defined(_AIX) +#if !defined(_AIX) && !defined(__APPLE__) sigsegv_new.sa_restorer = NULL; #endif res = sigemptyset( &sigsegv_new.sa_mask ); diff --git a/memcheck/tests/darwin/Makefile.am b/memcheck/tests/darwin/Makefile.am new file mode 100644 index 000000000..9d41756e3 --- /dev/null +++ b/memcheck/tests/darwin/Makefile.am @@ -0,0 +1,20 @@ + +include $(top_srcdir)/Makefile.tool-tests.am + +noinst_SCRIPTS = filter_stderr + +noinst_HEADERS = scalar.h + +EXTRA_DIST = $(noinst_SCRIPTS) \ + scalar.stderr.exp scalar.vgtest \ + scalar_fork.stderr.exp scalar_fork.vgtest \ + scalar_vfork.stderr.exp scalar_vfork.vgtest + +check_PROGRAMS = \ + scalar scalar_fork scalar_vfork + + +AM_CFLAGS += @FLAG_M32@ $(FLAG_MMMX) $(FLAG_MSSE) +AM_CXXFLAGS += @FLAG_M32@ $(FLAG_MMMX) $(FLAG_MSSE) +AM_CCASFLAGS += @FLAG_M32@ + diff --git a/memcheck/tests/darwin/filter_stderr b/memcheck/tests/darwin/filter_stderr new file mode 100644 index 000000000..0ae9313a9 --- /dev/null +++ b/memcheck/tests/darwin/filter_stderr @@ -0,0 +1,3 @@ +#! /bin/sh + +../filter_stderr diff --git a/memcheck/tests/darwin/scalar.c b/memcheck/tests/darwin/scalar.c new file mode 100644 index 000000000..2ee66ea19 --- /dev/null +++ b/memcheck/tests/darwin/scalar.c @@ -0,0 +1,1726 @@ +#include "../../memcheck.h" +#include "scalar.h" +#include +#include +#include + +// See memcheck/tests/x86-linux/scalar.c for an explanation of what this test +// is doing. + +int main(void) +{ + // uninitialised, but we know px[0] is 0x0 + long* px = malloc(sizeof(long)); + long x0 = px[0]; + long res; + + VALGRIND_MAKE_MEM_NOACCESS(0, 0x1000); + + // __NR_syscall 0 + // XXX + + // __NR_exit 1 + GO(__NR_exit, "below"); + // (see below) + + // __NR_fork 2 + GO(__NR_fork, "other"); + // (sse scalar_fork.c) + + // __NR_read 3 + // Nb: here we are also getting an error from the syscall arg itself. + GO(__NR_read, "1+3s 1m"); + SY(__NR_read+(int)x0, x0, x0, x0+1); FAILx(EFAULT); + + // __NR_write 4 + GO(__NR_write, "3s 1m"); + SY(__NR_write, x0, x0, x0+1); FAIL; + //res = write(x0, x0, x0+1); FAIL; + + // __NR_open 5 + // __NR_close 6 + // __NR_wait4 7 + // /* 8 old creat */ + // __NR_link 9 + // __NR_unlink 10 + // /* 11 old execv */ + // __NR_chdir 12 + // __NR_fchdir 13 + // __NR_mknod 14 + // __NR_chmod 15 + // __NR_chown 16 + // /* 17 old break */ + // __NR_getfsstat 18 + // /* 19 old lseek */ + // __NR_getpid 20 + // /* 21 old mount */ + // /* 22 old umount */ + // __NR_setuid 23 + // __NR_getuid 24 + // __NR_geteuid 25 + // __NR_ptrace 26 + // __NR_recvmsg 27 + // __NR_sendmsg 28 + // __NR_recvfrom 29 + // __NR_accept 30 + // __NR_getpeername 31 + // __NR_getsockname 32 + // __NR_access 33 + // __NR_chflags 34 + // __NR_fchflags 35 + // __NR_sync 36 + // __NR_kill 37 + // /* 38 old stat */ + // __NR_getppid 39 + // /* 40 old lstat */ + // __NR_dup 41 + // __NR_pipe VG_DARWIN_SYSCALL_CONSTRUCT_UX64(42 + // __NR_getegid 43 + // __NR_profil 44 + // /* 45 old ktrace */ + // __NR_sigaction 46 + // __NR_getgid 47 + // __NR_sigprocmask 48 + // __NR_getlogin 49 + // __NR_setlogin 50 + // __NR_acct 51 + // __NR_sigpending 52 + // __NR_sigaltstack 53 + // __NR_ioctl 54 + // __NR_reboot 55 + // __NR_revoke 56 + // __NR_symlink 57 + // __NR_readlink 58 + // __NR_execve 59 + // __NR_umask 60 + // __NR_chroot 61 + // /* 62 old fstat */ + // /* 63 used internally , reserved */ + // /* 64 old getpagesize */ + // __NR_msync 65 + // __NR_vfork 66 + // /* 67 old vread */ + // /* 68 old vwrite */ + // /* 69 old sbrk */ + // /* 70 old sstk */ + // /* 71 old mmap */ + // /* 72 old vadvise */ + // __NR_munmap 73 + // __NR_mprotect 74 + // __NR_madvise 75 + // /* 76 old vhangup */ + // /* 77 old vlimit */ + // __NR_mincore 78 + // __NR_getgroups 79 + // __NR_setgroups 80 + // __NR_getpgrp 81 + // __NR_setpgid 82 + // __NR_setitimer 83 + // /* 84 old wait */ + // __NR_swapon 85 + // __NR_getitimer 86 + // /* 87 old gethostname */ + // /* 88 old sethostname */ + // __NR_getdtablesize 89 + // __NR_dup2 90 + // /* 91 old getdopt */ + // __NR_fcntl 92 + // __NR_select 93 + // /* 94 old setdopt */ + // __NR_fsync 95 + // __NR_setpriority 96 + // __NR_socket 97 + // __NR_connect 98 + // /* 99 old accept */ + // __NR_getpriority 100 + // /* 101 old send */ + // /* 102 old recv */ + // /* 103 old sigreturn */ + // __NR_bind 104 + + // __NR_setsockopt 105 + GO(__NR_setsockopt, "5s 1m"); + SY(__NR_setsockopt, x0, x0, x0, x0+1, x0+1); FAIL; + + // __NR_listen 106 + // /* 107 old vtimes */ + // /* 108 old sigvec */ + // /* 109 old sigblock */ + // /* 110 old sigsetmask */ + // __NR_sigsuspend 111 + // /* 112 old sigstack */ + // /* 113 old recvmsg */ + // /* 114 old sendmsg */ + // /* 115 old vtrace */ + // __NR_gettimeofday 116 + // __NR_getrusage 117 + + // __NR_getsockopt 118 + // Nb: there's no "getsockopt(optlen) points to unaddressable byte(s)"; + // difficult to get with arg4 being checked with buf_and_len_pre_check. + GO(__NR_getsockopt, "5s 1m"); + SY(__NR_getsockopt, x0, x0, x0, x0+1, x0+&px[1]); FAIL; + + // /* 119 old resuba */ + // __NR_readv 120 + // __NR_writev 121 + // __NR_settimeofday 122 + // __NR_fchown 123 + // __NR_fchmod 124 + // /* 125 old recvfrom */ + // __NR_setreuid 126 + // __NR_setregid 127 + // __NR_rename 128 + // /* 129 old truncate */ + // /* 130 old ftruncate */ + // __NR_flock 131 + // __NR_mkfifo 132 + // __NR_sendto 133 + // __NR_shutdown 134 + // __NR_socketpair 135 + // __NR_mkdir 136 + // __NR_rmdir 137 + // __NR_utimes 138 + // __NR_futimes 139 + // __NR_adjtime 140 + // /* 141 old getpeername */ + // __NR_gethostuuid 142 + // /* 143 old sethostid */ + // /* 144 old getrlimit */ + // /* 145 old setrlimit */ + // /* 146 old killpg */ + // __NR_setsid 147 + // /* 148 old setquota */ + // /* 149 old qquota */ + // /* 150 old getsockname */ + // __NR_getpgid 151 + // __NR_setprivexec 152 + // __NR_pread 153 + // __NR_pwrite 154 + // __NR_nfssvc 155 + // /* 156 old getdirentries */ + // __NR_statfs 157 + // __NR_fstatfs 158 + // __NR_unmount 159 + // /* 160 old async_daemon */ + // __NR_getfh 161 + // /* 162 old getdomainname */ + // /* 163 old setdomainname */ + // /* 164 */ + // __NR_quotactl 165 + // /* 166 old exportfs */ + // __NR_mount 167 + // /* 168 old ustat */ + + // __NR_csops 169 + GO(__NR_csops, "4s 1m"); + SY(__NR_csops, x0, x0, x0+1, x0+1); FAILx(EFAULT); + + // /* 170 old table */ + // /* 171 old wait3 */ + // /* 172 old rpause */ + // __NR_waitid 173 + // /* 174 old getdents */ + // /* 175 old gc_control */ + // __NR_add_profil 176 + // /* 177 */ + // /* 178 */ + // /* 179 */ + // __NR_kdebug_trace 180 + // __NR_setgid 181 + // __NR_setegid 182 + // __NR_seteuid 183 + // __NR_sigreturn 184 + // __NR_chud 185 + // /* 186 */ + // /* 187 */ + // __NR_stat 188 + // __NR_fstat 189 + // __NR_lstat 190 + // __NR_pathconf 191 + // __NR_fpathconf 192 + // /* 193 */ + // __NR_getrlimit 194 + // __NR_setrlimit 195 + // __NR_getdirentries 196 + // __NR_mmap 197 + // /* 198 __syscall */ + // __NR_lseek VG_DARWIN_SYSCALL_CONSTRUCT_UX64(199 + // __NR_truncate 200 + // __NR_ftruncate 201 + // __NR___sysctl 202 + // __NR_mlock 203 + // __NR_munlock 204 + // __NR_undelete 205 + // __NR_ATsocket 206 + // __NR_ATgetmsg 207 + // __NR_ATputmsg 208 + // __NR_ATPsndreq 209 + // __NR_ATPsndrsp 210 + // __NR_ATPgetreq 211 + // __NR_ATPgetrsp 212 + // /* 213 Reserved for AppleTalk */ + // __NR_kqueue_from_portset_np 214 + // __NR_kqueue_portset_np 215 + // __NR_mkcomplex 216 + // __NR_statv 217 + // __NR_lstatv 218 + // __NR_fstatv 219 + // __NR_getattrlist 220 + // __NR_setattrlist 221 + // __NR_getdirentriesattr 222 + // __NR_exchangedata 223 + // /* 224 checkuseraccess */ + // __NR_searchfs 225 + // __NR_delete 226 + // __NR_copyfile 227 + // /* 228 */ + // /* 229 */ + // __NR_poll 230 + // __NR_watchevent 231 + // __NR_waitevent 232 + // __NR_modwatch 233 + // __NR_getxattr 234 + // __NR_fgetxattr 235 + // __NR_setxattr 236 + // __NR_fsetxattr 237 + // __NR_removexattr 238 + // __NR_fremovexattr 239 + // __NR_listxattr 240 + // __NR_flistxattr 241 + // __NR_fsctl 242 + // __NR_initgroups 243 + // __NR_posix_spawn 244 + // /* 245 */ + // /* 246 */ + // __NR_nfsclnt 247 + // __NR_fhopen 248 + // /* 249 */ + // __NR_minherit 250 + // __NR_semsys 251 + // __NR_msgsys 252 + // __NR_shmsys 253 + // __NR_semctl 254 + // __NR_semget 255 + // __NR_semop 256 + // /* 257 */ + // __NR_msgctl 258 + // __NR_msgget 259 + // __NR_msgsnd 260 + // __NR_msgrcv 261 + // __NR_shmat 262 + // __NR_shmctl 263 + // __NR_shmdt 264 + + // __NR_shmget 265 + GO(__NR_shmget, "3s 0m"); + SY(__NR_shmget, x0, x0, x0); FAIL; + + // __NR_shm_open 266 + // __NR_shm_unlink 267 + + // __NR_sem_open 268 + GO(__NR_sem_open, "2s 1m"); + SY(__NR_sem_open, x0, x0); FAIL; + + GO(__NR_sem_open, "(4-args) 2s 0m"); + SY(__NR_sem_open, "my_sem", O_CREAT|O_EXCL, x0, x0); SUCC_OR_FAIL; + + // __NR_sem_close 269 + // Nb: we add 0x12345 to make sure it's not a valid semaphore descriptor. + GO(__NR_sem_close, "1s 0m"); + SY(__NR_sem_close, x0+0x12345); FAIL; + + // __NR_sem_unlink 270 + GO(__NR_sem_unlink, "1s 1m"); + SY(__NR_sem_unlink, x0); FAIL; + + // __NR_sem_wait 271 + // __NR_sem_trywait 272 + + // __NR_sem_post 273 + GO(__NR_sem_post, "1s 0m"); + SY(__NR_sem_post, x0); FAIL; + + // __NR_sem_getvalue 274 + + // __NR_sem_init 275 + GO(__NR_sem_init, "3s 1m"); + SY(__NR_sem_init, x0+1, x0, x0); FAILx(ENOSYS); + + // __NR_sem_destroy 276 + GO(__NR_sem_destroy, "1s 1m"); + SY(__NR_sem_destroy, x0+1); FAILx(ENOSYS); + + // __NR_open_extended 277 + // __NR_umask_extended 278 + // __NR_stat_extended 279 + // __NR_lstat_extended 280 + // __NR_fstat_extended 281 + // __NR_chmod_extended 282 + // __NR_fchmod_extended 283 + // __NR_access_extended 284 + // __NR_settid 285 + // __NR_gettid 286 + // __NR_setsgroups 287 + // __NR_getsgroups 288 + // __NR_setwgroups 289 + // __NR_getwgroups 290 + // __NR_mkfifo_extended 291 + // __NR_mkdir_extended 292 + // __NR_identitysvc 293 + // __NR_shared_region_check_np 294 + // __NR_shared_region_map_np 295 + // /* 296 old load_shared_file */ + // /* 297 old reset_shared_file */ + // /* 298 old new_system_shared_regions */ + // /* 299 old shared_region_map_file_np */ + // /* 300 old shared_region_make_private_np */ + // __NR___pthread_mutex_destroy 301 + // __NR___pthread_mutex_init 302 + // __NR___pthread_mutex_lock 303 + // __NR___pthread_mutex_trylock 304 + // __NR___pthread_mutex_unlock 305 + // __NR___pthread_cond_init 306 + // __NR___pthread_cond_destroy 307 + // __NR___pthread_cond_broadcast 308 + // __NR___pthread_cond_signal 309 + // __NR_getsid 310 + // __NR_settid_with_pid 311 + // __NR___pthread_cond_timedwait 312 + // __NR_aio_fsync 313 + // __NR_aio_return 314 + // __NR_aio_suspend 315 + // __NR_aio_cancel 316 + // __NR_aio_error 317 + // __NR_aio_read 318 + // __NR_aio_write 319 + // __NR_lio_listio 320 + // __NR___pthread_cond_wait 321 + // __NR_iopolicysys 322 + // /* 323 */ + // __NR_mlockall 324 + // __NR_munlockall 325 + // /* 326 */ + // __NR_issetugid 327 + // __NR___pthread_kill 328 + // __NR___pthread_sigmask 329 + // __NR___sigwait 330 + // __NR_sigwait 330) // GrP fixme hack + // __NR___disable_threadsignal 331 + // __NR___pthread_markcancel 332 + // __NR___pthread_canceled 333 + // __NR___semwait_signal 334 + // /* 335 old utrace */ + // __NR_proc_info 336 + // __NR_sendfile 337 + // __NR_stat64 338 + // __NR_fstat64 339 + // __NR_lstat64 340 + // __NR_stat64_extended 341 + // __NR_lstat64_extended 342 + // __NR_fstat64_extended 343 + // __NR_getdirentries64 344 + // __NR_statfs64 345 + // __NR_fstatfs64 346 + // __NR_getfsstat64 347 + // __NR___pthread_chdir 348 + // __NR___pthread_fchdir 349 + // __NR_audit 350 + // __NR_auditon 351 + // /* 352 */ + // __NR_getauid 353 + // __NR_setauid 354 + // __NR_getaudit 355 + // __NR_setaudit 356 + // __NR_getaudit_addr 357 + // __NR_setaudit_addr 358 + // __NR_auditctl 359 + // __NR_bsdthread_create 360 + // __NR_bsdthread_terminate 361 + // __NR_kqueue 362 + // __NR_kevent 363 + // __NR_lchown 364 + // __NR_stack_snapshot 365 + // __NR_bsdthread_register 366 + // __NR_workq_open 367 + // __NR_workq_ops 368 + // /* 369 */ + // /* 370 */ + // /* 371 */ + // /* 372 */ + // /* 373 */ + // /* 374 */ + // /* 375 */ + // /* 376 */ + // /* 377 */ + // /* 378 */ + // /* 379 */ + // __NR___mac_execve 380 + // __NR___mac_syscall 381 + // __NR___mac_get_file 382 + // __NR___mac_set_file 383 + // __NR___mac_get_link 384 + // __NR___mac_set_link 385 + // __NR___mac_get_proc 386 + // __NR___mac_set_proc 387 + // __NR___mac_get_fd 388 + // __NR___mac_set_fd 389 + // __NR___mac_get_pid 390 + // __NR___mac_get_lcid 391 + // __NR___mac_get_lctx 392 + // __NR___mac_set_lctx 393 + // __NR_setlcid 394 + // __NR_getlcid 395 + // __NR_read_nocancel 396 + // __NR_write_nocancel 397 + // __NR_open_nocancel 398 + // __NR_close_nocancel 399 + // __NR_wait4_nocancel 400 + // __NR_recvmsg_nocancel 401 + // __NR_sendmsg_nocancel 402 + // __NR_recvfrom_nocancel 403 + // __NR_accept_nocancel 404 + // __NR_msync_nocancel 405 + // __NR_fcntl_nocancel 406 + // __NR_select_nocancel 407 + // __NR_fsync_nocancel 408 + // __NR_connect_nocancel 409 + // __NR_sigsuspend_nocancel 410 + // __NR_readv_nocancel 411 + // __NR_writev_nocancel 412 + // __NR_sendto_nocancel 413 + // __NR_pread_nocancel 414 + // __NR_pwrite_nocancel 415 + // __NR_waitid_nocancel 416 + // __NR_poll_nocancel 417 + // __NR_msgsnd_nocancel 418 + // __NR_msgrcv_nocancel 419 + + // __NR_sem_wait_nocancel 420 + GO(__NR_sem_wait_nocancel, "1s 0m"); + SY(__NR_sem_wait_nocancel, x0); FAIL; + + // __NR_aio_suspend_nocancel 421 + // __NR___sigwait_nocancel 422 + // __NR___semwait_signal_nocancel 423 + // __NR___mac_mount 424 + // __NR___mac_get_mount 425 + // __NR___mac_getfsstat 426 + // __NR_MAXSYSCALL 427 + +#if 0 + // XXX: all these are copied from x86-darwin/scalar.c. + + // __NR_open 5 + GO(__NR_open, "(2-args) 2s 1m"); + SY(__NR_open, x0, x0); FAIL; + + // Only 1s 0m errors -- the other 2s 1m have been checked in the previous + // open test, and if we test them they may be commoned up but they also + // may not. + GO(__NR_open, "(3-args) 1s 0m"); + SY(__NR_open, "scalar.c", O_CREAT|O_EXCL, x0); FAIL; + + // __NR_close 6 + GO(__NR_close, "1s 0m"); + SY(__NR_close, x0-1); FAIL; + + // __NR_waitpid 7 + GO(__NR_waitpid, "3s 1m"); + SY(__NR_waitpid, x0, x0+1, x0); FAIL; + + // __NR_creat 8 + GO(__NR_creat, "2s 1m"); + SY(__NR_creat, x0, x0); FAIL; + + // __NR_link 9 + GO(__NR_link, "2s 2m"); + SY(__NR_link, x0, x0); FAIL; + + // __NR_unlink 10 + GO(__NR_unlink, "1s 1m"); + SY(__NR_unlink, x0); FAIL; + + // __NR_execve 11 + // Nb: could have 3 memory errors if we pass x0+1 as the 2nd and 3rd + // args, except for bug #93174. + GO(__NR_execve, "3s 1m"); + SY(__NR_execve, x0, x0, x0); FAIL; + + // __NR_chdir 12 + GO(__NR_chdir, "1s 1m"); + SY(__NR_chdir, x0); FAIL; + + // __NR_time 13 + GO(__NR_time, "1s 1m"); + SY(__NR_time, x0+1); FAIL; + + // __NR_mknod 14 + GO(__NR_mknod, "3s 1m"); + SY(__NR_mknod, x0, x0, x0); FAIL; + + // __NR_chmod 15 + GO(__NR_chmod, "2s 1m"); + SY(__NR_chmod, x0, x0); FAIL; + + // __NR_lchown 16 + GO(__NR_lchown, "n/a"); + //SY(__NR_lchown); // (Not yet handled by Valgrind) FAIL; + + // __NR_break 17 + GO(__NR_break, "ni"); + SY(__NR_break); FAIL; + + // __NR_oldstat 18 + GO(__NR_oldstat, "n/a"); + // (obsolete, not handled by Valgrind) + + // __NR_lseek 19 + GO(__NR_lseek, "3s 0m"); + SY(__NR_lseek, x0-1, x0, x0); FAILx(EBADF); + + // __NR_getpid 20 + GO(__NR_getpid, "0s 0m"); + SY(__NR_getpid); SUCC; + + // __NR_mount 21 + GO(__NR_mount, "5s 3m"); + SY(__NR_mount, x0, x0, x0, x0, x0); FAIL; + + // __NR_umount 22 + GO(__NR_umount, "1s 1m"); + SY(__NR_umount, x0); FAIL; + + // __NR_setuid 23 + GO(__NR_setuid, "1s 0m"); + SY(__NR_setuid, x0); FAIL; + + // __NR_getuid 24 + GO(__NR_getuid, "0s 0m"); + SY(__NR_getuid); SUCC; + + // __NR_stime 25 + GO(__NR_stime, "n/a"); + //SY(__NR_stime); // (Not yet handled by Valgrind) FAIL; + + // __NR_ptrace 26 + // XXX: memory pointed to be arg3 goes unchecked... otherwise would be 2m + GO(__NR_ptrace, "4s 1m"); + SY(__NR_ptrace, x0+PTRACE_GETREGS, x0, x0, x0); FAIL; + + // __NR_alarm 27 + GO(__NR_alarm, "1s 0m"); + SY(__NR_alarm, x0); SUCC; + + // __NR_oldfstat 28 + GO(__NR_oldfstat, "n/a"); + // (obsolete, not handled by Valgrind) + + // __NR_pause 29 + GO(__NR_pause, "ignore"); + // (hard to test, and no args so not much to be gained -- don't bother) + + // __NR_utime 30 + GO(__NR_utime, "2s 2m"); + SY(__NR_utime, x0, x0+1); FAIL; + + // __NR_stty 31 + GO(__NR_stty, "ni"); + SY(__NR_stty); FAIL; + + // __NR_gtty 32 + GO(__NR_gtty, "ni"); + SY(__NR_gtty); FAIL; + + // __NR_access 33 + GO(__NR_access, "2s 1m"); + SY(__NR_access, x0, x0); FAIL; + + // __NR_nice 34 + GO(__NR_nice, "1s 0m"); + SY(__NR_nice, x0); SUCC; + + // __NR_ftime 35 + GO(__NR_ftime, "ni"); + SY(__NR_ftime); FAIL; + + // __NR_sync 36 + GO(__NR_sync, "0s 0m"); + SY(__NR_sync); SUCC; + + // __NR_kill 37 + GO(__NR_kill, "2s 0m"); + SY(__NR_kill, x0, x0); SUCC; + + // __NR_rename 38 + GO(__NR_rename, "2s 2m"); + SY(__NR_rename, x0, x0); FAIL; + + // __NR_mkdir 39 + GO(__NR_mkdir, "2s 1m"); + SY(__NR_mkdir, x0, x0); FAIL; + + // __NR_rmdir 40 + GO(__NR_rmdir, "1s 1m"); + SY(__NR_rmdir, x0); FAIL; + + // __NR_dup 41 + GO(__NR_dup, "1s 0m"); + SY(__NR_dup, x0-1); FAIL; + + // __NR_pipe 42 + GO(__NR_pipe, "1s 1m"); + SY(__NR_pipe, x0); FAIL; + + // __NR_times 43 + GO(__NR_times, "1s 1m"); + SY(__NR_times, x0+1); FAIL; + + // __NR_prof 44 + GO(__NR_prof, "ni"); + SY(__NR_prof); FAIL; + + // __NR_brk 45 + GO(__NR_brk, "1s 0m"); + SY(__NR_brk, x0); SUCC; + + // __NR_setgid 46 + GO(__NR_setgid, "1s 0m"); + SY(__NR_setgid, x0); FAIL; + + // __NR_getgid 47 + GO(__NR_getgid, "0s 0m"); + SY(__NR_getgid); SUCC; + + // __NR_signal 48 + GO(__NR_signal, "n/a"); + //SY(__NR_signal); // (Not yet handled by Valgrind) FAIL; + + // __NR_geteuid 49 + GO(__NR_geteuid, "0s 0m"); + SY(__NR_geteuid); SUCC; + + // __NR_getegid 50 + GO(__NR_getegid, "0s 0m"); + SY(__NR_getegid); SUCC; + + // __NR_acct 51 + GO(__NR_acct, "1s 1m"); + SY(__NR_acct, x0); FAIL; + + // __NR_umount2 52 + GO(__NR_umount2, "2s 1m"); + SY(__NR_umount2, x0, x0); FAIL; + + // __NR_lock 53 + GO(__NR_lock, "ni"); + SY(__NR_lock); FAIL; + + // __NR_ioctl 54 + #include + GO(__NR_ioctl, "3s 1m"); + SY(__NR_ioctl, x0, x0+TCSETS, x0); FAIL; + + // __NR_fcntl 55 + // As with sys_open(), the 'fd' error is suppressed for the later ones. + // For F_GETFD the 3rd arg is ignored + GO(__NR_fcntl, "(GETFD) 2s 0m"); + SY(__NR_fcntl, x0-1, x0+F_GETFD, x0); FAILx(EBADF); + + // For F_DUPFD the 3rd arg is 'arg'. We don't check the 1st two args + // because any errors may or may not be commoned up with the ones from + // the previous fcntl call. + GO(__NR_fcntl, "(DUPFD) 1s 0m"); + SY(__NR_fcntl, -1, F_DUPFD, x0); FAILx(EBADF); + + // For F_GETLK the 3rd arg is 'lock'. On x86, this fails w/EBADF. But + // on amd64 in 32-bit mode it fails w/EFAULT. We don't check the 1st two + // args for the reason given above. + GO(__NR_fcntl, "(GETLK) 1s 0m"); + SY(__NR_fcntl, -1, F_GETLK, x0); FAIL; //FAILx(EBADF); + + // __NR_mpx 56 + GO(__NR_mpx, "ni"); + SY(__NR_mpx); FAIL; + + // __NR_setpgid 57 + GO(__NR_setpgid, "2s 0m"); + SY(__NR_setpgid, x0, x0-1); FAIL; + + // __NR_ulimit 58 + GO(__NR_ulimit, "ni"); + SY(__NR_ulimit); FAIL; + + // __NR_oldolduname 59 + GO(__NR_oldolduname, "n/a"); + // (obsolete, not handled by Valgrind) + + // __NR_umask 60 + GO(__NR_umask, "1s 0m"); + SY(__NR_umask, x0+022); SUCC; + + // __NR_chroot 61 + GO(__NR_chroot, "1s 1m"); + SY(__NR_chroot, x0); FAIL; + + // __NR_ustat 62 + GO(__NR_ustat, "n/a"); + // (deprecated, not handled by Valgrind) + + // __NR_dup2 63 + GO(__NR_dup2, "2s 0m"); + SY(__NR_dup2, x0-1, x0); FAIL; + + // __NR_getppid 64 + GO(__NR_getppid, "0s 0m"); + SY(__NR_getppid); SUCC; + + // __NR_getpgrp 65 + GO(__NR_getpgrp, "0s 0m"); + SY(__NR_getpgrp); SUCC; + + // __NR_setsid 66 + GO(__NR_setsid, "0s 0m"); + SY(__NR_setsid); SUCC_OR_FAIL; + + // __NR_sigaction 67 + GO(__NR_sigaction, "3s 4m"); + SY(__NR_sigaction, x0, x0+&px[1], x0+&px[1]); FAIL; + + // __NR_sgetmask 68 sys_sgetmask() + GO(__NR_sgetmask, "n/a"); + //SY(__NR_sgetmask); // (Not yet handled by Valgrind) FAIL; + + // __NR_ssetmask 69 + GO(__NR_ssetmask, "n/a"); + //SY(__NR_ssetmask); // (Not yet handled by Valgrind) FAIL; + + // __NR_setreuid 70 + GO(__NR_setreuid, "2s 0m"); + SY(__NR_setreuid, x0, x0); FAIL; + + // __NR_setregid 71 + GO(__NR_setregid, "2s 0m"); + SY(__NR_setregid, x0, x0); FAIL; + + // __NR_sigsuspend 72 + // XXX: how do you use this function? + GO(__NR_sigsuspend, "ignore"); + // (I don't know how to test this...) + + // __NR_sigpending 73 + GO(__NR_sigpending, "1s 1m"); + SY(__NR_sigpending, x0); FAIL; + + // __NR_sethostname 74 + GO(__NR_sethostname, "n/a"); + //SY(__NR_sethostname); // (Not yet handled by Valgrind) FAIL; + + // __NR_setrlimit 75 + GO(__NR_setrlimit, "2s 1m"); + SY(__NR_setrlimit, x0, x0); FAIL; + + // __NR_getrlimit 76 + GO(__NR_getrlimit, "2s 1m"); + SY(__NR_getrlimit, x0, x0); FAIL; + + // __NR_getrusage 77 + GO(__NR_getrusage, "2s 1m"); + SY(__NR_getrusage, x0, x0); FAIL; + + // __NR_gettimeofday 78 + GO(__NR_gettimeofday, "2s 2m"); + SY(__NR_gettimeofday, x0, x0+1); FAIL; + + // __NR_settimeofday 79 + GO(__NR_settimeofday, "2s 2m"); + SY(__NR_settimeofday, x0, x0+1); FAIL; + + // __NR_getgroups 80 + GO(__NR_getgroups, "2s 1m"); + SY(__NR_getgroups, x0+1, x0+1); FAIL; + + // __NR_setgroups 81 + GO(__NR_setgroups, "2s 1m"); + SY(__NR_setgroups, x0+1, x0+1); FAIL; + + // __NR_select 82 + { + long args[5] = { x0+8, x0+0xffffffee, x0+1, x0+1, x0+1 }; + GO(__NR_select, "1s 5m"); + SY(__NR_select, args+x0); FAIL; + } + + // __NR_symlink 83 + GO(__NR_symlink, "2s 2m"); + SY(__NR_symlink, x0, x0); FAIL; + + // __NR_oldlstat 84 + GO(__NR_oldlstat, "n/a"); + // (obsolete, not handled by Valgrind) + + // __NR_readlink 85 + GO(__NR_readlink, "3s 2m"); + SY(__NR_readlink, x0+1, x0+1, x0+1); FAIL; + + // __NR_uselib 86 + GO(__NR_uselib, "n/a"); + //SY(__NR_uselib); // (Not yet handled by Valgrind) FAIL; + + // __NR_swapon 87 + GO(__NR_swapon, "n/a"); + //SY(__NR_swapon); // (Not yet handled by Valgrind) FAIL; + + // __NR_reboot 88 + GO(__NR_reboot, "n/a"); + //SY(__NR_reboot); // (Not yet handled by Valgrind) FAIL; + + // __NR_readdir 89 + GO(__NR_readdir, "n/a"); + // (superseded, not handled by Valgrind) + + // __NR_mmap 90 + { + long args[6] = { x0, x0, x0, x0, x0-1, x0 }; + GO(__NR_mmap, "1s 1m"); + SY(__NR_mmap, args+x0); FAIL; + } + + // __NR_munmap 91 + GO(__NR_munmap, "2s 0m"); + SY(__NR_munmap, x0, x0); FAIL; + + // __NR_truncate 92 + GO(__NR_truncate, "2s 1m"); + SY(__NR_truncate, x0, x0); FAIL; + + // __NR_ftruncate 93 + GO(__NR_ftruncate, "2s 0m"); + SY(__NR_ftruncate, x0, x0); FAIL; + + // __NR_fchmod 94 + GO(__NR_fchmod, "2s 0m"); + SY(__NR_fchmod, x0-1, x0); FAIL; + + // __NR_fchown 95 + GO(__NR_fchown, "3s 0m"); + SY(__NR_fchown, x0, x0, x0); FAIL; + + // __NR_getpriority 96 + GO(__NR_getpriority, "2s 0m"); + SY(__NR_getpriority, x0-1, x0); FAIL; + + // __NR_setpriority 97 + GO(__NR_setpriority, "3s 0m"); + SY(__NR_setpriority, x0-1, x0, x0); FAIL; + + // __NR_profil 98 + GO(__NR_profil, "ni"); + SY(__NR_profil); FAIL; + + // __NR_statfs 99 + GO(__NR_statfs, "2s 2m"); + SY(__NR_statfs, x0, x0); FAIL; + + // __NR_fstatfs 100 + GO(__NR_fstatfs, "2s 1m"); + SY(__NR_fstatfs, x0, x0); FAIL; + + // __NR_ioperm 101 + GO(__NR_ioperm, "3s 0m"); + SY(__NR_ioperm, x0, x0, x0); FAIL; + + // __NR_socketcall 102 + GO(__NR_socketcall, "XXX"); + // (XXX: need to do all sub-cases properly) + + // __NR_syslog 103 + GO(__NR_syslog, "3s 1m"); + SY(__NR_syslog, x0+2, x0, x0+1); FAIL; + + // __NR_setitimer 104 + GO(__NR_setitimer, "3s 2m"); + SY(__NR_setitimer, x0, x0+1, x0+1); FAIL; + + // __NR_getitimer 105 + GO(__NR_getitimer, "2s 1m"); + SY(__NR_getitimer, x0, x0, x0); FAIL; + + // __NR_stat 106 + GO(__NR_stat, "2s 2m"); + SY(__NR_stat, x0, x0); FAIL; + + // __NR_lstat 107 + GO(__NR_lstat, "2s 2m"); + SY(__NR_lstat, x0, x0); FAIL; + + // __NR_fstat 108 + GO(__NR_fstat, "2s 1m"); + SY(__NR_fstat, x0, x0); FAIL; + + // __NR_olduname 109 + GO(__NR_olduname, "n/a"); + // (obsolete, not handled by Valgrind) + + // __NR_iopl 110 + GO(__NR_iopl, "1s 0m"); + SY(__NR_iopl, x0+100); FAIL; + + // __NR_vhangup 111 + GO(__NR_vhangup, "0s 0m"); + SY(__NR_vhangup); SUCC_OR_FAIL; // Will succeed for superuser + + // __NR_idle 112 + GO(__NR_idle, "ni"); + SY(__NR_idle); FAIL; + + // __NR_vm86old 113 + GO(__NR_vm86old, "n/a"); + // (will probably never be handled by Valgrind) + + // __NR_wait4 114 + GO(__NR_wait4, "4s 2m"); + SY(__NR_wait4, x0, x0+1, x0, x0+1); FAIL; + + // __NR_swapoff 115 + GO(__NR_swapoff, "n/a"); + //SY(__NR_swapoff); // (Not yet handled by Valgrind) FAIL; + + // __NR_sysinfo 116 + GO(__NR_sysinfo, "1s 1m"); + SY(__NR_sysinfo, x0); FAIL; + + // __NR_ipc 117 + // XXX: This is simplistic -- need to do all the sub-cases properly. + // XXX: Also, should be 6 scalar errors, except glibc's syscall() doesn't + // use the 6th one! + GO(__NR_ipc, "5s 0m"); + SY(__NR_ipc, x0+4, x0, x0, x0, x0, x0); FAIL; + + // __NR_fsync 118 + GO(__NR_fsync, "1s 0m"); + SY(__NR_fsync, x0-1); FAIL; + + // __NR_sigreturn 119 + GO(__NR_sigreturn, "n/a"); + //SY(__NR_sigreturn); // (Not yet handled by Valgrind) FAIL; + + // __NR_clone 120 +#ifndef CLONE_PARENT_SETTID +#define CLONE_PARENT_SETTID 0x00100000 +#endif + // XXX: should really be "4s 2m"? Not sure... (see PRE(sys_clone)) + GO(__NR_clone, "4s 0m"); + SY(__NR_clone, x0|CLONE_PARENT_SETTID|SIGCHLD, x0, x0, x0); FAIL; + if (0 == res) { + SY(__NR_exit, 0); FAIL; + } + + // __NR_setdomainname 121 + GO(__NR_setdomainname, "n/a"); + //SY(__NR_setdomainname); // (Not yet handled by Valgrind) FAIL; + + // __NR_uname 122 + GO(__NR_uname, "1s 1m"); + SY(__NR_uname, x0); FAIL; + + // __NR_modify_ldt 123 + GO(__NR_modify_ldt, "3s 1m"); + SY(__NR_modify_ldt, x0+1, x0, x0+1); FAILx(EINVAL); + + // __NR_adjtimex 124 + // XXX: need to do properly, but deref'ing NULL causing Valgrind to crash... + GO(__NR_adjtimex, "XXX"); +// SY(__NR_adjtimex, x0); FAIL; + + // __NR_mprotect 125 + GO(__NR_mprotect, "3s 0m"); + SY(__NR_mprotect, x0+1, x0, x0); FAILx(EINVAL); + + // __NR_sigprocmask 126 + GO(__NR_sigprocmask, "3s 2m"); + SY(__NR_sigprocmask, x0, x0+&px[1], x0+&px[1]); SUCC; + + // __NR_create_module 127 + GO(__NR_create_module, "ni"); + SY(__NR_create_module); FAIL; + + // __NR_init_module 128 + GO(__NR_init_module, "3s 2m"); + SY(__NR_init_module, x0, x0+1, x0); FAIL; + + // __NR_delete_module 129 + GO(__NR_delete_module, "n/a"); + //SY(__NR_delete_module); // (Not yet handled by Valgrind) FAIL; + + // __NR_get_kernel_syms 130 + GO(__NR_get_kernel_syms, "ni"); + SY(__NR_get_kernel_syms); FAIL; + + // __NR_quotactl 131 + GO(__NR_quotactl, "4s 1m"); + SY(__NR_quotactl, x0, x0, x0, x0); FAIL; + + // __NR_getpgid 132 + GO(__NR_getpgid, "1s 0m"); + SY(__NR_getpgid, x0-1); FAIL; + + // __NR_fchdir 133 + GO(__NR_fchdir, "1s 0m"); + SY(__NR_fchdir, x0-1); FAIL; + + // __NR_bdflush 134 + GO(__NR_bdflush, "n/a"); + //SY(__NR_bdflush); // (Not yet handled by Valgrind) FAIL; + + // __NR_sysfs 135 + GO(__NR_sysfs, "n/a"); + //SY(__NR_sysfs); // (Not yet handled by Valgrind) FAIL; + + // __NR_personality 136 + GO(__NR_personality, "1s 0m"); + SY(__NR_personality, x0+0xffffffff); SUCC; + + // __NR_afs_syscall 137 + GO(__NR_afs_syscall, "ni"); + SY(__NR_afs_syscall); FAIL; + + // __NR_setfsuid 138 + GO(__NR_setfsuid, "1s 0m"); + SY(__NR_setfsuid, x0); SUCC; // This syscall has a stupid return value + + // __NR_setfsgid 139 + GO(__NR_setfsgid, "1s 0m"); + SY(__NR_setfsgid, x0); SUCC; // This syscall has a stupid return value + + // __NR__llseek 140 + GO(__NR__llseek, "5s 1m"); + SY(__NR__llseek, x0, x0, x0, x0, x0); FAIL; + + // __NR_getdents 141 + GO(__NR_getdents, "3s 1m"); + SY(__NR_getdents, x0, x0, x0+1); FAIL; + + // __NR__newselect 142 + GO(__NR__newselect, "5s 4m"); + SY(__NR__newselect, x0+8, x0+0xffffffff, x0+1, x0+1, x0+1); FAIL; + + // __NR_flock 143 + GO(__NR_flock, "2s 0m"); + SY(__NR_flock, x0, x0); FAIL; + + // __NR_msync 144 + GO(__NR_msync, "3s 1m"); + SY(__NR_msync, x0, x0+1, x0); FAIL; + + // __NR_readv 145 + GO(__NR_readv, "3s 1m"); + SY(__NR_readv, x0, x0, x0+1); FAIL; + + // __NR_writev 146 + GO(__NR_writev, "3s 1m"); + SY(__NR_writev, x0, x0, x0+1); FAIL; + + // __NR_getsid 147 + GO(__NR_getsid, "1s 0m"); + SY(__NR_getsid, x0-1); FAIL; + + // __NR_fdatasync 148 + GO(__NR_fdatasync, "1s 0m"); + SY(__NR_fdatasync, x0-1); FAIL; + + // __NR__sysctl 149 + GO(__NR__sysctl, "1s 1m"); + SY(__NR__sysctl, x0); FAIL; + + // __NR_mlock 150 + GO(__NR_mlock, "2s 0m"); + SY(__NR_mlock, x0, x0+1); FAIL; + + // __NR_munlock 151 + GO(__NR_munlock, "2s 0m"); + SY(__NR_munlock, x0, x0+1); FAIL; + + // __NR_mlockall 152 + GO(__NR_mlockall, "1s 0m"); + SY(__NR_mlockall, x0-1); FAIL; + + // __NR_munlockall 153 + GO(__NR_munlockall, "0s 0m"); + SY(__NR_munlockall); SUCC_OR_FAILx(EPERM); + + // __NR_sched_setparam 154 + GO(__NR_sched_setparam, "2s 1m"); + SY(__NR_sched_setparam, x0, x0); FAIL; + + // __NR_sched_getparam 155 + GO(__NR_sched_getparam, "2s 1m"); + SY(__NR_sched_getparam, x0, x0); FAIL; + + // __NR_sched_setscheduler 156 + GO(__NR_sched_setscheduler, "3s 1m"); + SY(__NR_sched_setscheduler, x0-1, x0, x0+1); FAIL; + + // __NR_sched_getscheduler 157 + GO(__NR_sched_getscheduler, "1s 0m"); + SY(__NR_sched_getscheduler, x0-1); FAIL; + + // __NR_sched_yield 158 + GO(__NR_sched_yield, "0s 0m"); + SY(__NR_sched_yield); SUCC; + + // __NR_sched_get_priority_max 159 + GO(__NR_sched_get_priority_max, "1s 0m"); + SY(__NR_sched_get_priority_max, x0-1); FAIL; + + // __NR_sched_get_priority_min 160 + GO(__NR_sched_get_priority_min, "1s 0m"); + SY(__NR_sched_get_priority_min, x0-1); FAIL; + + // __NR_sched_rr_get_interval 161 + GO(__NR_sched_rr_get_interval, "n/a"); + //SY(__NR_sched_rr_get_interval); // (Not yet handled by Valgrind) FAIL; + + // __NR_nanosleep 162 + GO(__NR_nanosleep, "2s 2m"); + SY(__NR_nanosleep, x0, x0+1); FAIL; + + // __NR_mremap 163 + GO(__NR_mremap, "5s 0m"); + SY(__NR_mremap, x0+1, x0, x0, x0+MREMAP_FIXED, x0); FAILx(EINVAL); + + // __NR_setresuid 164 + GO(__NR_setresuid, "3s 0m"); + SY(__NR_setresuid, x0, x0, x0); FAIL; + + // __NR_getresuid 165 + GO(__NR_getresuid, "3s 3m"); + SY(__NR_getresuid, x0, x0, x0); FAIL; + + // __NR_vm86 166 + GO(__NR_vm86, "n/a"); + // (will probably never be handled by Valgrind) + + // __NR_query_module 167 + GO(__NR_query_module, "ni"); + SY(__NR_query_module); FAIL; + + // __NR_poll 168 + GO(__NR_poll, "3s 1m"); + SY(__NR_poll, x0, x0+1, x0); FAIL; + + // __NR_nfsservctl 169 + GO(__NR_nfsservctl, "n/a"); + //SY(__NR_nfsservctl); // (Not yet handled by Valgrind) FAIL; + + // __NR_setresgid 170 + GO(__NR_setresgid, "3s 0m"); + SY(__NR_setresgid, x0, x0, x0); FAIL; + + // __NR_getresgid 171 + GO(__NR_getresgid, "3s 3m"); + SY(__NR_getresgid, x0, x0, x0); FAIL; + + // __NR_prctl 172 + GO(__NR_prctl, "5s 0m"); + SY(__NR_prctl, x0, x0, x0, x0, x0); FAIL; + + // __NR_rt_sigreturn 173 + GO(__NR_rt_sigreturn, "n/a"); + //SY(__NR_rt_sigreturn); // (Not yet handled by Valgrind) FAIL; + + // __NR_rt_sigaction 174 + GO(__NR_rt_sigaction, "4s 4m"); + SY(__NR_rt_sigaction, x0, x0+&px[2], x0+&px[2], x0); FAIL; + + // __NR_rt_sigprocmask 175 + GO(__NR_rt_sigprocmask, "4s 2m"); + SY(__NR_rt_sigprocmask, x0, x0+1, x0+1, x0); FAIL; + + // __NR_rt_sigpending 176 + GO(__NR_rt_sigpending, "2s 1m"); + SY(__NR_rt_sigpending, x0, x0+1); FAIL; + + // __NR_rt_sigtimedwait 177 + GO(__NR_rt_sigtimedwait, "4s 3m"); + SY(__NR_rt_sigtimedwait, x0+1, x0+1, x0+1, x0); FAIL; + + // __NR_rt_sigqueueinfo 178 + GO(__NR_rt_sigqueueinfo, "3s 1m"); + SY(__NR_rt_sigqueueinfo, x0, x0+1, x0); FAIL; + + // __NR_rt_sigsuspend 179 + GO(__NR_rt_sigsuspend, "ignore"); + // (I don't know how to test this...) + + // __NR_pread64 180 + GO(__NR_pread64, "5s 1m"); + SY(__NR_pread64, x0, x0, x0+1, x0, x0); FAIL; + + // __NR_pwrite64 181 + GO(__NR_pwrite64, "5s 1m"); + SY(__NR_pwrite64, x0, x0, x0+1, x0, x0); FAIL; + + // __NR_chown 182 + GO(__NR_chown, "3s 1m"); + SY(__NR_chown, x0, x0, x0); FAIL; + + // __NR_getcwd 183 + GO(__NR_getcwd, "2s 1m"); + SY(__NR_getcwd, x0, x0+1); FAIL; + + // __NR_capget 184 + GO(__NR_capget, "2s 2m"); + SY(__NR_capget, x0, x0); FAIL; + + // __NR_capset 185 + GO(__NR_capset, "2s 2m"); + SY(__NR_capset, x0, x0); FAIL; + + // __NR_sigaltstack 186 + { + struct our_sigaltstack { + void *ss_sp; + int ss_flags; + size_t ss_size; + } ss; + ss.ss_sp = NULL; + ss.ss_flags = 0; + ss.ss_size = 0; + VALGRIND_MAKE_MEM_NOACCESS(& ss, sizeof(struct our_sigaltstack)); + GO(__NR_sigaltstack, "2s 2m"); + SY(__NR_sigaltstack, x0+&ss, x0+&ss); SUCC; + } + + // __NR_sendfile 187 + GO(__NR_sendfile, "4s 1m"); + SY(__NR_sendfile, x0, x0, x0+1, x0); FAIL; + + // __NR_getpmsg 188 + // Could do 5s 4m with more effort, but I can't be bothered for this + // crappy non-standard syscall. + GO(__NR_getpmsg, "5s 0m"); + SY(__NR_getpmsg, x0, x0, x0, x0); FAIL; + + // __NR_putpmsg 189 + // Could do 5s 2m with more effort, but I can't be bothered for this + // crappy non-standard syscall. + GO(__NR_putpmsg, "5s 0m"); + SY(__NR_putpmsg, x0, x0, x0, x0, x0); FAIL; + + // __NR_vfork 190 + GO(__NR_vfork, "other"); + // (sse scalar_vfork.c) + + // __NR_ugetrlimit 191 + GO(__NR_ugetrlimit, "2s 1m"); + SY(__NR_ugetrlimit, x0, x0); FAIL; + + // __NR_mmap2 192 + GO(__NR_mmap2, "6s 0m"); + SY(__NR_mmap2, x0, x0, x0, x0, x0-1, x0); FAIL; + + // __NR_truncate64 193 + GO(__NR_truncate64, "3s 1m"); + SY(__NR_truncate64, x0, x0, x0); FAIL; + + // __NR_ftruncate64 194 + GO(__NR_ftruncate64, "3s 0m"); + SY(__NR_ftruncate64, x0, x0, x0); FAIL; + + // __NR_stat64 195 + GO(__NR_stat64, "2s 2m"); + SY(__NR_stat64, x0, x0); FAIL; + + // __NR_lstat64 196 + GO(__NR_lstat64, "2s 2m"); + SY(__NR_lstat64, x0, x0); FAIL; + + // __NR_fstat64 197 + GO(__NR_fstat64, "2s 1m"); + SY(__NR_fstat64, x0, x0); FAIL; + + // __NR_lchown32 198 + GO(__NR_lchown32, "3s 1m"); + SY(__NR_lchown32, x0, x0, x0); FAIL; + + // __NR_getuid32 199 + GO(__NR_getuid32, "0s 0m"); + SY(__NR_getuid32); SUCC; + + // __NR_getgid32 200 + GO(__NR_getgid32, "0s 0m"); + SY(__NR_getgid32); SUCC; + + // __NR_geteuid32 201 + GO(__NR_geteuid32, "0s 0m"); + SY(__NR_geteuid32); SUCC; + + // __NR_getegid32 202 + GO(__NR_getegid32, "0s 0m"); + SY(__NR_getegid32); SUCC; + + // __NR_setreuid32 203 + GO(__NR_setreuid32, "2s 0m"); + SY(__NR_setreuid32, x0, x0); FAIL; + + // __NR_setregid32 204 + GO(__NR_setregid32, "2s 0m"); + SY(__NR_setregid32, x0, x0); FAIL; + + // __NR_getgroups32 205 + GO(__NR_getgroups32, "2s 1m"); + SY(__NR_getgroups32, x0+1, x0+1); FAIL; + + // __NR_setgroups32 206 + GO(__NR_setgroups32, "2s 1m"); + SY(__NR_setgroups32, x0+1, x0+1); FAIL; + + // __NR_fchown32 207 + GO(__NR_fchown32, "3s 0m"); + SY(__NR_fchown32, x0, x0, x0); FAIL; + + // __NR_setresuid32 208 + GO(__NR_setresuid32, "3s 0m"); + SY(__NR_setresuid32, x0, x0, x0); FAIL; + + // __NR_getresuid32 209 + GO(__NR_getresuid32, "3s 3m"); + SY(__NR_getresuid32, x0, x0, x0); FAIL; + + // __NR_setresgid32 210 + GO(__NR_setresgid32, "3s 0m"); + SY(__NR_setresgid32, x0, x0, x0); FAIL; + + // __NR_getresgid32 211 + GO(__NR_getresgid32, "3s 3m"); + SY(__NR_getresgid32, x0, x0, x0); FAIL; + + // __NR_chown32 212 + GO(__NR_chown32, "3s 1m"); + SY(__NR_chown32, x0, x0, x0); FAIL; + + // __NR_setuid32 213 + GO(__NR_setuid32, "1s 0m"); + SY(__NR_setuid32, x0); FAIL; + + // __NR_setgid32 214 + GO(__NR_setgid32, "1s 0m"); + SY(__NR_setgid32, x0); FAIL; + + // __NR_setfsuid32 215 + GO(__NR_setfsuid32, "1s 0m"); + SY(__NR_setfsuid32, x0); SUCC; // This syscall has a stupid return value + + // __NR_setfsgid32 216 + GO(__NR_setfsgid32, "1s 0m"); + SY(__NR_setfsgid32, x0); SUCC; // This syscall has a stupid return value + + // __NR_pivot_root 217 + GO(__NR_pivot_root, "n/a"); + //SY(__NR_pivot_root); // (Not yet handled by Valgrind) FAIL; + + // __NR_mincore 218 + GO(__NR_mincore, "3s 1m"); + SY(__NR_mincore, x0, x0+40960, x0); FAIL; + + // __NR_madvise 219 + GO(__NR_madvise, "3s 0m"); + SY(__NR_madvise, x0, x0+1, x0); FAILx(ENOMEM); + + // __NR_getdents64 220 + GO(__NR_getdents64, "3s 1m"); + SY(__NR_getdents64, x0, x0, x0+1); FAIL; + + // __NR_fcntl64 221 + // As with sys_open(), we don't trigger errors for the 1st two args for + // the later ones. + // For F_GETFD the 3rd arg is ignored. + GO(__NR_fcntl64, "(GETFD) 2s 0m"); + SY(__NR_fcntl64, x0-1, x0+F_GETFD, x0); FAILx(EBADF); + + // For F_DUPFD the 3rd arg is 'arg' + GO(__NR_fcntl64, "(DUPFD) 1s 0m"); + SY(__NR_fcntl64, -1, F_DUPFD, x0); FAILx(EBADF); + + // For F_GETLK the 3rd arg is 'lock'. + // On x86, this fails w/EBADF. But on amd64 in 32-bit mode it fails + // w/EFAULT. + GO(__NR_fcntl64, "(GETLK) 1s 0m"); + SY(__NR_fcntl64, -1, +F_GETLK, x0); FAIL; //FAILx(EBADF); + + // 222 + GO(222, "ni"); + SY(222); FAIL; + + // 223 + GO(223, "ni"); + SY(223); FAIL; + + // __NR_gettid 224 + GO(__NR_gettid, "n/a"); + //SY(__NR_gettid); // (Not yet handled by Valgrind) FAIL; + + // __NR_readahead 225 + GO(__NR_readahead, "n/a"); + //SY(__NR_readahead); // (Not yet handled by Valgrind) FAIL; + + // __NR_setxattr 226 + GO(__NR_setxattr, "5s 3m"); + SY(__NR_setxattr, x0, x0, x0, x0+1, x0); FAIL; + + // __NR_lsetxattr 227 + GO(__NR_lsetxattr, "5s 3m"); + SY(__NR_lsetxattr, x0, x0, x0, x0+1, x0); FAIL; + + // __NR_fsetxattr 228 + GO(__NR_fsetxattr, "5s 2m"); + SY(__NR_fsetxattr, x0, x0, x0, x0+1, x0); FAIL; + + // __NR_getxattr 229 + GO(__NR_getxattr, "4s 3m"); + SY(__NR_getxattr, x0, x0, x0, x0+1); FAIL; + + // __NR_lgetxattr 230 + GO(__NR_lgetxattr, "4s 3m"); + SY(__NR_lgetxattr, x0, x0, x0, x0+1); FAIL; + + // __NR_fgetxattr 231 + GO(__NR_fgetxattr, "4s 2m"); + SY(__NR_fgetxattr, x0, x0, x0, x0+1); FAIL; + + // __NR_listxattr 232 + GO(__NR_listxattr, "3s 2m"); + SY(__NR_listxattr, x0, x0, x0+1); FAIL; + + // __NR_llistxattr 233 + GO(__NR_llistxattr, "3s 2m"); + SY(__NR_llistxattr, x0, x0, x0+1); FAIL; + + // __NR_flistxattr 234 + GO(__NR_flistxattr, "3s 1m"); + SY(__NR_flistxattr, x0-1, x0, x0+1); FAIL; /* kernel returns EBADF, but both seem correct */ + + // __NR_removexattr 235 + GO(__NR_removexattr, "2s 2m"); + SY(__NR_removexattr, x0, x0); FAIL; + + // __NR_lremovexattr 236 + GO(__NR_lremovexattr, "2s 2m"); + SY(__NR_lremovexattr, x0, x0); FAIL; + + // __NR_fremovexattr 237 + GO(__NR_fremovexattr, "2s 1m"); + SY(__NR_fremovexattr, x0, x0); FAIL; + + // __NR_tkill 238 + GO(__NR_tkill, "n/a"); + //SY(__NR_tkill); // (Not yet handled by Valgrind) FAIL; + + // __NR_sendfile64 239 + GO(__NR_sendfile64, "4s 1m"); + SY(__NR_sendfile64, x0, x0, x0+1, x0); FAIL; + + // __NR_futex 240 + #ifndef FUTEX_WAIT + #define FUTEX_WAIT 0 + #endif + // XXX: again, glibc not doing 6th arg means we have only 5s errors + GO(__NR_futex, "5s 2m"); + SY(__NR_futex, x0+FUTEX_WAIT, x0, x0, x0+1, x0, x0); FAIL; + + // __NR_sched_setaffinity 241 + GO(__NR_sched_setaffinity, "3s 1m"); + SY(__NR_sched_setaffinity, x0, x0+1, x0); FAIL; + + // __NR_sched_getaffinity 242 + GO(__NR_sched_getaffinity, "3s 1m"); + SY(__NR_sched_getaffinity, x0, x0+1, x0); FAIL; + + // __NR_set_thread_area 243 + GO(__NR_set_thread_area, "1s 1m"); + SY(__NR_set_thread_area, x0); FAILx(EFAULT); + + // __NR_get_thread_area 244 + GO(__NR_get_thread_area, "1s 1m"); + SY(__NR_get_thread_area, x0); FAILx(EFAULT); + + // __NR_io_setup 245 + GO(__NR_io_setup, "2s 1m"); + SY(__NR_io_setup, x0, x0); FAIL; + + // __NR_io_destroy 246 + { + // jump through hoops to prevent the PRE(io_destroy) wrapper crashing. + struct fake_aio_ring { + unsigned id; /* kernel internal index number */ + unsigned nr; /* number of io_events */ + // There are more fields in the real aio_ring, but the 'nr' field is + // the only one used by the PRE() wrapper. + } ring = { 0, 0 }; + struct fake_aio_ring* ringptr = ˚ + GO(__NR_io_destroy, "1s 0m"); + SY(__NR_io_destroy, x0+&ringptr); FAIL; + } + + // __NR_io_getevents 247 + GO(__NR_io_getevents, "5s 2m"); + SY(__NR_io_getevents, x0, x0, x0+1, x0, x0+1); FAIL; + + // __NR_io_submit 248 + GO(__NR_io_submit, "3s 1m"); + SY(__NR_io_submit, x0, x0+1, x0); FAIL; + + // __NR_io_cancel 249 + GO(__NR_io_cancel, "3s 2m"); + SY(__NR_io_cancel, x0, x0, x0); FAIL; + + // __NR_fadvise64 250 + GO(__NR_fadvise64, "n/a"); + //SY(__NR_fadvise64); // (Not yet handled by Valgrind) FAIL; + + // 251 + GO(251, "ni"); + SY(251); FAIL; + + // __NR_exit_group 252 + GO(__NR_exit_group, "other"); + // (see scalar_exit_group.c) + + // __NR_lookup_dcookie 253 + GO(__NR_lookup_dcookie, "4s 1m"); + SY(__NR_lookup_dcookie, x0, x0, x0, x0+1); FAIL; + + // __NR_epoll_create 254 + GO(__NR_epoll_create, "1s 0m"); + SY(__NR_epoll_create, x0); SUCC_OR_FAIL; + + // __NR_epoll_ctl 255 + GO(__NR_epoll_ctl, "4s 1m"); + SY(__NR_epoll_ctl, x0, x0, x0, x0); FAIL; + + // __NR_epoll_wait 256 + GO(__NR_epoll_wait, "4s 1m"); + SY(__NR_epoll_wait, x0, x0, x0+1, x0); FAIL; + + // __NR_remap_file_pages 257 + GO(__NR_remap_file_pages, "n/a"); + //SY(__NR_remap_file_pages); // (Not yet handled by Valgrind) FAIL; + + // __NR_set_tid_address 258 + GO(__NR_set_tid_address, "1s 0m"); + SY(__NR_set_tid_address, x0); SUCC_OR_FAILx(ENOSYS); + + // __NR_timer_create 259 + GO(__NR_timer_create, "3s 2m"); + SY(__NR_timer_create, x0, x0+1, x0); FAIL; + + // __NR_timer_settime (__NR_timer_create+1) + GO(__NR_timer_settime, "4s 2m"); + SY(__NR_timer_settime, x0, x0, x0, x0+1); FAIL; + + // __NR_timer_gettime (__NR_timer_create+2) + GO(__NR_timer_gettime, "2s 1m"); + SY(__NR_timer_gettime, x0, x0); FAIL; + + // __NR_timer_getoverrun (__NR_timer_create+3) + GO(__NR_timer_getoverrun, "1s 0m"); + SY(__NR_timer_getoverrun, x0); FAIL; + + // __NR_timer_delete (__NR_timer_create+4) + GO(__NR_timer_delete, "1s 0m"); + SY(__NR_timer_delete, x0); FAIL; + + // __NR_clock_settime (__NR_timer_create+5) + GO(__NR_clock_settime, "2s 1m"); + SY(__NR_clock_settime, x0, x0); FAIL; FAIL; + + // __NR_clock_gettime (__NR_timer_create+6) + GO(__NR_clock_gettime, "2s 1m"); + SY(__NR_clock_gettime, x0, x0); FAIL; + + // __NR_clock_getres (__NR_timer_create+7) + GO(__NR_clock_getres, "2s 1m"); + SY(__NR_clock_getres, x0+1, x0+1); FAIL; FAIL; + + // __NR_clock_nanosleep (__NR_timer_create+8) + GO(__NR_clock_nanosleep, "n/a"); + //SY(__NR_clock_nanosleep); // (Not yet handled by Valgrind) FAIL; + + // __NR_statfs64 268 + GO(__NR_statfs64, "3s 2m"); + SY(__NR_statfs64, x0, x0+1, x0); FAIL; + + // __NR_fstatfs64 269 + GO(__NR_fstatfs64, "3s 1m"); + SY(__NR_fstatfs64, x0, x0+1, x0); FAIL; + + // __NR_tgkill 270 + GO(__NR_tgkill, "n/a"); + //SY(__NR_tgkill); // (Not yet handled by Valgrind) FAIL; + + // __NR_utimes 271 + GO(__NR_utimes, "2s 2m"); + SY(__NR_utimes, x0, x0+1); FAIL; + + // __NR_fadvise64_64 272 + GO(__NR_fadvise64_64, "n/a"); + //SY(__NR_fadvise64_64); // (Not yet handled by Valgrind) FAIL; + + // __NR_vserver 273 + GO(__NR_vserver, "ni"); + SY(__NR_vserver); FAIL; + + // __NR_mbind 274 + GO(__NR_mbind, "n/a"); + //SY(__NR_mbind); // (Not yet handled by Valgrind) FAIL; + + // __NR_get_mempolicy 275 + GO(__NR_get_mempolicy, "n/a"); + //SY(__NR_get_mempolicy); // (Not yet handled by Valgrind) FAIL; + + // __NR_set_mempolicy 276 + GO(__NR_set_mempolicy, "n/a"); + //SY(__NR_set_mempolicy); // (Not yet handled by Valgrind) FAIL; + + // __NR_mq_open 277 + GO(__NR_mq_open, "4s 3m"); + SY(__NR_mq_open, x0, x0+O_CREAT, x0, x0+1); FAIL; + + // __NR_mq_unlink (__NR_mq_open+1) + GO(__NR_mq_unlink, "1s 1m"); + SY(__NR_mq_unlink, x0); FAIL; + + // __NR_mq_timedsend (__NR_mq_open+2) + GO(__NR_mq_timedsend, "5s 2m"); + SY(__NR_mq_timedsend, x0, x0, x0+1, x0, x0+1); FAIL; + + // __NR_mq_timedreceive (__NR_mq_open+3) + GO(__NR_mq_timedreceive, "5s 3m"); + SY(__NR_mq_timedreceive, x0, x0, x0+1, x0+1, x0+1); FAIL; + + // __NR_mq_notify (__NR_mq_open+4) + GO(__NR_mq_notify, "2s 1m"); + SY(__NR_mq_notify, x0, x0+1); FAIL; + + // __NR_mq_getsetattr (__NR_mq_open+5) + GO(__NR_mq_getsetattr, "3s 2m"); + SY(__NR_mq_getsetattr, x0, x0+1, x0+1); FAIL; + + // __NR_sys_kexec_load 283 + GO(__NR_sys_kexec_load, "ni"); + SY(__NR_sys_kexec_load); FAIL; +#endif + + // no such syscall... + GO(9999, "1e"); + SY(9999); FAIL; + + // __NR_exit 1 + GO(__NR_exit, "1s 0m"); + SY(__NR_exit, x0); FAIL; + + assert(0); +} + diff --git a/memcheck/tests/darwin/scalar.h b/memcheck/tests/darwin/scalar.h new file mode 100644 index 000000000..8bfd2e15a --- /dev/null +++ b/memcheck/tests/darwin/scalar.h @@ -0,0 +1,57 @@ +#include "vki/vki-scnums-darwin.h" +#include "pub_tool_vkiscnums.h" + +#include +#include +#include +#include +#include + +// Since we use vki_unistd.h, we can't include . So we have to +// declare this ourselves. +extern int syscall (int __sysno, ...); + +// Thorough syscall scalar arg checking. Also serves as thorough checking +// for (very) basic syscall use. Generally not trying to do anything +// meaningful with the syscalls. + +#define GO(__NR_xxx, s) \ + fprintf(stderr, "-----------------------------------------------------\n" \ + "%3d:%20s %s\n" \ + "-----------------------------------------------------\n", \ + VG_DARWIN_SYSNO_PRINT(__NR_xxx), #__NR_xxx, s); + +#define SY(__NR_xxx, args...) res = syscall(__NR_xxx, ##args); + +#define FAIL assert(-1 == res); +#define SUCC assert(-1 != res); +#define SUCC_OR_FAIL /* no test */ + +#define FAILx(E) \ + do { \ + int myerrno = errno; \ + if (-1 == res) { \ + if (E == myerrno) { \ + /* as expected */ \ + } else { \ + fprintf(stderr, "Expected error %s (%d), got %d\n", #E, E, myerrno); \ + exit(1); \ + } \ + } else { \ + fprintf(stderr, "Expected error %s (%d), got success\n", #E, E); \ + exit(1); \ + } \ + } while (0); + +#define SUCC_OR_FAILx(E) \ + do { \ + int myerrno = errno; \ + if (-1 == res) { \ + if (E == myerrno) { \ + /* as expected */ \ + } else { \ + fprintf(stderr, "Expected error %s (%d), got %d\n", #E, E, myerrno); \ + exit(1); \ + } \ + } \ + } while (0); diff --git a/memcheck/tests/darwin/scalar.stderr.exp b/memcheck/tests/darwin/scalar.stderr.exp new file mode 100644 index 000000000..7b383ccb7 --- /dev/null +++ b/memcheck/tests/darwin/scalar.stderr.exp @@ -0,0 +1,208 @@ +----------------------------------------------------- + 1: __NR_exit below +----------------------------------------------------- +----------------------------------------------------- + 2: __NR_fork other +----------------------------------------------------- +----------------------------------------------------- + 3: __NR_read 1+3s 1m +----------------------------------------------------- +Syscall param (syscallno) contains uninitialised byte(s) + ... + +Syscall param read(fd) contains uninitialised byte(s) + ... + +Syscall param read(buf) contains uninitialised byte(s) + ... + +Syscall param read(count) contains uninitialised byte(s) + ... + +Syscall param read(buf) points to unaddressable byte(s) + ... + Address 0x........ is not stack'd, malloc'd or (recently) free'd +----------------------------------------------------- + 4: __NR_write 3s 1m +----------------------------------------------------- + +Syscall param write(fd) contains uninitialised byte(s) + ... + +Syscall param write(buf) contains uninitialised byte(s) + ... + +Syscall param write(count) contains uninitialised byte(s) + ... + +Syscall param write(buf) points to unaddressable byte(s) + ... + Address 0x........ is not stack'd, malloc'd or (recently) free'd +----------------------------------------------------- +105: __NR_setsockopt 5s 1m +----------------------------------------------------- + +Syscall param setsockopt(s) contains uninitialised byte(s) + ... + +Syscall param setsockopt(level) contains uninitialised byte(s) + ... + +Syscall param setsockopt(optname) contains uninitialised byte(s) + ... + +Syscall param setsockopt(optval) contains uninitialised byte(s) + ... + +Syscall param setsockopt(optlen) contains uninitialised byte(s) + ... + +Syscall param socketcall.setsockopt(optval) points to unaddressable byte(s) + ... + Address 0x........ is not stack'd, malloc'd or (recently) free'd +----------------------------------------------------- +118: __NR_getsockopt 5s 1m +----------------------------------------------------- + +Syscall param getsockopt(s) contains uninitialised byte(s) + ... + +Syscall param getsockopt(level) contains uninitialised byte(s) + ... + +Syscall param getsockopt(optname) contains uninitialised byte(s) + ... + +Syscall param getsockopt(optval) contains uninitialised byte(s) + ... + +Syscall param getsockopt(optlen) contains uninitialised byte(s) + ... + +Syscall param socketcall.getsockopt(optlen) points to unaddressable byte(s) + ... + Address 0x........ is 0 bytes after a block of size 4 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (scalar.c:13) +----------------------------------------------------- +169: __NR_csops 4s 1m +----------------------------------------------------- + +Syscall param csops(pid) contains uninitialised byte(s) + ... + +Syscall param csops(ops) contains uninitialised byte(s) + ... + +Syscall param csops(useraddr) contains uninitialised byte(s) + ... + +Syscall param csops(usersize) contains uninitialised byte(s) + ... + +Syscall param csops(addr) points to unaddressable byte(s) + ... + Address 0x........ is not stack'd, malloc'd or (recently) free'd +----------------------------------------------------- +265: __NR_shmget 3s 0m +----------------------------------------------------- + +Syscall param shmget(key) contains uninitialised byte(s) + ... + +Syscall param shmget(size) contains uninitialised byte(s) + ... + +Syscall param shmget(shmflg) contains uninitialised byte(s) + ... +----------------------------------------------------- +268: __NR_sem_open 2s 1m +----------------------------------------------------- + +Syscall param sem_open(name) contains uninitialised byte(s) + ... + +Syscall param sem_open(oflag) contains uninitialised byte(s) + ... + +Syscall param sem_open(name) points to unaddressable byte(s) + ... + Address 0x........ is not stack'd, malloc'd or (recently) free'd +----------------------------------------------------- +268: __NR_sem_open (4-args) 2s 0m +----------------------------------------------------- + +Syscall param sem_open(mode) contains uninitialised byte(s) + ... + +Syscall param sem_open(value) contains uninitialised byte(s) + ... +----------------------------------------------------- +269: __NR_sem_close 1s 0m +----------------------------------------------------- + +Syscall param sem_close(sem) contains uninitialised byte(s) + ... +----------------------------------------------------- +270: __NR_sem_unlink 1s 1m +----------------------------------------------------- + +Syscall param sem_unlink(name) contains uninitialised byte(s) + ... + +Syscall param sem_unlink(name) points to unaddressable byte(s) + ... + Address 0x........ is not stack'd, malloc'd or (recently) free'd +----------------------------------------------------- +273: __NR_sem_post 1s 0m +----------------------------------------------------- + +Syscall param sem_post(sem) contains uninitialised byte(s) + ... +----------------------------------------------------- +275: __NR_sem_init 3s 1m +----------------------------------------------------- + +Syscall param sem_init(sem) contains uninitialised byte(s) + ... + +Syscall param sem_init(pshared) contains uninitialised byte(s) + ... + +Syscall param sem_init(value) contains uninitialised byte(s) + ... + +Syscall param sem_init(sem) points to unaddressable byte(s) + ... + Address 0x........ is not stack'd, malloc'd or (recently) free'd +----------------------------------------------------- +276: __NR_sem_destroy 1s 1m +----------------------------------------------------- + +Syscall param sem_destroy(sem) contains uninitialised byte(s) + ... + +Syscall param sem_destroy(sem) points to unaddressable byte(s) + ... + Address 0x........ is not stack'd, malloc'd or (recently) free'd +----------------------------------------------------- +420:__NR_sem_wait_nocancel 1s 0m +----------------------------------------------------- + +Syscall param sem_wait_nocancel(sem) contains uninitialised byte(s) + ... +----------------------------------------------------- +9999: 9999 1e +----------------------------------------------------- +WARNING: unhandled syscall: 33564431 + a.k.a.: 9999 +You may be able to write your own handler. +Read the file README_MISSING_SYSCALL_OR_IOCTL. +Nevertheless we consider this a bug. Please report +it at http://valgrind.org/support/bug_reports.html. +----------------------------------------------------- + 1: __NR_exit 1s 0m +----------------------------------------------------- + +Syscall param exit(status) contains uninitialised byte(s) + ... diff --git a/memcheck/tests/darwin/scalar.vgtest b/memcheck/tests/darwin/scalar.vgtest new file mode 100644 index 000000000..897d9e73c --- /dev/null +++ b/memcheck/tests/darwin/scalar.vgtest @@ -0,0 +1,3 @@ +prog: scalar +vgopts: -q --error-limit=no +args: < scalar.c diff --git a/memcheck/tests/darwin/scalar_fork.c b/memcheck/tests/darwin/scalar_fork.c new file mode 100644 index 000000000..805ade844 --- /dev/null +++ b/memcheck/tests/darwin/scalar_fork.c @@ -0,0 +1,15 @@ +#include "scalar.h" + +int main(void) +{ + int res; + + // All __NR_xxx numbers are taken from x86 + + // __NR_fork 2 --> arch/sys_fork() + GO(__NR_fork, "0e"); + SY(__NR_fork); + + return(0); +} + diff --git a/memcheck/tests/darwin/scalar_fork.stderr.exp b/memcheck/tests/darwin/scalar_fork.stderr.exp new file mode 100644 index 000000000..e898498e9 --- /dev/null +++ b/memcheck/tests/darwin/scalar_fork.stderr.exp @@ -0,0 +1,3 @@ +----------------------------------------------------- + 2: __NR_fork 0e +----------------------------------------------------- diff --git a/memcheck/tests/darwin/scalar_fork.vgtest b/memcheck/tests/darwin/scalar_fork.vgtest new file mode 100644 index 000000000..e38fdf6eb --- /dev/null +++ b/memcheck/tests/darwin/scalar_fork.vgtest @@ -0,0 +1,2 @@ +prog: scalar_fork +vgopts: -q diff --git a/memcheck/tests/darwin/scalar_vfork.c b/memcheck/tests/darwin/scalar_vfork.c new file mode 100644 index 000000000..5f9cea8c0 --- /dev/null +++ b/memcheck/tests/darwin/scalar_vfork.c @@ -0,0 +1,13 @@ +#include "scalar.h" + +int main(void) +{ + int res; + + // __NR_vfork 66 --> __NR_fork [we can't use sys_vfork()] + GO(__NR_vfork, "0e"); + SY(__NR_vfork); + + return(0); +} + diff --git a/memcheck/tests/darwin/scalar_vfork.stderr.exp b/memcheck/tests/darwin/scalar_vfork.stderr.exp new file mode 100644 index 000000000..32644bd5a --- /dev/null +++ b/memcheck/tests/darwin/scalar_vfork.stderr.exp @@ -0,0 +1,3 @@ +----------------------------------------------------- + 66: __NR_vfork 0e +----------------------------------------------------- diff --git a/memcheck/tests/darwin/scalar_vfork.vgtest b/memcheck/tests/darwin/scalar_vfork.vgtest new file mode 100644 index 000000000..3ddad70a4 --- /dev/null +++ b/memcheck/tests/darwin/scalar_vfork.vgtest @@ -0,0 +1,2 @@ +prog: scalar_vfork +vgopts: -q diff --git a/memcheck/tests/linux/Makefile.am b/memcheck/tests/linux/Makefile.am index 81a2c9bd4..664ee632b 100644 --- a/memcheck/tests/linux/Makefile.am +++ b/memcheck/tests/linux/Makefile.am @@ -22,3 +22,4 @@ AM_CXXFLAGS += $(AM_FLAG_M3264_PRI) stack_switch_LDADD = -lpthread timerfd_syscall_LDADD = -lrt + diff --git a/memcheck/tests/malloc_usable.c b/memcheck/tests/malloc_usable.c index 4e2d2a048..ab57663a1 100644 --- a/memcheck/tests/malloc_usable.c +++ b/memcheck/tests/malloc_usable.c @@ -5,7 +5,7 @@ int main(void) { -# if !defined(VGO_aix5) +# if !defined(VGO_aix5) && !defined(VGO_darwin) // Because Memcheck marks any slop as inaccessible, it doesn't round up // sizes for malloc_usable_size(). int* x = malloc(99); diff --git a/memcheck/tests/memalign2.c b/memcheck/tests/memalign2.c index e163bf4ed..a3aacfe85 100644 --- a/memcheck/tests/memalign2.c +++ b/memcheck/tests/memalign2.c @@ -22,6 +22,9 @@ int main ( void ) # if defined(VGO_aix5) // AIX 5.2 has neither memalign() nor posix_memalign(); do nothing. +# elif defined(VGO_darwin) + // Likewise for Mac OS X. + # else // Nb: assuming VG_MIN_MALLOC_SZB is 8 or more... int* p; diff --git a/memcheck/tests/origin1-yes.c b/memcheck/tests/origin1-yes.c index acfd8aba7..3ebe6f0f4 100644 --- a/memcheck/tests/origin1-yes.c +++ b/memcheck/tests/origin1-yes.c @@ -86,12 +86,21 @@ int main(void) } // Heap segment (brk), uninitialised + // Nb: on Darwin, sbrk() is implemented via vm_allocate() which always + // zeroes its allocated memory. So we use a separate .exp file for Darwin, + // but we add an extra printf on Darwin only so that it cannot be + // successfully matched on non-Darwin platforms. +#if defined(VGO_darwin) + fprintf(stderr, "\nUndef 7 of 8 (brk)\n"); + fprintf(stderr, "\n(no complaint; sbrk initialises memory on Darwin)\n"); +#else { int* ptr_to_new_brk_limit = sbrk(4096); int undef_brk_int = *ptr_to_new_brk_limit; fprintf(stderr, "\nUndef 7 of 8 (brk)\n"); x += (undef_brk_int == 0x12345678 ? 15 : 26); } +#endif // User block, marked as undefined { diff --git a/memcheck/tests/origin1-yes.stderr.exp b/memcheck/tests/origin1-yes.stderr.exp index 418dcd0e6..e2af1abdc 100644 --- a/memcheck/tests/origin1-yes.stderr.exp +++ b/memcheck/tests/origin1-yes.stderr.exp @@ -45,18 +45,18 @@ Conditional jump or move depends on uninitialised value(s) Undef 7 of 8 (brk) Conditional jump or move depends on uninitialised value(s) - at 0x........: main (origin1-yes.c:93) + at 0x........: main (origin1-yes.c:101) Uninitialised value was created at 0x........: brk (in /...libc...) by 0x........: sbrk (in /...libc...) - by 0x........: main (origin1-yes.c:90) + by 0x........: main (origin1-yes.c:98) Undef 8 of 8 (MAKE_MEM_UNDEFINED) Conditional jump or move depends on uninitialised value(s) - at 0x........: main (origin1-yes.c:101) + at 0x........: main (origin1-yes.c:110) Uninitialised value was created by a client request - at 0x........: main (origin1-yes.c:99) + at 0x........: main (origin1-yes.c:108) Def 1 of 3 diff --git a/memcheck/tests/origin1-yes.stderr.exp-darwin b/memcheck/tests/origin1-yes.stderr.exp-darwin new file mode 100644 index 000000000..add1a37de --- /dev/null +++ b/memcheck/tests/origin1-yes.stderr.exp-darwin @@ -0,0 +1,60 @@ + +Undef 1 of 8 (stack, 32 bit) +Conditional jump or move depends on uninitialised value(s) + at 0x........: main (origin1-yes.c:37) + Uninitialised value was created by a stack allocation + at 0x........: main (origin1-yes.c:23) + +Undef 2 of 8 (stack, 32 bit) + +Conditional jump or move depends on uninitialised value(s) + at 0x........: main (origin1-yes.c:49) + Uninitialised value was created by a stack allocation + at 0x........: main (origin1-yes.c:23) + +Undef 3 of 8 (stack, 64 bit) + +Conditional jump or move depends on uninitialised value(s) + at 0x........: main (origin1-yes.c:56) + Uninitialised value was created by a stack allocation + at 0x........: main (origin1-yes.c:23) + +Undef 4 of 8 (mallocd, 32-bit) + +Conditional jump or move depends on uninitialised value(s) + at 0x........: main (origin1-yes.c:64) + Uninitialised value was created by a heap allocation + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (origin1-yes.c:61) + +Undef 5 of 8 (realloc) + +Conditional jump or move depends on uninitialised value(s) + at 0x........: main (origin1-yes.c:76) + Uninitialised value was created by a heap allocation + at 0x........: realloc (vg_replace_malloc.c:...) + by 0x........: main (origin1-yes.c:71) + +Undef 6 of 8 (MALLOCLIKE_BLOCK) + +Conditional jump or move depends on uninitialised value(s) + at 0x........: main (origin1-yes.c:85) + Uninitialised value was created by a heap allocation + at 0x........: main (origin1-yes.c:82) + +Undef 7 of 8 (brk) + +(no complaint; sbrk initialises memory on Darwin) + +Undef 8 of 8 (MAKE_MEM_UNDEFINED) + +Conditional jump or move depends on uninitialised value(s) + at 0x........: main (origin1-yes.c:110) + Uninitialised value was created by a client request + at 0x........: main (origin1-yes.c:108) + +Def 1 of 3 + +Def 2 of 3 + +Def 3 of 3 diff --git a/memcheck/tests/sigkill.stderr.exp-darwin b/memcheck/tests/sigkill.stderr.exp-darwin new file mode 100644 index 000000000..0e04bd87e --- /dev/null +++ b/memcheck/tests/sigkill.stderr.exp-darwin @@ -0,0 +1,192 @@ + +setting signal 1: Success +getting signal 1: Success + +setting signal 2: Success +getting signal 2: Success + +setting signal 3: Success +getting signal 3: Success + +setting signal 4: Success +getting signal 4: Success + +setting signal 5: Success +getting signal 5: Success + +setting signal 6: Success +getting signal 6: Success + +setting signal 7: Success +getting signal 7: Success + +setting signal 8: Success +getting signal 8: Success + +setting signal 9: Invalid argument +getting signal 9: Invalid argument + +setting signal 10: Success +getting signal 10: Success + +setting signal 11: Success +getting signal 11: Success + +setting signal 12: Success +getting signal 12: Success + +setting signal 13: Success +getting signal 13: Success + +setting signal 14: Success +getting signal 14: Success + +setting signal 15: Success +getting signal 15: Success + +setting signal 16: Success +getting signal 16: Success + +setting signal 17: Invalid argument +getting signal 17: Invalid argument + +setting signal 18: Success +getting signal 18: Success + +setting signal 19: Success +getting signal 19: Success + +setting signal 20: Success +getting signal 20: Success + +setting signal 21: Success +getting signal 21: Success + +setting signal 22: Success +getting signal 22: Success + +setting signal 23: Success +getting signal 23: Success + +setting signal 24: Success +getting signal 24: Success + +setting signal 25: Success +getting signal 25: Success + +setting signal 26: Success +getting signal 26: Success + +setting signal 27: Success +getting signal 27: Success + +setting signal 28: Success +getting signal 28: Success + +setting signal 29: Success +getting signal 29: Success + +setting signal 30: Success +getting signal 30: Success + +setting signal 31: Warning: ignored attempt to set SIGUSR2 handler in sigaction(); + the SIGUSR2 signal is used internally by Valgrind +Invalid argument +getting signal 31: Success + +setting signal 34: Invalid argument +getting signal 34: Invalid argument + +setting signal 35: Invalid argument +getting signal 35: Invalid argument + +setting signal 36: Invalid argument +getting signal 36: Invalid argument + +setting signal 37: Invalid argument +getting signal 37: Invalid argument + +setting signal 38: Invalid argument +getting signal 38: Invalid argument + +setting signal 39: Invalid argument +getting signal 39: Invalid argument + +setting signal 40: Invalid argument +getting signal 40: Invalid argument + +setting signal 41: Invalid argument +getting signal 41: Invalid argument + +setting signal 42: Invalid argument +getting signal 42: Invalid argument + +setting signal 43: Invalid argument +getting signal 43: Invalid argument + +setting signal 44: Invalid argument +getting signal 44: Invalid argument + +setting signal 45: Invalid argument +getting signal 45: Invalid argument + +setting signal 46: Invalid argument +getting signal 46: Invalid argument + +setting signal 47: Invalid argument +getting signal 47: Invalid argument + +setting signal 48: Invalid argument +getting signal 48: Invalid argument + +setting signal 49: Invalid argument +getting signal 49: Invalid argument + +setting signal 50: Invalid argument +getting signal 50: Invalid argument + +setting signal 51: Invalid argument +getting signal 51: Invalid argument + +setting signal 52: Invalid argument +getting signal 52: Invalid argument + +setting signal 53: Invalid argument +getting signal 53: Invalid argument + +setting signal 54: Invalid argument +getting signal 54: Invalid argument + +setting signal 55: Invalid argument +getting signal 55: Invalid argument + +setting signal 56: Invalid argument +getting signal 56: Invalid argument + +setting signal 57: Invalid argument +getting signal 57: Invalid argument + +setting signal 58: Invalid argument +getting signal 58: Invalid argument + +setting signal 59: Invalid argument +getting signal 59: Invalid argument + +setting signal 60: Invalid argument +getting signal 60: Invalid argument + +setting signal 61: Invalid argument +getting signal 61: Invalid argument + +setting signal 62: Invalid argument +getting signal 62: Invalid argument + +setting signal 65: Invalid argument +getting signal 65: Invalid argument + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) +malloc/free: in use at exit: ... bytes in ... blocks. +malloc/free: ... allocs, ... frees, ... bytes allocated. +For a detailed leak analysis, rerun with: --leak-check=yes +For counts of detected errors, rerun with: -v diff --git a/memcheck/tests/stack_changes.c b/memcheck/tests/stack_changes.c index e9d57d019..0d7438c14 100644 --- a/memcheck/tests/stack_changes.c +++ b/memcheck/tests/stack_changes.c @@ -10,7 +10,7 @@ // This test is checking the libc context calls (setcontext, etc.) and // checks that Valgrind notices their stack changes properly. -#if defined(_AIX) +#if defined(_AIX) || defined(__APPLE__) typedef ucontext_t mycontext; #else /* linux */ typedef struct ucontext mycontext; diff --git a/memcheck/tests/str_tester.c b/memcheck/tests/str_tester.c index 3c15ea7ca..7516696ee 100644 --- a/memcheck/tests/str_tester.c +++ b/memcheck/tests/str_tester.c @@ -264,6 +264,8 @@ test_stpcpy (void) SIMPLE_COPY(stpcpy, 16, "6666666666666666", 59); } +// DDD: better done by testing for the function. +#if !defined(__APPLE__) static void test_stpncpy (void) { @@ -278,6 +280,7 @@ test_stpncpy (void) check (stpncpy (one, "abcd", 6) == one + 4, 7); check (one[4] == '\0' && one[5] == '\0' && one[6] == 'x', 8); } +#endif static void test_strcat (void) @@ -464,7 +467,7 @@ test_strchr (void) } // DDD: better done by testing for the function. -#if !defined(_AIX) +#if !defined(_AIX) && !defined(__APPLE__) static void test_strchrnul (void) { @@ -501,7 +504,7 @@ test_strchrnul (void) #endif /* !defined(_AIX) */ // DDD: better done by testing for the function. -#if !defined(_AIX) +#if !defined(_AIX) && !defined(__APPLE__) static void test_rawmemchr (void) { @@ -577,7 +580,7 @@ test_strrchr (void) } // DDD: better done by testing for the function. -#if !defined(_AIX) +#if !defined(_AIX) && !defined(__APPLE__) static void test_memrchr (void) { @@ -899,7 +902,7 @@ test_strsep (void) equal(one+4, "c", 50); { -# if !defined(_AIX) +# if !defined(_AIX) && !defined(__APPLE__) char text[] = "This,is,a,test"; char *list = strdupa (text); equal (strsep (&list, ","), "This", 51); @@ -1060,7 +1063,7 @@ test_memcpy (void) } } -#if !defined(_AIX) +#if !defined(_AIX) && !defined(__APPLE__) static void test_mempcpy (void) { @@ -1276,6 +1279,7 @@ test_bzero (void) equal(one, "abcdef", 4); /* Zero-length copy. */ } +#if !defined(__APPLE__) static void test_strndup (void) { @@ -1299,6 +1303,7 @@ test_strndup (void) equal(p, "abc", 6); free (p); } +#endif static void test_bcmp (void) @@ -1382,8 +1387,10 @@ main (void) /* A closely related function is stpcpy. */ test_stpcpy (); +#if !defined(__APPLE__) /* stpncpy. */ test_stpncpy (); +#endif /* strcat. */ test_strcat (); @@ -1403,12 +1410,12 @@ main (void) /* strchr. */ test_strchr (); -# if !defined(_AIX) +# if !defined(_AIX) && !defined(__APPLE__) /* strchrnul. */ test_strchrnul (); # endif -# if !defined(_AIX) +# if !defined(_AIX) && !defined(__APPLE__) /* rawmemchr. */ test_rawmemchr (); # endif @@ -1419,7 +1426,7 @@ main (void) /* strrchr. */ test_strrchr (); -# if !defined(_AIX) +# if !defined(_AIX) && !defined(__APPLE__) /* memrchr. */ test_memrchr (); # endif @@ -1460,7 +1467,7 @@ main (void) /* memmove - must work on overlap. */ test_memmove (); -# if !defined(_AIX) +# if !defined(_AIX) && !defined(__APPLE__) /* mempcpy */ test_mempcpy (); # endif @@ -1480,8 +1487,10 @@ main (void) /* bcmp - somewhat like memcmp. */ test_bcmp (); +#if !defined(__APPLE__) /* strndup. */ test_strndup (); +#endif /* strerror - VERY system-dependent. */ test_strerror (); diff --git a/memcheck/tests/strchr.stderr.exp-darwin b/memcheck/tests/strchr.stderr.exp-darwin new file mode 100644 index 000000000..8b870df38 --- /dev/null +++ b/memcheck/tests/strchr.stderr.exp-darwin @@ -0,0 +1,11 @@ +Conditional jump or move depends on uninitialised value(s) + at 0x........: strchr (mc_replace_strmem.c:...) + by 0x........: main (strchr.c:15) + +Conditional jump or move depends on uninitialised value(s) + at 0x........: strchr (mc_replace_strmem.c:...) + by 0x........: main (strchr.c:15) + +Conditional jump or move depends on uninitialised value(s) + at 0x........: strrchr (mc_replace_strmem.c:...) + by 0x........: main (strchr.c:16) diff --git a/memcheck/tests/x86/Makefile.am b/memcheck/tests/x86/Makefile.am index 28dd116fb..2cd7b03bc 100644 --- a/memcheck/tests/x86/Makefile.am +++ b/memcheck/tests/x86/Makefile.am @@ -42,9 +42,15 @@ AM_CFLAGS += @FLAG_M32@ $(FLAG_MMMX) $(FLAG_MSSE) AM_CXXFLAGS += @FLAG_M32@ $(FLAG_MMMX) $(FLAG_MSSE) AM_CCASFLAGS += @FLAG_M32@ -# must be built with these flags -- bug only occurred with them + +# fpeflags must use these flags -- bug only occurred with them. fpeflags_CFLAGS = $(AM_CFLAGS) -march=i686 pushfpopf_SOURCES = pushfpopf_c.c pushfpopf_s.S +if VGCONF_OS_IS_DARWIN +pushpopmem_CFLAGS = $(AM_CFLAGS) -mdynamic-no-pic +else +pushpopmem_CFLAGS = $(AM_CFLAGS) +endif tronical_SOURCES = tronical.S more_x86_fp_LDADD = -lm diff --git a/none/Makefile.am b/none/Makefile.am index ac47371f6..34061af3f 100644 --- a/none/Makefile.am +++ b/none/Makefile.am @@ -19,6 +19,12 @@ endif if VGCONF_PLATFORMS_INCLUDE_PPC64_AIX5 noinst_PROGRAMS += none-ppc64-aix5 endif +if VGCONF_PLATFORMS_INCLUDE_X86_DARWIN +noinst_PROGRAMS += none-x86-darwin +endif +if VGCONF_PLATFORMS_INCLUDE_AMD64_DARWIN +noinst_PROGRAMS += none-amd64-darwin +endif NONE_SOURCES_COMMON = nl_main.c @@ -63,3 +69,17 @@ none_ppc64_aix5_CFLAGS = $(AM_CFLAGS_PPC64_AIX5) none_ppc64_aix5_DEPENDENCIES = $(COREGRIND_LIBS_PPC64_AIX5) none_ppc64_aix5_LDADD = $(TOOL_LDADD_PPC64_AIX5) none_ppc64_aix5_LDFLAGS = $(TOOL_LDFLAGS_PPC64_AIX5) + +none_x86_darwin_SOURCES = $(NONE_SOURCES_COMMON) +none_x86_darwin_CPPFLAGS = $(AM_CPPFLAGS_X86_DARWIN) +none_x86_darwin_CFLAGS = $(AM_CFLAGS_X86_DARWIN) +none_x86_darwin_DEPENDENCIES = $(COREGRIND_LIBS_X86_DARWIN) +none_x86_darwin_LDADD = $(TOOL_LDADD_X86_DARWIN) +none_x86_darwin_LDFLAGS = $(TOOL_LDFLAGS_X86_DARWIN) + +none_amd64_darwin_SOURCES = $(NONE_SOURCES_COMMON) +none_amd64_darwin_CPPFLAGS = $(AM_CPPFLAGS_AMD64_DARWIN) +none_amd64_darwin_CFLAGS = $(AM_CFLAGS_AMD64_DARWIN) +none_amd64_darwin_DEPENDENCIES = $(COREGRIND_LIBS_AMD64_DARWIN) +none_amd64_darwin_LDADD = $(TOOL_LDADD_AMD64_DARWIN) +none_amd64_darwin_LDFLAGS = $(TOOL_LDFLAGS_AMD64_DARWIN) diff --git a/none/tests/Makefile.am b/none/tests/Makefile.am index dfec0678c..1cc18afc0 100644 --- a/none/tests/Makefile.am +++ b/none/tests/Makefile.am @@ -21,13 +21,16 @@ endif if VGCONF_OS_IS_LINUX SUBDIRS += linux endif +if VGCONF_OS_IS_DARWIN +SUBDIRS += darwin +endif # Platform-specific tests if VGCONF_PLATFORMS_INCLUDE_X86_LINUX SUBDIRS += x86-linux endif -DIST_SUBDIRS = x86 amd64 ppc32 ppc64 linux x86-linux . +DIST_SUBDIRS = x86 amd64 ppc32 ppc64 linux darwin x86-linux . noinst_SCRIPTS = \ filter_cmdline0 filter_linenos \ @@ -140,7 +143,7 @@ check_PROGRAMS = \ fdleak_cmsg fdleak_creat fdleak_dup fdleak_dup2 \ fdleak_fcntl fdleak_ipv4 fdleak_open fdleak_pipe \ fdleak_socketpair \ - floored fork fucomip manythreads \ + floored fork fucomip \ munmap_exe map_unaligned map_unmap mq \ nestedfns \ pending \ @@ -151,10 +154,27 @@ check_PROGRAMS = \ rlimit_nofile selfrun sem semlimit sha1_test \ shortpush shorts stackgrowth sigstackgrowth \ syscall-restart1 syscall-restart2 system \ - thread-exits threaded-fork threadederrno \ + threaded-fork threadederrno \ tls tls.so tls2.so vgprintf \ coolo_sigaction gxx304 +# DDD: +# - manythreads and thread-exits have lots of this: +# --61831:0:aspacem sync_check_mapping_callback: segment mismatch: +# kernel's seg: +# --61831:0:aspacem start=0x102538000 end=0x1025b7fff prot=3 dev=0 ino=0 +# offset=0 name="(none)" +# --61831:0:aspacem sync_check_mapping_callback: segment mismatch: V's seg: +# --61831:0:aspacem NSegment{ , start=0x10067a000, end=0x109a1efff, +# smode=SmFixed, dev=0, ino=0, offset=0, fnIdx=-1, hasR=0, +# hasW=0, hasX=0, hasT=0, mark=0, name="(none)"} +if ! VGCONF_PLATFORMS_INCLUDE_AMD64_DARWIN + check_PROGRAMS += \ + manythreads \ + thread-exits +endif + + AM_CFLAGS += $(AM_FLAG_M3264_PRI) AM_CXXFLAGS += $(AM_FLAG_M3264_PRI) @@ -162,7 +182,11 @@ AM_CXXFLAGS += $(AM_FLAG_M3264_PRI) ansi_CFLAGS = $(AM_CFLAGS) -ansi floored_LDADD = -lm manythreads_LDADD = -lpthread -mq_LDADD = -lrt +if VGCONF_OS_IS_DARWIN + nestedfns_CFLAGS = $(AM_CFLAGS) -fnested-functions +else + mq_LDADD = -lrt +endif pth_atfork1_LDADD = -lpthread pth_blockedsig_LDADD = -lpthread pth_cancel1_CFLAGS = $(AM_CFLAGS) -Wno-shadow @@ -180,6 +204,7 @@ if VGCONF_OS_IS_AIX5 res_search_LDADD = -lpthread else res_search_LDADD = -lresolv -lpthread + resolv_LDADD = -lresolv -lpthread endif semlimit_LDADD = -lpthread thread_exits_LDADD = -lpthread @@ -198,13 +223,22 @@ tls_so_DEPENDENCIES = tls2.so if VGCONF_OS_IS_AIX5 tls_so_LDFLAGS = -shared -fPIC tls_so_LDADD = `pwd`/tls2.so +else +if VGCONF_OS_IS_DARWIN + tls_so_LDFLAGS = -dynamic -dynamiclib -all_load -fpic + tls_so_LDADD = `pwd`/tls2.so else tls_so_LDFLAGS = -Wl,-rpath,$(top_builddir)/none/tests -shared -fPIC tls_so_LDADD = tls2.so endif +endif tls_so_CFLAGS = $(AM_CFLAGS) -fPIC tls2_so_SOURCES = tls2_so.c +if VGCONF_OS_IS_DARWIN +tls2_so_LDFLAGS = -dynamic -dynamiclib -all_load +else tls2_so_LDFLAGS = -shared +endif # C++ tests coolo_sigaction_SOURCES = coolo_sigaction.cpp diff --git a/none/tests/amd64/Makefile.am b/none/tests/amd64/Makefile.am index 13621d08b..798bf86e0 100644 --- a/none/tests/amd64/Makefile.am +++ b/none/tests/amd64/Makefile.am @@ -50,19 +50,31 @@ EXTRA_DIST = $(noinst_SCRIPTS) \ slahf-amd64.vgtest check_PROGRAMS = \ - bug127521-64 bug132813-amd64 bug137714-amd64 bug132918 \ - bug156404-amd64 \ + bug127521-64 bug132813-amd64 bug132918 \ clc \ - faultstatus fcmovnu fxtract $(INSN_TESTS) looper jrcxz \ + $(INSN_TESTS) \ rcl-amd64 \ redundantRexW \ - smc1 shrld \ - nibz_bennee_mmap \ - slahf-amd64 + smc1 \ + nibz_bennee_mmap if BUILD_SSSE3_TESTS check_PROGRAMS += ssse3_misaligned endif +# DDD: these need to be made to work on Darwin like the x86/ ones were. +if ! VGCONF_OS_IS_DARWIN + check_PROGRAMS += \ + bug137714-amd64 \ + bug156404-amd64 \ + faultstatus \ + fcmovnu \ + fxtract \ + looper \ + jrcxz \ + shrld \ + slahf-amd64 +endif + AM_CFLAGS += @FLAG_M64@ AM_CXXFLAGS += @FLAG_M64@ AM_CCASFLAGS += @FLAG_M64@ diff --git a/none/tests/darwin/Makefile.am b/none/tests/darwin/Makefile.am new file mode 100644 index 000000000..b4c90bfaf --- /dev/null +++ b/none/tests/darwin/Makefile.am @@ -0,0 +1,17 @@ + +include $(top_srcdir)/Makefile.tool-tests.am + +noinst_SCRIPTS = filter_stderr + +EXTRA_DIST = $(noinst_SCRIPTS) \ + apple-main-arg.stderr.exp apple-main-arg.vgtest \ + rlimit.stderr.exp rlimit.vgtest + +check_PROGRAMS = \ + apple-main-arg \ + rlimit + + +AM_CFLAGS += $(AM_FLAG_M3264_PRI) +AM_CXXFLAGS += $(AM_FLAG_M3264_PRI) + diff --git a/none/tests/darwin/apple-main-arg.c b/none/tests/darwin/apple-main-arg.c new file mode 100644 index 000000000..945acc1a7 --- /dev/null +++ b/none/tests/darwin/apple-main-arg.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include +#include + +// On Darwin there's this secret fourth argument, 'apple', which is a pointer +// to a string that contains the executable path, like argv[0], but unlike +// argv[0] it can't be changed using exec(). + +int main(int argc, char *argv[], char *envp[], char *apple[]) +{ + char *pargv = calloc((PATH_MAX+1), sizeof(char)), + *pappl = calloc((PATH_MAX+1), sizeof(char)); + int i; + + for (i = 0; envp[i]; i++) + ; + + // envp[i]==NULL; envp[i+1]==apple[0]==executable_path + assert(envp[i+1] == apple[0]); + + // Make sure realpath(argv[0]) == realpath(apple[0]). (realpath resolves + // symlinks.) + realpath(argv[0], pargv); + realpath(apple[0], pappl); + assert(0 == strcmp(pargv, pappl)); + + return 0; +} + diff --git a/none/tests/darwin/apple-main-arg.stderr.exp b/none/tests/darwin/apple-main-arg.stderr.exp new file mode 100644 index 000000000..e69de29bb diff --git a/none/tests/darwin/apple-main-arg.vgtest b/none/tests/darwin/apple-main-arg.vgtest new file mode 100644 index 000000000..df4e15a41 --- /dev/null +++ b/none/tests/darwin/apple-main-arg.vgtest @@ -0,0 +1,2 @@ +prog: apple-main-arg +vgopts: -q diff --git a/none/tests/darwin/filter_stderr b/none/tests/darwin/filter_stderr new file mode 100644 index 000000000..0ae9313a9 --- /dev/null +++ b/none/tests/darwin/filter_stderr @@ -0,0 +1,3 @@ +#! /bin/sh + +../filter_stderr diff --git a/none/tests/darwin/rlimit.c b/none/tests/darwin/rlimit.c new file mode 100644 index 000000000..ac5ac8248 --- /dev/null +++ b/none/tests/darwin/rlimit.c @@ -0,0 +1,17 @@ +// Small test program to demonstrate Valgrind bug. +// https://bugs.kde.org/show_bug.cgi?id=191761 +// Author: rohitrao@google.com +// +// Before the fix, it was printing 266. Now it prints 256. + +#include +#include + +int main(void) +{ + struct rlimit rlp; + getrlimit(RLIMIT_NOFILE, &rlp); + fprintf(stderr, "RLIMIT_NOFILE is %lld\n", (long long)rlp.rlim_cur); + return 0; +} + diff --git a/none/tests/darwin/rlimit.stderr.exp b/none/tests/darwin/rlimit.stderr.exp new file mode 100644 index 000000000..56d4e4c7d --- /dev/null +++ b/none/tests/darwin/rlimit.stderr.exp @@ -0,0 +1 @@ +RLIMIT_NOFILE is 256 diff --git a/none/tests/darwin/rlimit.vgtest b/none/tests/darwin/rlimit.vgtest new file mode 100644 index 000000000..2706845b7 --- /dev/null +++ b/none/tests/darwin/rlimit.vgtest @@ -0,0 +1,2 @@ +prog: rlimit +vgopts: -q diff --git a/none/tests/pth_atfork1.c b/none/tests/pth_atfork1.c index 79ce17713..df5ef9811 100644 --- a/none/tests/pth_atfork1.c +++ b/none/tests/pth_atfork1.c @@ -18,7 +18,7 @@ Boston, MA 02111-1307, USA. */ #include -#if !defined(_AIX) +#if !defined(_AIX) && !defined(__APPLE__) # include #endif #include @@ -27,7 +27,7 @@ #include #include -#if defined(_AIX) +#if defined(_AIX) || defined(__APPLE__) #include /* strerror */ static void error (int status, int errnum, char* msg) { diff --git a/none/tests/x86/Makefile.am b/none/tests/x86/Makefile.am index cc0372b02..f405c51ee 100644 --- a/none/tests/x86/Makefile.am +++ b/none/tests/x86/Makefile.am @@ -92,6 +92,14 @@ AM_CFLAGS += @FLAG_M32@ $(FLAG_MMMX) $(FLAG_MSSE) AM_CXXFLAGS += @FLAG_M32@ $(FLAG_MMMX) $(FLAG_MSSE) AM_CCASFLAGS += @FLAG_M32@ +if VGCONF_OS_IS_DARWIN +# Some of the tests (bug125959_x86, bug152818_x86, insn_*) need +# -mdynamic-no-pic. I tried setting *_CFLAGS separately for all of them, +# but it caused problems with the generation of insn_*.c. So just use this +# crude approach of setting -mdynamic-no-pic for all tests in this +# directory. +AM_CFLAGS += -mdynamic-no-pic +endif cpuid_SOURCES = cpuid_c.c cpuid_s.S # fpu_lazy_eflags must use these flags -- the bug only occurred with them. diff --git a/tests/Makefile.am b/tests/Makefile.am index 0350f0db1..2cf5a82f9 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -27,3 +27,7 @@ check_PROGRAMS = \ AM_CFLAGS += $(AM_FLAG_M3264_PRI) AM_CXXFLAGS += $(AM_FLAG_M3264_PRI) +if VGCONF_OS_IS_DARWIN +x86_amd64_features_CFLAGS = $(AM_CFLAGS) -mdynamic-no-pic +endif + diff --git a/tests/arch_test.c b/tests/arch_test.c index f6206e3ef..6771f4a6a 100644 --- a/tests/arch_test.c +++ b/tests/arch_test.c @@ -32,10 +32,10 @@ char* all_archs[] = { static Bool go(char* arch) { -#if defined(VGP_x86_linux) +#if defined(VGP_x86_linux) || defined(VGP_x86_darwin) if ( 0 == strcmp( arch, "x86" ) ) return True; -#elif defined(VGP_amd64_linux) +#elif defined(VGP_amd64_linux) || defined(VGP_amd64_darwin) if ( 0 == strcmp( arch, "x86" ) ) return True; if ( 0 == strcmp( arch, "amd64" ) ) return True; diff --git a/tests/asm.h b/tests/asm.h index 99f9cc5a5..e2c2524ed 100644 --- a/tests/asm.h +++ b/tests/asm.h @@ -5,7 +5,15 @@ // general, any symbol named in asm code should be wrapped by VG_SYM. // This one is for use in inline asm in C files. +#if defined(VGO_darwin) +#define VG_SYM(x) "_"#x +#else #define VG_SYM(x) #x +#endif // This one is for use in asm files. +#if defined(VGO_darwin) +#define VG_SYM_ASM(x) _##x +#else #define VG_SYM_ASM(x) x +#endif diff --git a/tests/filter_libc b/tests/filter_libc index a89fb8912..48c443ffc 100755 --- a/tests/filter_libc +++ b/tests/filter_libc @@ -15,8 +15,9 @@ while (<>) s/ __GI___/ __/; s/ __([a-z]*)_nocancel / $1 /; - s/\(in \/.*libc.*\)$/(in \/...libc...)/; - s/\(within \/.*libc.*\)$/(within \/...libc...)/; + # "libSystem*" occurs on Darwin. + s/\(in \/.*(libc|libSystem).*\)$/(in \/...libc...)/; + s/\(within \/.*(libc|libSystem).*\)$/(within \/...libc...)/; # Remove the filename -- on some platforms (eg. Linux) it will be in # libc, on some (eg. Darwin) it will be in the main executable. diff --git a/tests/malloc.h b/tests/malloc.h index 454d2cff6..0179b387c 100644 --- a/tests/malloc.h +++ b/tests/malloc.h @@ -1,7 +1,11 @@ // Replacement for malloc.h which factors out platform differences. #include -#include +#if defined(VGO_darwin) +# include +#else +# include +#endif #include @@ -10,7 +14,12 @@ __attribute__((unused)) static void* memalign16(size_t szB) { void* x; +#if defined(VGO_darwin) + // Darwin lacks memalign, but its malloc is always 16-aligned anyway. + x = malloc(szB); +#else x = memalign(16, szB); +#endif assert(x); assert(0 == ((16-1) & (unsigned long)x)); return x; diff --git a/tests/os_test.c b/tests/os_test.c index 065dbe536..a65115c64 100644 --- a/tests/os_test.c +++ b/tests/os_test.c @@ -22,6 +22,7 @@ typedef int Bool; char* all_OSes[] = { "linux", "aix5", + "darwin", NULL }; @@ -31,7 +32,10 @@ static Bool go(char* OS) if ( 0 == strcmp( OS, "linux" ) ) return True; #elif defined(VGO_aix5) - if ( 0 == strcmp( OS, "aix5" ) ) return True; + if ( 0 == strcmp( OS, "aix5" ) ) return True; + +#elif defined(VGO_darwin) + if ( 0 == strcmp( OS, "darwin" ) ) return True; #else # error Unknown OS diff --git a/tests/platform_test b/tests/platform_test index e91b9f7ab..4d5790113 100644 --- a/tests/platform_test +++ b/tests/platform_test @@ -13,6 +13,7 @@ all_platforms= all_platforms="$all_platforms x86-linux amd64-linux ppc32-linux ppc64-linux" all_platforms="$all_platforms ppc32-aix5 ppc64-aix5" +all_platforms="$all_platforms x86-darwin amd64-darwin" if [ $# -ne 2 ] ; then echo "usage: platform_test " diff --git a/tests/sys_mman.h b/tests/sys_mman.h index 862bccc13..7ac64d54c 100644 --- a/tests/sys_mman.h +++ b/tests/sys_mman.h @@ -2,6 +2,10 @@ #include +#if defined(VGO_darwin) +# define MAP_ANONYMOUS MAP_ANON +#endif + #include #include diff --git a/tests/x86_amd64_features.c b/tests/x86_amd64_features.c index 9d17805a8..06ebe5d1e 100644 --- a/tests/x86_amd64_features.c +++ b/tests/x86_amd64_features.c @@ -83,7 +83,7 @@ static Bool go(char* cpu) return 1; // Feature not present. } -#else // defined(VGA_x86) || defined(VGA_amd64) +#else static Bool go(char* cpu) {