mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-02-05 04:29:32 +08:00
[libsanitizer] add mach_override and enable libsanitizer on darwin
From-SVN: r193781
This commit is contained in:
parent
9f1b2dcbad
commit
f246eadc0a
@ -1,3 +1,7 @@
|
|||||||
|
2012-11-24 Jack Howarth <howarth@bromo.med.uc.edu>
|
||||||
|
|
||||||
|
* config/darwin.h (LINK_COMMAND_SPEC_A): Deal with -fsanitize=address.
|
||||||
|
|
||||||
2012-11-24 Matthias Klose <doko@ubuntu.com>
|
2012-11-24 Matthias Klose <doko@ubuntu.com>
|
||||||
|
|
||||||
* configure.ac (multiarch): Use $enableval instead of $withval.
|
* configure.ac (multiarch): Use $enableval instead of $withval.
|
||||||
|
@ -180,6 +180,9 @@ extern GTY(()) int darwin_ms_struct;
|
|||||||
%{L*} %(link_libgcc) %o %{fprofile-arcs|fprofile-generate*|coverage:-lgcov} \
|
%{L*} %(link_libgcc) %o %{fprofile-arcs|fprofile-generate*|coverage:-lgcov} \
|
||||||
%{fopenmp|ftree-parallelize-loops=*: \
|
%{fopenmp|ftree-parallelize-loops=*: \
|
||||||
%{static|static-libgcc|static-libstdc++|static-libgfortran: libgomp.a%s; : -lgomp } } \
|
%{static|static-libgcc|static-libstdc++|static-libgfortran: libgomp.a%s; : -lgomp } } \
|
||||||
|
%{fsanitize=address: \
|
||||||
|
%{static|static-libasan|static-libgcc|static-libgfortran: -framework CoreFoundation -lstdc++ libasan.a%s; \
|
||||||
|
static-libstdc++: -framework CoreFoundation libstdc++.a%s libasan.a%s; : -framework CoreFoundation -lasan } } \
|
||||||
%{fgnu-tm: \
|
%{fgnu-tm: \
|
||||||
%{static|static-libgcc|static-libstdc++|static-libgfortran: libitm.a%s; : -litm } } \
|
%{static|static-libgcc|static-libstdc++|static-libgfortran: libitm.a%s; : -litm } } \
|
||||||
%{!nostdlib:%{!nodefaultlibs:\
|
%{!nostdlib:%{!nodefaultlibs:\
|
||||||
|
@ -1,3 +1,16 @@
|
|||||||
|
2012-11-24 Kostya Serebryany kcc@google.com
|
||||||
|
Jack Howarth <howarth@bromo.med.uc.edu>
|
||||||
|
|
||||||
|
* interception/mach_override/mach_override.c: Migrate from llvm.
|
||||||
|
* interception/mach_override/mach_override.h: Likewise.
|
||||||
|
* interception/mach_override/LICENSE.txt: Likewise.
|
||||||
|
* configure.tgt: Add darwin to supported targets.
|
||||||
|
* configure.ac: Define USING_MACH_OVERRIDE when on darwin.
|
||||||
|
* interception/Makefile.am: Compile mach_override.c when
|
||||||
|
USING_MACH_OVERRIDE defined.
|
||||||
|
* configure: Regenerated.
|
||||||
|
* interception/Makefile.in: Likewise.
|
||||||
|
|
||||||
2012-11-23 H.J. Lu <hongjiu.lu@intel.com>
|
2012-11-23 H.J. Lu <hongjiu.lu@intel.com>
|
||||||
|
|
||||||
PR sanitizer/55450
|
PR sanitizer/55450
|
||||||
|
23
libsanitizer/configure
vendored
23
libsanitizer/configure
vendored
@ -604,6 +604,8 @@ ac_subst_vars='am__EXEEXT_FALSE
|
|||||||
am__EXEEXT_TRUE
|
am__EXEEXT_TRUE
|
||||||
LTLIBOBJS
|
LTLIBOBJS
|
||||||
LIBOBJS
|
LIBOBJS
|
||||||
|
USING_MACH_OVERRIDE_FALSE
|
||||||
|
USING_MACH_OVERRIDE_TRUE
|
||||||
TSAN_SUPPORTED_FALSE
|
TSAN_SUPPORTED_FALSE
|
||||||
TSAN_SUPPORTED_TRUE
|
TSAN_SUPPORTED_TRUE
|
||||||
enable_static
|
enable_static
|
||||||
@ -11078,7 +11080,7 @@ else
|
|||||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||||
lt_status=$lt_dlunknown
|
lt_status=$lt_dlunknown
|
||||||
cat > conftest.$ac_ext <<_LT_EOF
|
cat > conftest.$ac_ext <<_LT_EOF
|
||||||
#line 11081 "configure"
|
#line 11083 "configure"
|
||||||
#include "confdefs.h"
|
#include "confdefs.h"
|
||||||
|
|
||||||
#if HAVE_DLFCN_H
|
#if HAVE_DLFCN_H
|
||||||
@ -11184,7 +11186,7 @@ else
|
|||||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||||
lt_status=$lt_dlunknown
|
lt_status=$lt_dlunknown
|
||||||
cat > conftest.$ac_ext <<_LT_EOF
|
cat > conftest.$ac_ext <<_LT_EOF
|
||||||
#line 11187 "configure"
|
#line 11189 "configure"
|
||||||
#include "confdefs.h"
|
#include "confdefs.h"
|
||||||
|
|
||||||
#if HAVE_DLFCN_H
|
#if HAVE_DLFCN_H
|
||||||
@ -14494,6 +14496,19 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
case "$host" in
|
||||||
|
*-*-darwin*) MACH_OVERRIDE=true ;;
|
||||||
|
*) MACH_OVERRIDE=false ;;
|
||||||
|
esac
|
||||||
|
if $MACH_OVERRIDE; then
|
||||||
|
USING_MACH_OVERRIDE_TRUE=
|
||||||
|
USING_MACH_OVERRIDE_FALSE='#'
|
||||||
|
else
|
||||||
|
USING_MACH_OVERRIDE_TRUE='#'
|
||||||
|
USING_MACH_OVERRIDE_FALSE=
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
ac_config_files="$ac_config_files Makefile"
|
ac_config_files="$ac_config_files Makefile"
|
||||||
|
|
||||||
|
|
||||||
@ -14670,6 +14685,10 @@ if test -z "${TSAN_SUPPORTED_TRUE}" && test -z "${TSAN_SUPPORTED_FALSE}"; then
|
|||||||
as_fn_error "conditional \"TSAN_SUPPORTED\" was never defined.
|
as_fn_error "conditional \"TSAN_SUPPORTED\" was never defined.
|
||||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||||
fi
|
fi
|
||||||
|
if test -z "${USING_MACH_OVERRIDE_TRUE}" && test -z "${USING_MACH_OVERRIDE_FALSE}"; then
|
||||||
|
as_fn_error "conditional \"USING_MACH_OVERRIDE\" was never defined.
|
||||||
|
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||||
|
fi
|
||||||
|
|
||||||
: ${CONFIG_STATUS=./config.status}
|
: ${CONFIG_STATUS=./config.status}
|
||||||
ac_write_fail=0
|
ac_write_fail=0
|
||||||
|
@ -80,6 +80,12 @@ unset TSAN_SUPPORTED
|
|||||||
. ${srcdir}/configure.tgt
|
. ${srcdir}/configure.tgt
|
||||||
AM_CONDITIONAL(TSAN_SUPPORTED, [test "x$TSAN_SUPPORTED" = "xyes"])
|
AM_CONDITIONAL(TSAN_SUPPORTED, [test "x$TSAN_SUPPORTED" = "xyes"])
|
||||||
|
|
||||||
|
case "$host" in
|
||||||
|
*-*-darwin*) MACH_OVERRIDE=true ;;
|
||||||
|
*) MACH_OVERRIDE=false ;;
|
||||||
|
esac
|
||||||
|
AM_CONDITIONAL(USING_MACH_OVERRIDE, $MACH_OVERRIDE)
|
||||||
|
|
||||||
AC_CONFIG_FILES([Makefile])
|
AC_CONFIG_FILES([Makefile])
|
||||||
|
|
||||||
AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common asan], [DIR/Makefile ]),
|
AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common asan], [DIR/Makefile ]),
|
||||||
|
@ -27,6 +27,9 @@ case "${target}" in
|
|||||||
;;
|
;;
|
||||||
sparc*-*-linux*)
|
sparc*-*-linux*)
|
||||||
;;
|
;;
|
||||||
|
x86_64-*-darwin* | i?86-*-darwin*)
|
||||||
|
TSAN_SUPPORTED=no
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
UNSUPPORTED=1
|
UNSUPPORTED=1
|
||||||
;;
|
;;
|
||||||
|
@ -14,7 +14,11 @@ interception_files = \
|
|||||||
interception_mac.cc \
|
interception_mac.cc \
|
||||||
interception_win.cc
|
interception_win.cc
|
||||||
|
|
||||||
libinterception_la_SOURCES = $(interception_files)
|
if USING_MACH_OVERRIDE
|
||||||
|
libinterception_la_SOURCES = $(interception_files) mach_override/mach_override.c
|
||||||
|
else
|
||||||
|
libinterception_la_SOURCES = $(interception_files)
|
||||||
|
endif
|
||||||
|
|
||||||
# Work around what appears to be a GNU make bug handling MAKEFLAGS
|
# Work around what appears to be a GNU make bug handling MAKEFLAGS
|
||||||
# values defined in terms of make variables, as is the case for CC and
|
# values defined in terms of make variables, as is the case for CC and
|
||||||
|
@ -53,14 +53,29 @@ CONFIG_CLEAN_FILES =
|
|||||||
CONFIG_CLEAN_VPATH_FILES =
|
CONFIG_CLEAN_VPATH_FILES =
|
||||||
LTLIBRARIES = $(noinst_LTLIBRARIES)
|
LTLIBRARIES = $(noinst_LTLIBRARIES)
|
||||||
libinterception_la_LIBADD =
|
libinterception_la_LIBADD =
|
||||||
|
am__libinterception_la_SOURCES_DIST = interception_linux.cc \
|
||||||
|
interception_mac.cc interception_win.cc \
|
||||||
|
mach_override/mach_override.c
|
||||||
am__objects_1 = interception_linux.lo interception_mac.lo \
|
am__objects_1 = interception_linux.lo interception_mac.lo \
|
||||||
interception_win.lo
|
interception_win.lo
|
||||||
am_libinterception_la_OBJECTS = $(am__objects_1)
|
@USING_MACH_OVERRIDE_FALSE@am_libinterception_la_OBJECTS = \
|
||||||
|
@USING_MACH_OVERRIDE_FALSE@ $(am__objects_1)
|
||||||
|
@USING_MACH_OVERRIDE_TRUE@am_libinterception_la_OBJECTS = \
|
||||||
|
@USING_MACH_OVERRIDE_TRUE@ $(am__objects_1) mach_override.lo
|
||||||
libinterception_la_OBJECTS = $(am_libinterception_la_OBJECTS)
|
libinterception_la_OBJECTS = $(am_libinterception_la_OBJECTS)
|
||||||
DEFAULT_INCLUDES = -I.@am__isrc@
|
DEFAULT_INCLUDES = -I.@am__isrc@
|
||||||
depcomp = $(SHELL) $(top_srcdir)/../depcomp
|
depcomp = $(SHELL) $(top_srcdir)/../depcomp
|
||||||
am__depfiles_maybe = depfiles
|
am__depfiles_maybe = depfiles
|
||||||
am__mv = mv -f
|
am__mv = mv -f
|
||||||
|
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
|
||||||
|
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
|
||||||
|
LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
|
||||||
|
--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
|
||||||
|
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
|
||||||
|
CCLD = $(CC)
|
||||||
|
LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
|
||||||
|
--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
|
||||||
|
$(LDFLAGS) -o $@
|
||||||
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
|
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
|
||||||
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
|
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
|
||||||
LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
|
LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
|
||||||
@ -71,7 +86,7 @@ CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
|
|||||||
--mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
|
--mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
|
||||||
$(LDFLAGS) -o $@
|
$(LDFLAGS) -o $@
|
||||||
SOURCES = $(libinterception_la_SOURCES)
|
SOURCES = $(libinterception_la_SOURCES)
|
||||||
DIST_SOURCES = $(libinterception_la_SOURCES)
|
DIST_SOURCES = $(am__libinterception_la_SOURCES_DIST)
|
||||||
ETAGS = etags
|
ETAGS = etags
|
||||||
CTAGS = ctags
|
CTAGS = ctags
|
||||||
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
|
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
|
||||||
@ -215,7 +230,8 @@ interception_files = \
|
|||||||
interception_mac.cc \
|
interception_mac.cc \
|
||||||
interception_win.cc
|
interception_win.cc
|
||||||
|
|
||||||
libinterception_la_SOURCES = $(interception_files)
|
@USING_MACH_OVERRIDE_FALSE@libinterception_la_SOURCES = $(interception_files)
|
||||||
|
@USING_MACH_OVERRIDE_TRUE@libinterception_la_SOURCES = $(interception_files) mach_override/mach_override.c
|
||||||
|
|
||||||
# Work around what appears to be a GNU make bug handling MAKEFLAGS
|
# Work around what appears to be a GNU make bug handling MAKEFLAGS
|
||||||
# values defined in terms of make variables, as is the case for CC and
|
# values defined in terms of make variables, as is the case for CC and
|
||||||
@ -261,7 +277,7 @@ MAKEOVERRIDES =
|
|||||||
all: all-am
|
all: all-am
|
||||||
|
|
||||||
.SUFFIXES:
|
.SUFFIXES:
|
||||||
.SUFFIXES: .cc .lo .o .obj
|
.SUFFIXES: .c .cc .lo .o .obj
|
||||||
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
|
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
|
||||||
@for dep in $?; do \
|
@for dep in $?; do \
|
||||||
case '$(am__configure_deps)' in \
|
case '$(am__configure_deps)' in \
|
||||||
@ -313,6 +329,35 @@ distclean-compile:
|
|||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_linux.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_linux.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_mac.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_mac.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_win.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_win.Plo@am__quote@
|
||||||
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mach_override.Plo@am__quote@
|
||||||
|
|
||||||
|
.c.o:
|
||||||
|
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
|
||||||
|
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
|
||||||
|
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||||
|
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||||
|
@am__fastdepCC_FALSE@ $(COMPILE) -c $<
|
||||||
|
|
||||||
|
.c.obj:
|
||||||
|
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
|
||||||
|
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
|
||||||
|
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||||
|
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||||
|
@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
|
||||||
|
|
||||||
|
.c.lo:
|
||||||
|
@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
|
||||||
|
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
|
||||||
|
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
|
||||||
|
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||||
|
@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
|
||||||
|
|
||||||
|
mach_override.lo: mach_override/mach_override.c
|
||||||
|
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mach_override.lo -MD -MP -MF $(DEPDIR)/mach_override.Tpo -c -o mach_override.lo `test -f 'mach_override/mach_override.c' || echo '$(srcdir)/'`mach_override/mach_override.c
|
||||||
|
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/mach_override.Tpo $(DEPDIR)/mach_override.Plo
|
||||||
|
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mach_override/mach_override.c' object='mach_override.lo' libtool=yes @AMDEPBACKSLASH@
|
||||||
|
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||||
|
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mach_override.lo `test -f 'mach_override/mach_override.c' || echo '$(srcdir)/'`mach_override/mach_override.c
|
||||||
|
|
||||||
.cc.o:
|
.cc.o:
|
||||||
@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
|
@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
|
||||||
|
3
libsanitizer/interception/mach_override/LICENSE.txt
Normal file
3
libsanitizer/interception/mach_override/LICENSE.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
|
||||||
|
Some rights reserved: <http://opensource.org/licenses/mit-license.php>
|
||||||
|
|
970
libsanitizer/interception/mach_override/mach_override.c
Normal file
970
libsanitizer/interception/mach_override/mach_override.c
Normal file
@ -0,0 +1,970 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
mach_override.c
|
||||||
|
Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
|
||||||
|
Some rights reserved: <http://opensource.org/licenses/mit-license.php>
|
||||||
|
|
||||||
|
***************************************************************************/
|
||||||
|
#ifdef __APPLE__
|
||||||
|
|
||||||
|
#include "mach_override.h"
|
||||||
|
|
||||||
|
#include <mach-o/dyld.h>
|
||||||
|
#include <mach/mach_host.h>
|
||||||
|
#include <mach/mach_init.h>
|
||||||
|
#include <mach/vm_map.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
#include <CoreServices/CoreServices.h>
|
||||||
|
|
||||||
|
//#define DEBUG_DISASM 1
|
||||||
|
#undef DEBUG_DISASM
|
||||||
|
|
||||||
|
/**************************
|
||||||
|
*
|
||||||
|
* Constants
|
||||||
|
*
|
||||||
|
**************************/
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark (Constants)
|
||||||
|
|
||||||
|
#if defined(__ppc__) || defined(__POWERPC__)
|
||||||
|
|
||||||
|
static
|
||||||
|
long kIslandTemplate[] = {
|
||||||
|
0x9001FFFC, // stw r0,-4(SP)
|
||||||
|
0x3C00DEAD, // lis r0,0xDEAD
|
||||||
|
0x6000BEEF, // ori r0,r0,0xBEEF
|
||||||
|
0x7C0903A6, // mtctr r0
|
||||||
|
0x8001FFFC, // lwz r0,-4(SP)
|
||||||
|
0x60000000, // nop ; optionally replaced
|
||||||
|
0x4E800420 // bctr
|
||||||
|
};
|
||||||
|
|
||||||
|
#define kAddressHi 3
|
||||||
|
#define kAddressLo 5
|
||||||
|
#define kInstructionHi 10
|
||||||
|
#define kInstructionLo 11
|
||||||
|
|
||||||
|
#elif defined(__i386__)
|
||||||
|
|
||||||
|
#define kOriginalInstructionsSize 16
|
||||||
|
|
||||||
|
static
|
||||||
|
unsigned char kIslandTemplate[] = {
|
||||||
|
// kOriginalInstructionsSize nop instructions so that we
|
||||||
|
// should have enough space to host original instructions
|
||||||
|
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
|
||||||
|
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
|
||||||
|
// Now the real jump instruction
|
||||||
|
0xE9, 0xEF, 0xBE, 0xAD, 0xDE
|
||||||
|
};
|
||||||
|
|
||||||
|
#define kInstructions 0
|
||||||
|
#define kJumpAddress kInstructions + kOriginalInstructionsSize + 1
|
||||||
|
#elif defined(__x86_64__)
|
||||||
|
|
||||||
|
#define kOriginalInstructionsSize 32
|
||||||
|
|
||||||
|
#define kJumpAddress kOriginalInstructionsSize + 6
|
||||||
|
|
||||||
|
static
|
||||||
|
unsigned char kIslandTemplate[] = {
|
||||||
|
// kOriginalInstructionsSize nop instructions so that we
|
||||||
|
// should have enough space to host original instructions
|
||||||
|
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
|
||||||
|
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
|
||||||
|
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
|
||||||
|
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
|
||||||
|
// Now the real jump instruction
|
||||||
|
0xFF, 0x25, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define kAllocateHigh 1
|
||||||
|
#define kAllocateNormal 0
|
||||||
|
|
||||||
|
/**************************
|
||||||
|
*
|
||||||
|
* Data Types
|
||||||
|
*
|
||||||
|
**************************/
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark (Data Types)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char instructions[sizeof(kIslandTemplate)];
|
||||||
|
int allocatedHigh;
|
||||||
|
} BranchIsland;
|
||||||
|
|
||||||
|
/**************************
|
||||||
|
*
|
||||||
|
* Funky Protos
|
||||||
|
*
|
||||||
|
**************************/
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark (Funky Protos)
|
||||||
|
|
||||||
|
|
||||||
|
static mach_error_t
|
||||||
|
allocateBranchIsland(
|
||||||
|
BranchIsland **island,
|
||||||
|
int allocateHigh,
|
||||||
|
void *originalFunctionAddress);
|
||||||
|
|
||||||
|
static mach_error_t
|
||||||
|
freeBranchIsland(
|
||||||
|
BranchIsland *island );
|
||||||
|
|
||||||
|
static mach_error_t
|
||||||
|
defaultIslandMalloc(
|
||||||
|
void **ptr, size_t unused_size, void *hint);
|
||||||
|
|
||||||
|
static mach_error_t
|
||||||
|
defaultIslandFree(
|
||||||
|
void *ptr);
|
||||||
|
|
||||||
|
#if defined(__ppc__) || defined(__POWERPC__)
|
||||||
|
static mach_error_t
|
||||||
|
setBranchIslandTarget(
|
||||||
|
BranchIsland *island,
|
||||||
|
const void *branchTo,
|
||||||
|
long instruction );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
|
static mach_error_t
|
||||||
|
setBranchIslandTarget_i386(
|
||||||
|
BranchIsland *island,
|
||||||
|
const void *branchTo,
|
||||||
|
char* instructions );
|
||||||
|
// Can't be made static because there's no C implementation for atomic_mov64
|
||||||
|
// on i386.
|
||||||
|
void
|
||||||
|
atomic_mov64(
|
||||||
|
uint64_t *targetAddress,
|
||||||
|
uint64_t value ) __attribute__((visibility("hidden")));
|
||||||
|
|
||||||
|
static Boolean
|
||||||
|
eatKnownInstructions(
|
||||||
|
unsigned char *code,
|
||||||
|
uint64_t *newInstruction,
|
||||||
|
int *howManyEaten,
|
||||||
|
char *originalInstructions,
|
||||||
|
int *originalInstructionCount,
|
||||||
|
uint8_t *originalInstructionSizes );
|
||||||
|
|
||||||
|
static void
|
||||||
|
fixupInstructions(
|
||||||
|
void *originalFunction,
|
||||||
|
void *escapeIsland,
|
||||||
|
void *instructionsToFix,
|
||||||
|
int instructionCount,
|
||||||
|
uint8_t *instructionSizes );
|
||||||
|
|
||||||
|
#ifdef DEBUG_DISASM
|
||||||
|
static void
|
||||||
|
dump16Bytes(
|
||||||
|
void *ptr);
|
||||||
|
#endif // DEBUG_DISASM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
*
|
||||||
|
* Interface
|
||||||
|
*
|
||||||
|
*******************************************************************************/
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark (Interface)
|
||||||
|
|
||||||
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
|
static mach_error_t makeIslandExecutable(void *address) {
|
||||||
|
mach_error_t err = err_none;
|
||||||
|
vm_size_t pageSize;
|
||||||
|
host_page_size( mach_host_self(), &pageSize );
|
||||||
|
uintptr_t page = (uintptr_t)address & ~(uintptr_t)(pageSize-1);
|
||||||
|
int e = err_none;
|
||||||
|
e |= mprotect((void *)page, pageSize, PROT_EXEC | PROT_READ | PROT_WRITE);
|
||||||
|
e |= msync((void *)page, pageSize, MS_INVALIDATE );
|
||||||
|
if (e) {
|
||||||
|
err = err_cannot_override;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static mach_error_t
|
||||||
|
defaultIslandMalloc(
|
||||||
|
void **ptr, size_t unused_size, void *hint) {
|
||||||
|
return allocateBranchIsland( (BranchIsland**)ptr, kAllocateHigh, hint );
|
||||||
|
}
|
||||||
|
static mach_error_t
|
||||||
|
defaultIslandFree(
|
||||||
|
void *ptr) {
|
||||||
|
return freeBranchIsland(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
mach_error_t
|
||||||
|
__asan_mach_override_ptr(
|
||||||
|
void *originalFunctionAddress,
|
||||||
|
const void *overrideFunctionAddress,
|
||||||
|
void **originalFunctionReentryIsland )
|
||||||
|
{
|
||||||
|
return __asan_mach_override_ptr_custom(originalFunctionAddress,
|
||||||
|
overrideFunctionAddress,
|
||||||
|
originalFunctionReentryIsland,
|
||||||
|
defaultIslandMalloc,
|
||||||
|
defaultIslandFree);
|
||||||
|
}
|
||||||
|
|
||||||
|
mach_error_t
|
||||||
|
__asan_mach_override_ptr_custom(
|
||||||
|
void *originalFunctionAddress,
|
||||||
|
const void *overrideFunctionAddress,
|
||||||
|
void **originalFunctionReentryIsland,
|
||||||
|
island_malloc *alloc,
|
||||||
|
island_free *dealloc)
|
||||||
|
{
|
||||||
|
assert( originalFunctionAddress );
|
||||||
|
assert( overrideFunctionAddress );
|
||||||
|
|
||||||
|
// this addresses overriding such functions as AudioOutputUnitStart()
|
||||||
|
// test with modified DefaultOutputUnit project
|
||||||
|
#if defined(__x86_64__)
|
||||||
|
for(;;){
|
||||||
|
if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp qword near [rip+0x????????]
|
||||||
|
originalFunctionAddress=*(void**)((char*)originalFunctionAddress+6+*(int32_t *)((uint16_t*)originalFunctionAddress+1));
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
#elif defined(__i386__)
|
||||||
|
for(;;){
|
||||||
|
if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp *0x????????
|
||||||
|
originalFunctionAddress=**(void***)((uint16_t*)originalFunctionAddress+1);
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef DEBUG_DISASM
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Replacing function at %p\n", originalFunctionAddress);
|
||||||
|
fprintf(stderr, "First 16 bytes of the function: ");
|
||||||
|
unsigned char *orig = (unsigned char *)originalFunctionAddress;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
fprintf(stderr, "%x ", (unsigned int) orig[i]);
|
||||||
|
}
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
fprintf(stderr,
|
||||||
|
"To disassemble, save the following function as disas.c"
|
||||||
|
" and run:\n gcc -c disas.c && gobjdump -d disas.o\n"
|
||||||
|
"The first 16 bytes of the original function will start"
|
||||||
|
" after four nop instructions.\n");
|
||||||
|
fprintf(stderr, "\nvoid foo() {\n asm volatile(\"nop;nop;nop;nop;\");\n");
|
||||||
|
int j = 0;
|
||||||
|
for (j = 0; j < 2; j++) {
|
||||||
|
fprintf(stderr, " asm volatile(\".byte ");
|
||||||
|
for (i = 8 * j; i < 8 * (j+1) - 1; i++) {
|
||||||
|
fprintf(stderr, "0x%x, ", (unsigned int) orig[i]);
|
||||||
|
}
|
||||||
|
fprintf(stderr, "0x%x;\");\n", (unsigned int) orig[8 * (j+1) - 1]);
|
||||||
|
}
|
||||||
|
fprintf(stderr, "}\n\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
long *originalFunctionPtr = (long*) originalFunctionAddress;
|
||||||
|
mach_error_t err = err_none;
|
||||||
|
|
||||||
|
#if defined(__ppc__) || defined(__POWERPC__)
|
||||||
|
// Ensure first instruction isn't 'mfctr'.
|
||||||
|
#define kMFCTRMask 0xfc1fffff
|
||||||
|
#define kMFCTRInstruction 0x7c0903a6
|
||||||
|
|
||||||
|
long originalInstruction = *originalFunctionPtr;
|
||||||
|
if( !err && ((originalInstruction & kMFCTRMask) == kMFCTRInstruction) )
|
||||||
|
err = err_cannot_override;
|
||||||
|
#elif defined(__i386__) || defined(__x86_64__)
|
||||||
|
int eatenCount = 0;
|
||||||
|
int originalInstructionCount = 0;
|
||||||
|
char originalInstructions[kOriginalInstructionsSize];
|
||||||
|
uint8_t originalInstructionSizes[kOriginalInstructionsSize];
|
||||||
|
uint64_t jumpRelativeInstruction = 0; // JMP
|
||||||
|
|
||||||
|
Boolean overridePossible = eatKnownInstructions ((unsigned char *)originalFunctionPtr,
|
||||||
|
&jumpRelativeInstruction, &eatenCount,
|
||||||
|
originalInstructions, &originalInstructionCount,
|
||||||
|
originalInstructionSizes );
|
||||||
|
#ifdef DEBUG_DISASM
|
||||||
|
if (!overridePossible) fprintf(stderr, "overridePossible = false @%d\n", __LINE__);
|
||||||
|
#endif
|
||||||
|
if (eatenCount > kOriginalInstructionsSize) {
|
||||||
|
#ifdef DEBUG_DISASM
|
||||||
|
fprintf(stderr, "Too many instructions eaten\n");
|
||||||
|
#endif
|
||||||
|
overridePossible = false;
|
||||||
|
}
|
||||||
|
if (!overridePossible) err = err_cannot_override;
|
||||||
|
if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Make the original function implementation writable.
|
||||||
|
if( !err ) {
|
||||||
|
err = vm_protect( mach_task_self(),
|
||||||
|
(vm_address_t) originalFunctionPtr, 8, false,
|
||||||
|
(VM_PROT_ALL | VM_PROT_COPY) );
|
||||||
|
if( err )
|
||||||
|
err = vm_protect( mach_task_self(),
|
||||||
|
(vm_address_t) originalFunctionPtr, 8, false,
|
||||||
|
(VM_PROT_DEFAULT | VM_PROT_COPY) );
|
||||||
|
}
|
||||||
|
if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
|
||||||
|
|
||||||
|
// Allocate and target the escape island to the overriding function.
|
||||||
|
BranchIsland *escapeIsland = NULL;
|
||||||
|
if( !err )
|
||||||
|
err = alloc( (void**)&escapeIsland, sizeof(BranchIsland), originalFunctionAddress );
|
||||||
|
if ( err ) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
|
||||||
|
|
||||||
|
#if defined(__ppc__) || defined(__POWERPC__)
|
||||||
|
if( !err )
|
||||||
|
err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 );
|
||||||
|
|
||||||
|
// Build the branch absolute instruction to the escape island.
|
||||||
|
long branchAbsoluteInstruction = 0; // Set to 0 just to silence warning.
|
||||||
|
if( !err ) {
|
||||||
|
long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF;
|
||||||
|
branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress;
|
||||||
|
}
|
||||||
|
#elif defined(__i386__) || defined(__x86_64__)
|
||||||
|
if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
|
||||||
|
|
||||||
|
if( !err )
|
||||||
|
err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 );
|
||||||
|
|
||||||
|
if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
|
||||||
|
// Build the jump relative instruction to the escape island
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
|
if (!err) {
|
||||||
|
uint32_t addressOffset = ((char*)escapeIsland - (char*)originalFunctionPtr - 5);
|
||||||
|
addressOffset = OSSwapInt32(addressOffset);
|
||||||
|
|
||||||
|
jumpRelativeInstruction |= 0xE900000000000000LL;
|
||||||
|
jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24;
|
||||||
|
jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Optionally allocate & return the reentry island. This may contain relocated
|
||||||
|
// jmp instructions and so has all the same addressing reachability requirements
|
||||||
|
// the escape island has to the original function, except the escape island is
|
||||||
|
// technically our original function.
|
||||||
|
BranchIsland *reentryIsland = NULL;
|
||||||
|
if( !err && originalFunctionReentryIsland ) {
|
||||||
|
err = alloc( (void**)&reentryIsland, sizeof(BranchIsland), escapeIsland);
|
||||||
|
if( !err )
|
||||||
|
*originalFunctionReentryIsland = reentryIsland;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__ppc__) || defined(__POWERPC__)
|
||||||
|
// Atomically:
|
||||||
|
// o If the reentry island was allocated:
|
||||||
|
// o Insert the original instruction into the reentry island.
|
||||||
|
// o Target the reentry island at the 2nd instruction of the
|
||||||
|
// original function.
|
||||||
|
// o Replace the original instruction with the branch absolute.
|
||||||
|
if( !err ) {
|
||||||
|
int escapeIslandEngaged = false;
|
||||||
|
do {
|
||||||
|
if( reentryIsland )
|
||||||
|
err = setBranchIslandTarget( reentryIsland,
|
||||||
|
(void*) (originalFunctionPtr+1), originalInstruction );
|
||||||
|
if( !err ) {
|
||||||
|
escapeIslandEngaged = CompareAndSwap( originalInstruction,
|
||||||
|
branchAbsoluteInstruction,
|
||||||
|
(UInt32*)originalFunctionPtr );
|
||||||
|
if( !escapeIslandEngaged ) {
|
||||||
|
// Someone replaced the instruction out from under us,
|
||||||
|
// re-read the instruction, make sure it's still not
|
||||||
|
// 'mfctr' and try again.
|
||||||
|
originalInstruction = *originalFunctionPtr;
|
||||||
|
if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction)
|
||||||
|
err = err_cannot_override;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while( !err && !escapeIslandEngaged );
|
||||||
|
}
|
||||||
|
#elif defined(__i386__) || defined(__x86_64__)
|
||||||
|
// Atomically:
|
||||||
|
// o If the reentry island was allocated:
|
||||||
|
// o Insert the original instructions into the reentry island.
|
||||||
|
// o Target the reentry island at the first non-replaced
|
||||||
|
// instruction of the original function.
|
||||||
|
// o Replace the original first instructions with the jump relative.
|
||||||
|
//
|
||||||
|
// Note that on i386, we do not support someone else changing the code under our feet
|
||||||
|
if ( !err ) {
|
||||||
|
fixupInstructions(originalFunctionPtr, reentryIsland, originalInstructions,
|
||||||
|
originalInstructionCount, originalInstructionSizes );
|
||||||
|
|
||||||
|
if( reentryIsland )
|
||||||
|
err = setBranchIslandTarget_i386( reentryIsland,
|
||||||
|
(void*) ((char *)originalFunctionPtr+eatenCount), originalInstructions );
|
||||||
|
// try making islands executable before planting the jmp
|
||||||
|
#if defined(__x86_64__) || defined(__i386__)
|
||||||
|
if( !err )
|
||||||
|
err = makeIslandExecutable(escapeIsland);
|
||||||
|
if( !err && reentryIsland )
|
||||||
|
err = makeIslandExecutable(reentryIsland);
|
||||||
|
#endif
|
||||||
|
if ( !err )
|
||||||
|
atomic_mov64((uint64_t *)originalFunctionPtr, jumpRelativeInstruction);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Clean up on error.
|
||||||
|
if( err ) {
|
||||||
|
if( reentryIsland )
|
||||||
|
dealloc( reentryIsland );
|
||||||
|
if( escapeIsland )
|
||||||
|
dealloc( escapeIsland );
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_DISASM
|
||||||
|
{
|
||||||
|
fprintf(stderr, "First 16 bytes of the function after slicing: ");
|
||||||
|
unsigned char *orig = (unsigned char *)originalFunctionAddress;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
fprintf(stderr, "%x ", (unsigned int) orig[i]);
|
||||||
|
}
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
*
|
||||||
|
* Implementation
|
||||||
|
*
|
||||||
|
*******************************************************************************/
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark (Implementation)
|
||||||
|
|
||||||
|
/***************************************************************************//**
|
||||||
|
Implementation: Allocates memory for a branch island.
|
||||||
|
|
||||||
|
@param island <- The allocated island.
|
||||||
|
@param allocateHigh -> Whether to allocate the island at the end of the
|
||||||
|
address space (for use with the branch absolute
|
||||||
|
instruction).
|
||||||
|
@result <- mach_error_t
|
||||||
|
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
static mach_error_t
|
||||||
|
allocateBranchIsland(
|
||||||
|
BranchIsland **island,
|
||||||
|
int allocateHigh,
|
||||||
|
void *originalFunctionAddress)
|
||||||
|
{
|
||||||
|
assert( island );
|
||||||
|
|
||||||
|
mach_error_t err = err_none;
|
||||||
|
|
||||||
|
if( allocateHigh ) {
|
||||||
|
vm_size_t pageSize;
|
||||||
|
err = host_page_size( mach_host_self(), &pageSize );
|
||||||
|
if( !err ) {
|
||||||
|
assert( sizeof( BranchIsland ) <= pageSize );
|
||||||
|
#if defined(__ppc__) || defined(__POWERPC__)
|
||||||
|
vm_address_t first = 0xfeffffff;
|
||||||
|
vm_address_t last = 0xfe000000 + pageSize;
|
||||||
|
#elif defined(__x86_64__)
|
||||||
|
vm_address_t first = ((uint64_t)originalFunctionAddress & ~(uint64_t)(((uint64_t)1 << 31) - 1)) | ((uint64_t)1 << 31); // start in the middle of the page?
|
||||||
|
vm_address_t last = 0x0;
|
||||||
|
#else
|
||||||
|
vm_address_t first = 0xffc00000;
|
||||||
|
vm_address_t last = 0xfffe0000;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
vm_address_t page = first;
|
||||||
|
int allocated = 0;
|
||||||
|
vm_map_t task_self = mach_task_self();
|
||||||
|
|
||||||
|
while( !err && !allocated && page != last ) {
|
||||||
|
|
||||||
|
err = vm_allocate( task_self, &page, pageSize, 0 );
|
||||||
|
if( err == err_none )
|
||||||
|
allocated = 1;
|
||||||
|
else if( err == KERN_NO_SPACE ) {
|
||||||
|
#if defined(__x86_64__)
|
||||||
|
page -= pageSize;
|
||||||
|
#else
|
||||||
|
page += pageSize;
|
||||||
|
#endif
|
||||||
|
err = err_none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( allocated )
|
||||||
|
*island = (BranchIsland*) page;
|
||||||
|
else if( !allocated && !err )
|
||||||
|
err = KERN_NO_SPACE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
void *block = malloc( sizeof( BranchIsland ) );
|
||||||
|
if( block )
|
||||||
|
*island = block;
|
||||||
|
else
|
||||||
|
err = KERN_NO_SPACE;
|
||||||
|
}
|
||||||
|
if( !err )
|
||||||
|
(**island).allocatedHigh = allocateHigh;
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************************//**
|
||||||
|
Implementation: Deallocates memory for a branch island.
|
||||||
|
|
||||||
|
@param island -> The island to deallocate.
|
||||||
|
@result <- mach_error_t
|
||||||
|
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
static mach_error_t
|
||||||
|
freeBranchIsland(
|
||||||
|
BranchIsland *island )
|
||||||
|
{
|
||||||
|
assert( island );
|
||||||
|
assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] );
|
||||||
|
assert( island->allocatedHigh );
|
||||||
|
|
||||||
|
mach_error_t err = err_none;
|
||||||
|
|
||||||
|
if( island->allocatedHigh ) {
|
||||||
|
vm_size_t pageSize;
|
||||||
|
err = host_page_size( mach_host_self(), &pageSize );
|
||||||
|
if( !err ) {
|
||||||
|
assert( sizeof( BranchIsland ) <= pageSize );
|
||||||
|
err = vm_deallocate(
|
||||||
|
mach_task_self(),
|
||||||
|
(vm_address_t) island, pageSize );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
free( island );
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************************//**
|
||||||
|
Implementation: Sets the branch island's target, with an optional
|
||||||
|
instruction.
|
||||||
|
|
||||||
|
@param island -> The branch island to insert target into.
|
||||||
|
@param branchTo -> The address of the target.
|
||||||
|
@param instruction -> Optional instruction to execute prior to branch. Set
|
||||||
|
to zero for nop.
|
||||||
|
@result <- mach_error_t
|
||||||
|
|
||||||
|
***************************************************************************/
|
||||||
|
#if defined(__ppc__) || defined(__POWERPC__)
|
||||||
|
static mach_error_t
|
||||||
|
setBranchIslandTarget(
|
||||||
|
BranchIsland *island,
|
||||||
|
const void *branchTo,
|
||||||
|
long instruction )
|
||||||
|
{
|
||||||
|
// Copy over the template code.
|
||||||
|
bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
|
||||||
|
|
||||||
|
// Fill in the address.
|
||||||
|
((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF;
|
||||||
|
((short*)island->instructions)[kAddressHi]
|
||||||
|
= (((long) branchTo) >> 16) & 0x0000FFFF;
|
||||||
|
|
||||||
|
// Fill in the (optional) instuction.
|
||||||
|
if( instruction != 0 ) {
|
||||||
|
((short*)island->instructions)[kInstructionLo]
|
||||||
|
= instruction & 0x0000FFFF;
|
||||||
|
((short*)island->instructions)[kInstructionHi]
|
||||||
|
= (instruction >> 16) & 0x0000FFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
//MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) );
|
||||||
|
msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
|
||||||
|
|
||||||
|
return err_none;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__i386__)
|
||||||
|
static mach_error_t
|
||||||
|
setBranchIslandTarget_i386(
|
||||||
|
BranchIsland *island,
|
||||||
|
const void *branchTo,
|
||||||
|
char* instructions )
|
||||||
|
{
|
||||||
|
|
||||||
|
// Copy over the template code.
|
||||||
|
bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
|
||||||
|
|
||||||
|
// copy original instructions
|
||||||
|
if (instructions) {
|
||||||
|
bcopy (instructions, island->instructions + kInstructions, kOriginalInstructionsSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill in the address.
|
||||||
|
int32_t addressOffset = (char *)branchTo - (island->instructions + kJumpAddress + 4);
|
||||||
|
*((int32_t *)(island->instructions + kJumpAddress)) = addressOffset;
|
||||||
|
|
||||||
|
msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
|
||||||
|
return err_none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__x86_64__)
|
||||||
|
static mach_error_t
|
||||||
|
setBranchIslandTarget_i386(
|
||||||
|
BranchIsland *island,
|
||||||
|
const void *branchTo,
|
||||||
|
char* instructions )
|
||||||
|
{
|
||||||
|
// Copy over the template code.
|
||||||
|
bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
|
||||||
|
|
||||||
|
// Copy original instructions.
|
||||||
|
if (instructions) {
|
||||||
|
bcopy (instructions, island->instructions, kOriginalInstructionsSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill in the address.
|
||||||
|
*((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo;
|
||||||
|
msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
|
||||||
|
|
||||||
|
return err_none;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
|
// simplistic instruction matching
|
||||||
|
typedef struct {
|
||||||
|
unsigned int length; // max 15
|
||||||
|
unsigned char mask[15]; // sequence of bytes in memory order
|
||||||
|
unsigned char constraint[15]; // sequence of bytes in memory order
|
||||||
|
} AsmInstructionMatch;
|
||||||
|
|
||||||
|
#if defined(__i386__)
|
||||||
|
static AsmInstructionMatch possibleInstructions[] = {
|
||||||
|
{ 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x????????
|
||||||
|
{ 0x5, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0x55, 0x89, 0xe5, 0xc9, 0xc3} }, // push %esp; mov %esp,%ebp; leave; ret
|
||||||
|
{ 0x1, {0xFF}, {0x90} }, // nop
|
||||||
|
{ 0x1, {0xF8}, {0x50} }, // push %reg
|
||||||
|
{ 0x2, {0xFF, 0xFF}, {0x89, 0xE5} }, // mov %esp,%ebp
|
||||||
|
{ 0x3, {0xFF, 0xFF, 0xFF}, {0x89, 0x1C, 0x24} }, // mov %ebx,(%esp)
|
||||||
|
{ 0x3, {0xFF, 0xFF, 0x00}, {0x83, 0xEC, 0x00} }, // sub 0x??, %esp
|
||||||
|
{ 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x81, 0xEC, 0x00, 0x00, 0x00, 0x00} }, // sub 0x??, %esp with 32bit immediate
|
||||||
|
{ 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax
|
||||||
|
{ 0x3, {0xFF, 0x4F, 0x00}, {0x8B, 0x45, 0x00} }, // mov $imm(%ebp), %reg
|
||||||
|
{ 0x3, {0xFF, 0x4C, 0x00}, {0x8B, 0x40, 0x00} }, // mov $imm(%eax-%edx), %reg
|
||||||
|
{ 0x3, {0xFF, 0xCF, 0x00}, {0x8B, 0x4D, 0x00} }, // mov $imm(%rpb), %reg
|
||||||
|
{ 0x3, {0xFF, 0x4F, 0x00}, {0x8A, 0x4D, 0x00} }, // mov $imm(%ebp), %cl
|
||||||
|
{ 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x8B, 0x4C, 0x24, 0x00} }, // mov $imm(%esp), %ecx
|
||||||
|
{ 0x4, {0xFF, 0x00, 0x00, 0x00}, {0x8B, 0x00, 0x00, 0x00} }, // mov r16,r/m16 or r32,r/m32
|
||||||
|
{ 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xB9, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %ecx
|
||||||
|
{ 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %eax
|
||||||
|
{ 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x66, 0x0F, 0xEF, 0x00} }, // pxor xmm2/128, xmm1
|
||||||
|
{ 0x2, {0xFF, 0xFF}, {0xDB, 0xE3} }, // fninit
|
||||||
|
{ 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE8, 0x00, 0x00, 0x00, 0x00} }, // call $imm
|
||||||
|
{ 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x0F, 0xBE, 0x55, 0x00} }, // movsbl $imm(%ebp), %edx
|
||||||
|
{ 0x0, {0x00}, {0x00} }
|
||||||
|
};
|
||||||
|
#elif defined(__x86_64__)
|
||||||
|
// TODO(glider): disassembling the "0x48, 0x89" sequences is trickier than it's done below.
|
||||||
|
// If it stops working, refer to http://ref.x86asm.net/geek.html#modrm_byte_32_64 to do it
|
||||||
|
// more accurately.
|
||||||
|
// Note: 0x48 is in fact the REX.W prefix, but it might be wrong to treat it as a separate
|
||||||
|
// instruction.
|
||||||
|
static AsmInstructionMatch possibleInstructions[] = {
|
||||||
|
{ 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x????????
|
||||||
|
{ 0x1, {0xFF}, {0x90} }, // nop
|
||||||
|
{ 0x1, {0xF8}, {0x50} }, // push %rX
|
||||||
|
{ 0x1, {0xFF}, {0x65} }, // GS prefix
|
||||||
|
{ 0x3, {0xFF, 0xFF, 0xFF}, {0x48, 0x89, 0xE5} }, // mov %rsp,%rbp
|
||||||
|
{ 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xEC, 0x00} }, // sub 0x??, %rsp
|
||||||
|
{ 0x4, {0xFB, 0xFF, 0x07, 0x00}, {0x48, 0x89, 0x05, 0x00} }, // move onto rbp
|
||||||
|
{ 0x3, {0xFB, 0xFF, 0x00}, {0x48, 0x89, 0x00} }, // mov %reg, %reg
|
||||||
|
{ 0x3, {0xFB, 0xFF, 0x00}, {0x49, 0x89, 0x00} }, // mov %reg, %reg (REX.WB)
|
||||||
|
{ 0x2, {0xFF, 0x00}, {0x41, 0x00} }, // push %rXX
|
||||||
|
{ 0x2, {0xFF, 0x00}, {0x84, 0x00} }, // test %rX8,%rX8
|
||||||
|
{ 0x2, {0xFF, 0x00}, {0x85, 0x00} }, // test %rX,%rX
|
||||||
|
{ 0x2, {0xFF, 0x00}, {0x77, 0x00} }, // ja $i8
|
||||||
|
{ 0x2, {0xFF, 0x00}, {0x74, 0x00} }, // je $i8
|
||||||
|
{ 0x5, {0xF8, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %reg
|
||||||
|
{ 0x3, {0xFF, 0xFF, 0x00}, {0xFF, 0x77, 0x00} }, // pushq $imm(%rdi)
|
||||||
|
{ 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax
|
||||||
|
{ 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0x25, 0x00, 0x00, 0x00, 0x00} }, // and $imm, %eax
|
||||||
|
{ 0x3, {0xFF, 0xFF, 0xFF}, {0x80, 0x3F, 0x00} }, // cmpb $imm, (%rdi)
|
||||||
|
|
||||||
|
{ 0x8, {0xFF, 0xFF, 0xCF, 0xFF, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
{0x48, 0x8B, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00}, }, // mov $imm, %{rax,rdx,rsp,rsi}
|
||||||
|
{ 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xFA, 0x00}, }, // cmp $i8, %rdx
|
||||||
|
{ 0x4, {0xFF, 0xFF, 0x00, 0x00}, {0x83, 0x7f, 0x00, 0x00}, }, // cmpl $imm, $imm(%rdi)
|
||||||
|
{ 0xa, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
{0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %rax
|
||||||
|
{ 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
{0x81, 0xE6, 0x00, 0x00, 0x00, 0x00} }, // and $imm, %esi
|
||||||
|
{ 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
{0xFF, 0x25, 0x00, 0x00, 0x00, 0x00} }, // jmpq *(%rip)
|
||||||
|
{ 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x66, 0x0F, 0xEF, 0x00} }, // pxor xmm2/128, xmm1
|
||||||
|
{ 0x2, {0xFF, 0x00}, {0x89, 0x00} }, // mov r/m32,r32 or r/m16,r16
|
||||||
|
{ 0x3, {0xFF, 0xFF, 0xFF}, {0x49, 0x89, 0xF8} }, // mov %rdi,%r8
|
||||||
|
{ 0x4, {0xFF, 0xFF, 0xFF, 0xFF}, {0x40, 0x0F, 0xBE, 0xCE} }, // movsbl %sil,%ecx
|
||||||
|
{ 0x7, {0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
{0x48, 0x8D, 0x05, 0x00, 0x00, 0x00, 0x00} }, // lea $imm(%rip),%rax
|
||||||
|
{ 0x3, {0xFF, 0xFF, 0xFF}, {0x0F, 0xBE, 0xCE} }, // movsbl, %dh, %ecx
|
||||||
|
{ 0x3, {0xFF, 0xFF, 0x00}, {0xFF, 0x77, 0x00} }, // pushq $imm(%rdi)
|
||||||
|
{ 0x2, {0xFF, 0xFF}, {0xDB, 0xE3} }, // fninit
|
||||||
|
{ 0x3, {0xFF, 0xFF, 0xFF}, {0x48, 0x85, 0xD2} }, // test %rdx,%rdx
|
||||||
|
{ 0x0, {0x00}, {0x00} }
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static Boolean codeMatchesInstruction(unsigned char *code, AsmInstructionMatch* instruction)
|
||||||
|
{
|
||||||
|
Boolean match = true;
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
assert(instruction);
|
||||||
|
#ifdef DEBUG_DISASM
|
||||||
|
fprintf(stderr, "Matching: ");
|
||||||
|
#endif
|
||||||
|
for (i=0; i<instruction->length; i++) {
|
||||||
|
unsigned char mask = instruction->mask[i];
|
||||||
|
unsigned char constraint = instruction->constraint[i];
|
||||||
|
unsigned char codeValue = code[i];
|
||||||
|
#ifdef DEBUG_DISASM
|
||||||
|
fprintf(stderr, "%x ", (unsigned)codeValue);
|
||||||
|
#endif
|
||||||
|
match = ((codeValue & mask) == constraint);
|
||||||
|
if (!match) break;
|
||||||
|
}
|
||||||
|
#ifdef DEBUG_DISASM
|
||||||
|
if (match) {
|
||||||
|
fprintf(stderr, " OK\n");
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, " FAIL\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
|
static Boolean
|
||||||
|
eatKnownInstructions(
|
||||||
|
unsigned char *code,
|
||||||
|
uint64_t *newInstruction,
|
||||||
|
int *howManyEaten,
|
||||||
|
char *originalInstructions,
|
||||||
|
int *originalInstructionCount,
|
||||||
|
uint8_t *originalInstructionSizes )
|
||||||
|
{
|
||||||
|
Boolean allInstructionsKnown = true;
|
||||||
|
int totalEaten = 0;
|
||||||
|
unsigned char* ptr = code;
|
||||||
|
int remainsToEat = 5; // a JMP instruction takes 5 bytes
|
||||||
|
int instructionIndex = 0;
|
||||||
|
|
||||||
|
if (howManyEaten) *howManyEaten = 0;
|
||||||
|
if (originalInstructionCount) *originalInstructionCount = 0;
|
||||||
|
while (remainsToEat > 0) {
|
||||||
|
Boolean curInstructionKnown = false;
|
||||||
|
|
||||||
|
// See if instruction matches one we know
|
||||||
|
AsmInstructionMatch* curInstr = possibleInstructions;
|
||||||
|
do {
|
||||||
|
if ((curInstructionKnown = codeMatchesInstruction(ptr, curInstr))) break;
|
||||||
|
curInstr++;
|
||||||
|
} while (curInstr->length > 0);
|
||||||
|
|
||||||
|
// if all instruction matches failed, we don't know current instruction then, stop here
|
||||||
|
if (!curInstructionKnown) {
|
||||||
|
allInstructionsKnown = false;
|
||||||
|
fprintf(stderr, "mach_override: some instructions unknown! Need to update mach_override.c\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point, we've matched curInstr
|
||||||
|
int eaten = curInstr->length;
|
||||||
|
ptr += eaten;
|
||||||
|
remainsToEat -= eaten;
|
||||||
|
totalEaten += eaten;
|
||||||
|
|
||||||
|
if (originalInstructionSizes) originalInstructionSizes[instructionIndex] = eaten;
|
||||||
|
instructionIndex += 1;
|
||||||
|
if (originalInstructionCount) *originalInstructionCount = instructionIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (howManyEaten) *howManyEaten = totalEaten;
|
||||||
|
|
||||||
|
if (originalInstructions) {
|
||||||
|
Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize);
|
||||||
|
|
||||||
|
if (enoughSpaceForOriginalInstructions) {
|
||||||
|
memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP
|
||||||
|
bcopy(code, originalInstructions, totalEaten);
|
||||||
|
} else {
|
||||||
|
#ifdef DEBUG_DISASM
|
||||||
|
fprintf(stderr, "Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n");
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allInstructionsKnown) {
|
||||||
|
// save last 3 bytes of first 64bits of codre we'll replace
|
||||||
|
uint64_t currentFirst64BitsOfCode = *((uint64_t *)code);
|
||||||
|
currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation
|
||||||
|
currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL;
|
||||||
|
|
||||||
|
// keep only last 3 instructions bytes, first 5 will be replaced by JMP instr
|
||||||
|
*newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes
|
||||||
|
*newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
return allInstructionsKnown;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fixupInstructions(
|
||||||
|
void *originalFunction,
|
||||||
|
void *escapeIsland,
|
||||||
|
void *instructionsToFix,
|
||||||
|
int instructionCount,
|
||||||
|
uint8_t *instructionSizes )
|
||||||
|
{
|
||||||
|
void *initialOriginalFunction = originalFunction;
|
||||||
|
int index, fixed_size, code_size = 0;
|
||||||
|
for (index = 0;index < instructionCount;index += 1)
|
||||||
|
code_size += instructionSizes[index];
|
||||||
|
|
||||||
|
#ifdef DEBUG_DISASM
|
||||||
|
void *initialInstructionsToFix = instructionsToFix;
|
||||||
|
fprintf(stderr, "BEFORE FIXING:\n");
|
||||||
|
dump16Bytes(initialOriginalFunction);
|
||||||
|
dump16Bytes(initialInstructionsToFix);
|
||||||
|
#endif // DEBUG_DISASM
|
||||||
|
|
||||||
|
for (index = 0;index < instructionCount;index += 1)
|
||||||
|
{
|
||||||
|
fixed_size = instructionSizes[index];
|
||||||
|
if ((*(uint8_t*)instructionsToFix == 0xE9) || // 32-bit jump relative
|
||||||
|
(*(uint8_t*)instructionsToFix == 0xE8)) // 32-bit call relative
|
||||||
|
{
|
||||||
|
uint32_t offset = (uintptr_t)originalFunction - (uintptr_t)escapeIsland;
|
||||||
|
uint32_t *jumpOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 1);
|
||||||
|
*jumpOffsetPtr += offset;
|
||||||
|
}
|
||||||
|
if ((*(uint8_t*)instructionsToFix == 0x74) || // Near jump if equal (je), 2 bytes.
|
||||||
|
(*(uint8_t*)instructionsToFix == 0x77)) // Near jump if above (ja), 2 bytes.
|
||||||
|
{
|
||||||
|
// We replace a near je/ja instruction, "7P JJ", with a 32-bit je/ja, "0F 8P WW XX YY ZZ".
|
||||||
|
// This is critical, otherwise a near jump will likely fall outside the original function.
|
||||||
|
uint32_t offset = (uintptr_t)initialOriginalFunction - (uintptr_t)escapeIsland;
|
||||||
|
uint32_t jumpOffset = *(uint8_t*)((uintptr_t)instructionsToFix + 1);
|
||||||
|
*((uint8_t*)instructionsToFix + 1) = *(uint8_t*)instructionsToFix + 0x10;
|
||||||
|
*(uint8_t*)instructionsToFix = 0x0F;
|
||||||
|
uint32_t *jumpOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 2 );
|
||||||
|
*jumpOffsetPtr = offset + jumpOffset;
|
||||||
|
fixed_size = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
originalFunction = (void*)((uintptr_t)originalFunction + instructionSizes[index]);
|
||||||
|
escapeIsland = (void*)((uintptr_t)escapeIsland + instructionSizes[index]);
|
||||||
|
instructionsToFix = (void*)((uintptr_t)instructionsToFix + fixed_size);
|
||||||
|
|
||||||
|
// Expanding short instructions into longer ones may overwrite the next instructions,
|
||||||
|
// so we must restore them.
|
||||||
|
code_size -= fixed_size;
|
||||||
|
if ((code_size > 0) && (fixed_size != instructionSizes[index])) {
|
||||||
|
bcopy(originalFunction, instructionsToFix, code_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef DEBUG_DISASM
|
||||||
|
fprintf(stderr, "AFTER_FIXING:\n");
|
||||||
|
dump16Bytes(initialOriginalFunction);
|
||||||
|
dump16Bytes(initialInstructionsToFix);
|
||||||
|
#endif // DEBUG_DISASM
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_DISASM
|
||||||
|
#define HEX_DIGIT(x) ((((x) % 16) < 10) ? ('0' + ((x) % 16)) : ('A' + ((x) % 16 - 10)))
|
||||||
|
|
||||||
|
static void
|
||||||
|
dump16Bytes(
|
||||||
|
void *ptr) {
|
||||||
|
int i;
|
||||||
|
char buf[3];
|
||||||
|
uint8_t *bytes = (uint8_t*)ptr;
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
buf[0] = HEX_DIGIT(bytes[i] / 16);
|
||||||
|
buf[1] = HEX_DIGIT(bytes[i] % 16);
|
||||||
|
buf[2] = ' ';
|
||||||
|
write(2, buf, 3);
|
||||||
|
}
|
||||||
|
write(2, "\n", 1);
|
||||||
|
}
|
||||||
|
#endif // DEBUG_DISASM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__i386__)
|
||||||
|
__asm(
|
||||||
|
".text;"
|
||||||
|
".align 2, 0x90;"
|
||||||
|
"_atomic_mov64:;"
|
||||||
|
" pushl %ebp;"
|
||||||
|
" movl %esp, %ebp;"
|
||||||
|
" pushl %esi;"
|
||||||
|
" pushl %ebx;"
|
||||||
|
" pushl %ecx;"
|
||||||
|
" pushl %eax;"
|
||||||
|
" pushl %edx;"
|
||||||
|
|
||||||
|
// atomic push of value to an address
|
||||||
|
// we use cmpxchg8b, which compares content of an address with
|
||||||
|
// edx:eax. If they are equal, it atomically puts 64bit value
|
||||||
|
// ecx:ebx in address.
|
||||||
|
// We thus put contents of address in edx:eax to force ecx:ebx
|
||||||
|
// in address
|
||||||
|
" mov 8(%ebp), %esi;" // esi contains target address
|
||||||
|
" mov 12(%ebp), %ebx;"
|
||||||
|
" mov 16(%ebp), %ecx;" // ecx:ebx now contains value to put in target address
|
||||||
|
" mov (%esi), %eax;"
|
||||||
|
" mov 4(%esi), %edx;" // edx:eax now contains value currently contained in target address
|
||||||
|
" lock; cmpxchg8b (%esi);" // atomic move.
|
||||||
|
|
||||||
|
// restore registers
|
||||||
|
" popl %edx;"
|
||||||
|
" popl %eax;"
|
||||||
|
" popl %ecx;"
|
||||||
|
" popl %ebx;"
|
||||||
|
" popl %esi;"
|
||||||
|
" popl %ebp;"
|
||||||
|
" ret"
|
||||||
|
);
|
||||||
|
#elif defined(__x86_64__)
|
||||||
|
void atomic_mov64(
|
||||||
|
uint64_t *targetAddress,
|
||||||
|
uint64_t value )
|
||||||
|
{
|
||||||
|
*targetAddress = value;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif // __APPLE__
|
140
libsanitizer/interception/mach_override/mach_override.h
Normal file
140
libsanitizer/interception/mach_override/mach_override.h
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
mach_override.h
|
||||||
|
Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
|
||||||
|
Some rights reserved: <http://opensource.org/licenses/mit-license.php>
|
||||||
|
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
/***************************************************************************//**
|
||||||
|
@mainpage mach_override
|
||||||
|
@author Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
|
||||||
|
|
||||||
|
This package, coded in C to the Mach API, allows you to override ("patch")
|
||||||
|
program- and system-supplied functions at runtime. You can fully replace
|
||||||
|
functions with your implementations, or merely head- or tail-patch the
|
||||||
|
original implementations.
|
||||||
|
|
||||||
|
Use it by #include'ing mach_override.h from your .c, .m or .mm file(s).
|
||||||
|
|
||||||
|
@todo Discontinue use of Carbon's MakeDataExecutable() and
|
||||||
|
CompareAndSwap() calls and start using the Mach equivalents, if they
|
||||||
|
exist. If they don't, write them and roll them in. That way, this
|
||||||
|
code will be pure Mach, which will make it easier to use everywhere.
|
||||||
|
Update: MakeDataExecutable() has been replaced by
|
||||||
|
msync(MS_INVALIDATE). There is an OSCompareAndSwap in libkern, but
|
||||||
|
I'm currently unsure if I can link against it. May have to roll in
|
||||||
|
my own version...
|
||||||
|
@todo Stop using an entire 4K high-allocated VM page per 28-byte escape
|
||||||
|
branch island. Done right, this will dramatically speed up escape
|
||||||
|
island allocations when they number over 250. Then again, if you're
|
||||||
|
overriding more than 250 functions, maybe speed isn't your main
|
||||||
|
concern...
|
||||||
|
@todo Add detection of: b, bl, bla, bc, bcl, bcla, bcctrl, bclrl
|
||||||
|
first-instructions. Initially, we should refuse to override
|
||||||
|
functions beginning with these instructions. Eventually, we should
|
||||||
|
dynamically rewrite them to make them position-independent.
|
||||||
|
@todo Write mach_unoverride(), which would remove an override placed on a
|
||||||
|
function. Must be multiple-override aware, which means an almost
|
||||||
|
complete rewrite under the covers, because the target address can't
|
||||||
|
be spread across two load instructions like it is now since it will
|
||||||
|
need to be atomically updatable.
|
||||||
|
@todo Add non-rentry variants of overrides to test_mach_override.
|
||||||
|
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
|
||||||
|
#ifndef _mach_override_
|
||||||
|
#define _mach_override_
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <mach/error.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returned if the function to be overrided begins with a 'mfctr' instruction.
|
||||||
|
*/
|
||||||
|
#define err_cannot_override (err_local|1)
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
Dynamically overrides the function implementation referenced by
|
||||||
|
originalFunctionAddress with the implentation pointed to by overrideFunctionAddress.
|
||||||
|
Optionally returns a pointer to a "reentry island" which, if jumped to, will resume
|
||||||
|
the original implementation.
|
||||||
|
|
||||||
|
@param originalFunctionAddress -> Required address of the function to
|
||||||
|
override (with overrideFunctionAddress).
|
||||||
|
@param overrideFunctionAddress -> Required address to the overriding
|
||||||
|
function.
|
||||||
|
@param originalFunctionReentryIsland <- Optional pointer to pointer to the
|
||||||
|
reentry island. Can be NULL.
|
||||||
|
@result <- err_cannot_override if the original
|
||||||
|
function's implementation begins with
|
||||||
|
the 'mfctr' instruction.
|
||||||
|
|
||||||
|
************************************************************************************/
|
||||||
|
|
||||||
|
// We're prefixing mach_override_ptr() with "__asan_" to avoid name conflicts with other
|
||||||
|
// mach_override_ptr() implementations that may appear in the client program.
|
||||||
|
mach_error_t
|
||||||
|
__asan_mach_override_ptr(
|
||||||
|
void *originalFunctionAddress,
|
||||||
|
const void *overrideFunctionAddress,
|
||||||
|
void **originalFunctionReentryIsland );
|
||||||
|
|
||||||
|
// Allow to use custom allocation and deallocation routines with mach_override_ptr().
|
||||||
|
// This should help to speed up the things on x86_64.
|
||||||
|
typedef mach_error_t island_malloc( void **ptr, size_t size, void *hint );
|
||||||
|
typedef mach_error_t island_free( void *ptr );
|
||||||
|
|
||||||
|
mach_error_t
|
||||||
|
__asan_mach_override_ptr_custom(
|
||||||
|
void *originalFunctionAddress,
|
||||||
|
const void *overrideFunctionAddress,
|
||||||
|
void **originalFunctionReentryIsland,
|
||||||
|
island_malloc *alloc,
|
||||||
|
island_free *dealloc );
|
||||||
|
|
||||||
|
/************************************************************************************//**
|
||||||
|
|
||||||
|
|
||||||
|
************************************************************************************/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
|
||||||
|
#define MACH_OVERRIDE( ORIGINAL_FUNCTION_RETURN_TYPE, ORIGINAL_FUNCTION_NAME, ORIGINAL_FUNCTION_ARGS, ERR ) \
|
||||||
|
{ \
|
||||||
|
static ORIGINAL_FUNCTION_RETURN_TYPE (*ORIGINAL_FUNCTION_NAME##_reenter)ORIGINAL_FUNCTION_ARGS; \
|
||||||
|
static bool ORIGINAL_FUNCTION_NAME##_overriden = false; \
|
||||||
|
class mach_override_class__##ORIGINAL_FUNCTION_NAME { \
|
||||||
|
public: \
|
||||||
|
static kern_return_t override(void *originalFunctionPtr) { \
|
||||||
|
kern_return_t result = err_none; \
|
||||||
|
if (!ORIGINAL_FUNCTION_NAME##_overriden) { \
|
||||||
|
ORIGINAL_FUNCTION_NAME##_overriden = true; \
|
||||||
|
result = mach_override_ptr( (void*)originalFunctionPtr, \
|
||||||
|
(void*)mach_override_class__##ORIGINAL_FUNCTION_NAME::replacement, \
|
||||||
|
(void**)&ORIGINAL_FUNCTION_NAME##_reenter ); \
|
||||||
|
} \
|
||||||
|
return result; \
|
||||||
|
} \
|
||||||
|
static ORIGINAL_FUNCTION_RETURN_TYPE replacement ORIGINAL_FUNCTION_ARGS {
|
||||||
|
|
||||||
|
#define END_MACH_OVERRIDE( ORIGINAL_FUNCTION_NAME ) \
|
||||||
|
} \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
err = mach_override_class__##ORIGINAL_FUNCTION_NAME::override((void*)ORIGINAL_FUNCTION_NAME); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif // _mach_override_
|
||||||
|
|
||||||
|
#endif // __APPLE__
|
Loading…
Reference in New Issue
Block a user