forked from OSchip/llvm-project
[ASan] Delete the code related to static runtime on OS X.
Nuke lib/interception/mach_override. llvm-svn: 174383
This commit is contained in:
parent
65bdeae121
commit
34157fc33f
|
@ -46,10 +46,6 @@ else()
|
|||
ASAN_NEEDS_SEGV=1)
|
||||
endif()
|
||||
|
||||
set(ASAN_DYLIB_DEFINITIONS
|
||||
${ASAN_COMMON_DEFINITIONS}
|
||||
MAC_INTERPOSE_FUNCTIONS=1)
|
||||
|
||||
# Architectures supported by ASan.
|
||||
filter_available_targets(ASAN_SUPPORTED_ARCH
|
||||
x86_64 i386 powerpc64 powerpc)
|
||||
|
@ -57,22 +53,13 @@ filter_available_targets(ASAN_SUPPORTED_ARCH
|
|||
set(ASAN_RUNTIME_LIBRARIES)
|
||||
if(APPLE)
|
||||
# Build universal binary on APPLE.
|
||||
add_compiler_rt_osx_static_runtime(clang_rt.asan_osx
|
||||
ARCH ${ASAN_SUPPORTED_ARCH}
|
||||
SOURCES ${ASAN_SOURCES}
|
||||
$<TARGET_OBJECTS:RTInterception.osx>
|
||||
$<TARGET_OBJECTS:RTSanitizerCommon.osx>
|
||||
CFLAGS ${ASAN_CFLAGS}
|
||||
DEFS ${ASAN_COMMON_DEFINITIONS})
|
||||
list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan_osx)
|
||||
|
||||
add_compiler_rt_osx_dynamic_runtime(clang_rt.asan_osx_dynamic
|
||||
ARCH ${ASAN_SUPPORTED_ARCH}
|
||||
SOURCES ${ASAN_DYLIB_SOURCES}
|
||||
$<TARGET_OBJECTS:RTInterception.osx>
|
||||
$<TARGET_OBJECTS:RTSanitizerCommon.osx>
|
||||
CFLAGS ${ASAN_CFLAGS}
|
||||
DEFS ${ASAN_DYLIB_DEFINITIONS}
|
||||
DEFS ${ASAN_COMMON_DEFINITIONS}
|
||||
# Dynamic lookup is needed because shadow scale and offset are
|
||||
# provided by the instrumented modules.
|
||||
LINKFLAGS "-framework Foundation"
|
||||
|
|
|
@ -18,7 +18,6 @@ Implementation := Generic
|
|||
# FIXME: use automatic dependencies?
|
||||
Dependencies := $(wildcard $(Dir)/*.h)
|
||||
Dependencies += $(wildcard $(Dir)/../interception/*.h)
|
||||
Dependencies += $(wildcard $(Dir)/../interception/mach_override/*.h)
|
||||
Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h)
|
||||
|
||||
# Define a convenience variable for all the asan functions.
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "sanitizer_common/sanitizer_platform_interceptors.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
using __sanitizer::uptr;
|
||||
|
||||
|
@ -68,7 +69,7 @@ using __sanitizer::uptr;
|
|||
|
||||
// On Darwin siglongjmp tailcalls longjmp, so we don't want to intercept it
|
||||
// there.
|
||||
#if !defined(_WIN32) && (!defined(__APPLE__) || MAC_INTERPOSE_FUNCTIONS)
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
# define ASAN_INTERCEPT_SIGLONGJMP 1
|
||||
#else
|
||||
# define ASAN_INTERCEPT_SIGLONGJMP 0
|
||||
|
@ -234,7 +235,7 @@ DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_group_async_f,
|
|||
dispatch_group_t group, dispatch_queue_t dq,
|
||||
void *ctxt, dispatch_function_t func);
|
||||
|
||||
# if MAC_INTERPOSE_FUNCTIONS && !defined(MISSING_BLOCKS_SUPPORT)
|
||||
# if !defined(MISSING_BLOCKS_SUPPORT)
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_group_async,
|
||||
dispatch_group_t dg,
|
||||
dispatch_queue_t dq, void (^work)(void));
|
||||
|
@ -246,7 +247,7 @@ DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_source_set_event_handler,
|
|||
dispatch_source_t ds, void (^work)(void));
|
||||
DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_source_set_cancel_handler,
|
||||
dispatch_source_t ds, void (^work)(void));
|
||||
# endif // MAC_INTERPOSE_FUNCTIONS
|
||||
# endif // MISSING_BLOCKS_SUPPORT
|
||||
|
||||
typedef void malloc_zone_t;
|
||||
typedef size_t vm_size_t;
|
||||
|
|
|
@ -278,13 +278,9 @@ INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) {
|
|||
ASAN_READ_RANGE(from, size);
|
||||
ASAN_WRITE_RANGE(to, size);
|
||||
}
|
||||
#if MAC_INTERPOSE_FUNCTIONS
|
||||
// Interposing of resolver functions is broken on Mac OS 10.7 and 10.8.
|
||||
// See also http://code.google.com/p/address-sanitizer/issues/detail?id=116.
|
||||
return internal_memcpy(to, from, size);
|
||||
#else
|
||||
return REAL(memcpy)(to, from, size);
|
||||
#endif
|
||||
}
|
||||
|
||||
INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) {
|
||||
|
@ -297,13 +293,9 @@ INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) {
|
|||
ASAN_READ_RANGE(from, size);
|
||||
ASAN_WRITE_RANGE(to, size);
|
||||
}
|
||||
#if MAC_INTERPOSE_FUNCTIONS
|
||||
// Interposing of resolver functions is broken on Mac OS 10.7 and 10.8.
|
||||
// See also http://code.google.com/p/address-sanitizer/issues/detail?id=116.
|
||||
return internal_memmove(to, from, size);
|
||||
#else
|
||||
return REAL(memmove)(to, from, size);
|
||||
#endif
|
||||
}
|
||||
|
||||
INTERCEPTOR(void*, memset, void *block, int c, uptr size) {
|
||||
|
@ -401,7 +393,7 @@ INTERCEPTOR(int, strcmp, const char *s1, const char *s2) {
|
|||
}
|
||||
|
||||
INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT
|
||||
#if MAC_INTERPOSE_FUNCTIONS
|
||||
#if defined(__APPLE__)
|
||||
if (!asan_inited) return REAL(strcpy)(to, from); // NOLINT
|
||||
#endif
|
||||
// strcpy is called from malloc_default_purgeable_zone()
|
||||
|
@ -421,7 +413,7 @@ INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT
|
|||
|
||||
#if ASAN_INTERCEPT_STRDUP
|
||||
INTERCEPTOR(char*, strdup, const char *s) {
|
||||
#if MAC_INTERPOSE_FUNCTIONS
|
||||
#if defined(__APPLE__)
|
||||
// FIXME: because internal_strdup() uses InternalAlloc(), which currently
|
||||
// just calls malloc() on Mac, we can't use internal_strdup() with the
|
||||
// dynamic runtime. We can remove the call to REAL(strdup) once InternalAlloc
|
||||
|
@ -562,7 +554,7 @@ INTERCEPTOR(long, strtol, const char *nptr, // NOLINT
|
|||
}
|
||||
|
||||
INTERCEPTOR(int, atoi, const char *nptr) {
|
||||
#if MAC_INTERPOSE_FUNCTIONS
|
||||
#if defined(__APPLE__)
|
||||
if (!asan_inited) return REAL(atoi)(nptr);
|
||||
#endif
|
||||
ENSURE_ASAN_INITED();
|
||||
|
@ -581,7 +573,7 @@ INTERCEPTOR(int, atoi, const char *nptr) {
|
|||
}
|
||||
|
||||
INTERCEPTOR(long, atol, const char *nptr) { // NOLINT
|
||||
#if MAC_INTERPOSE_FUNCTIONS
|
||||
#if defined(__APPLE__)
|
||||
if (!asan_inited) return REAL(atol)(nptr);
|
||||
#endif
|
||||
ENSURE_ASAN_INITED();
|
||||
|
@ -662,10 +654,9 @@ void InitializeAsanInterceptors() {
|
|||
static bool was_called_once;
|
||||
CHECK(was_called_once == false);
|
||||
was_called_once = true;
|
||||
#if MAC_INTERPOSE_FUNCTIONS
|
||||
#if defined(__APPLE__)
|
||||
return;
|
||||
#endif
|
||||
|
||||
#else
|
||||
SANITIZER_COMMON_INTERCEPTORS_INIT;
|
||||
|
||||
// Intercept mem* functions.
|
||||
|
@ -674,12 +665,6 @@ void InitializeAsanInterceptors() {
|
|||
ASAN_INTERCEPT_FUNC(memset);
|
||||
if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) {
|
||||
ASAN_INTERCEPT_FUNC(memcpy);
|
||||
} else {
|
||||
#if !MAC_INTERPOSE_FUNCTIONS
|
||||
// If we're using dynamic interceptors on Mac, these two are just plain
|
||||
// functions.
|
||||
internal_memcpy(&REAL(memcpy), &REAL(memmove), sizeof(REAL(memmove)));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Intercept str* functions.
|
||||
|
@ -701,12 +686,8 @@ void InitializeAsanInterceptors() {
|
|||
#if ASAN_INTERCEPT_STRNLEN
|
||||
ASAN_INTERCEPT_FUNC(strnlen);
|
||||
#endif
|
||||
#if ASAN_INTERCEPT_INDEX
|
||||
# if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX
|
||||
#if ASAN_INTERCEPT_INDEX && ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX
|
||||
ASAN_INTERCEPT_FUNC(index);
|
||||
# else
|
||||
CHECK(OVERRIDE_FUNCTION(index, WRAP(strchr)));
|
||||
# endif
|
||||
#endif
|
||||
|
||||
ASAN_INTERCEPT_FUNC(atoi);
|
||||
|
@ -756,14 +737,10 @@ void InitializeAsanInterceptors() {
|
|||
InitializeWindowsInterceptors();
|
||||
#endif
|
||||
|
||||
// Some Mac-specific interceptors.
|
||||
#if defined(__APPLE__)
|
||||
InitializeMacInterceptors();
|
||||
#endif
|
||||
|
||||
if (flags()->verbosity > 0) {
|
||||
Report("AddressSanitizer: libc interceptors initialized\n");
|
||||
}
|
||||
#endif // __APPLE__
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
|
|
@ -32,9 +32,6 @@ DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act,
|
|||
namespace __asan {
|
||||
|
||||
void InitializeAsanInterceptors();
|
||||
#if defined(__APPLE__)
|
||||
void InitializeMacInterceptors();
|
||||
#endif // __APPLE__
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
|
|
|
@ -91,10 +91,9 @@ static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES";
|
|||
|
||||
void MaybeReexec() {
|
||||
if (!flags()->allow_reexec) return;
|
||||
#if MAC_INTERPOSE_FUNCTIONS
|
||||
// If the program is linked with the dynamic ASan runtime library, make sure
|
||||
// the library is preloaded so that the wrappers work. If it is not, set
|
||||
// DYLD_INSERT_LIBRARIES and re-exec ourselves.
|
||||
// Make sure the dynamic ASan runtime library is preloaded so that the
|
||||
// wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec
|
||||
// ourselves.
|
||||
Dl_info info;
|
||||
CHECK(dladdr((void*)((uptr)__asan_init), &info));
|
||||
const char *dyld_insert_libraries = GetEnv(kDyldInsertLibraries);
|
||||
|
@ -116,8 +115,6 @@ void MaybeReexec() {
|
|||
}
|
||||
execv(program_name, *_NSGetArgv());
|
||||
}
|
||||
#endif // MAC_INTERPOSE_FUNCTIONS
|
||||
// If we're not using the dynamic runtime, do nothing.
|
||||
}
|
||||
|
||||
// No-op. Mac does not support static linkage anyway.
|
||||
|
@ -148,57 +145,6 @@ void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
|
|||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
// The range of pages to be used for escape islands.
|
||||
// TODO(glider): instead of mapping a fixed range we must find a range of
|
||||
// unmapped pages in vmmap and take them.
|
||||
// These constants were chosen empirically and may not work if the shadow
|
||||
// memory layout changes. Unfortunately they do necessarily depend on
|
||||
// kHighMemBeg or kHighMemEnd.
|
||||
static void *island_allocator_pos = 0;
|
||||
|
||||
#if SANITIZER_WORDSIZE == 32
|
||||
# define kIslandEnd (0xffdf0000 - GetPageSizeCached())
|
||||
# define kIslandBeg (kIslandEnd - 256 * GetPageSizeCached())
|
||||
#else
|
||||
# define kIslandEnd (0x7fffffdf0000 - GetPageSizeCached())
|
||||
# define kIslandBeg (kIslandEnd - 256 * GetPageSizeCached())
|
||||
#endif
|
||||
|
||||
extern "C"
|
||||
mach_error_t __interception_allocate_island(void **ptr,
|
||||
uptr unused_size,
|
||||
void *unused_hint) {
|
||||
if (!island_allocator_pos) {
|
||||
island_allocator_pos =
|
||||
internal_mmap((void*)kIslandBeg, kIslandEnd - kIslandBeg,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_PRIVATE | MAP_ANON | MAP_FIXED,
|
||||
-1, 0);
|
||||
if (island_allocator_pos != (void*)kIslandBeg) {
|
||||
return KERN_NO_SPACE;
|
||||
}
|
||||
if (flags()->verbosity) {
|
||||
Report("Mapped pages %p--%p for branch islands.\n",
|
||||
(void*)kIslandBeg, (void*)kIslandEnd);
|
||||
}
|
||||
// Should not be very performance-critical.
|
||||
internal_memset(island_allocator_pos, 0xCC, kIslandEnd - kIslandBeg);
|
||||
};
|
||||
*ptr = island_allocator_pos;
|
||||
island_allocator_pos = (char*)island_allocator_pos + GetPageSizeCached();
|
||||
if (flags()->verbosity) {
|
||||
Report("Branch island allocated at %p\n", *ptr);
|
||||
}
|
||||
return err_none;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
mach_error_t __interception_deallocate_island(void *ptr) {
|
||||
// Do nothing.
|
||||
// TODO(glider): allow to free and reuse the island memory.
|
||||
return err_none;
|
||||
}
|
||||
|
||||
// Support for the following functions from libdispatch on Mac OS:
|
||||
// dispatch_async_f()
|
||||
// dispatch_async()
|
||||
|
@ -352,14 +298,7 @@ INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
|
|||
asan_dispatch_call_block_and_release);
|
||||
}
|
||||
|
||||
#if MAC_INTERPOSE_FUNCTIONS && !defined(MISSING_BLOCKS_SUPPORT)
|
||||
// dispatch_async, dispatch_group_async and others tailcall the corresponding
|
||||
// dispatch_*_f functions. When wrapping functions with mach_override, those
|
||||
// dispatch_*_f are intercepted automatically. But with dylib interposition
|
||||
// this does not work, because the calls within the same library are not
|
||||
// interposed.
|
||||
// Therefore we need to re-implement dispatch_async and friends.
|
||||
|
||||
#if !defined(MISSING_BLOCKS_SUPPORT)
|
||||
extern "C" {
|
||||
// FIXME: consolidate these declarations with asan_intercepted_functions.h.
|
||||
void dispatch_async(dispatch_queue_t dq, void(^work)(void));
|
||||
|
@ -412,16 +351,4 @@ INTERCEPTOR(void, dispatch_source_set_event_handler,
|
|||
}
|
||||
#endif
|
||||
|
||||
namespace __asan {
|
||||
|
||||
void InitializeMacInterceptors() {
|
||||
CHECK(INTERCEPT_FUNCTION(dispatch_async_f));
|
||||
CHECK(INTERCEPT_FUNCTION(dispatch_sync_f));
|
||||
CHECK(INTERCEPT_FUNCTION(dispatch_after_f));
|
||||
CHECK(INTERCEPT_FUNCTION(dispatch_barrier_async_f));
|
||||
CHECK(INTERCEPT_FUNCTION(dispatch_group_async_f));
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
#endif // __APPLE__
|
||||
|
|
|
@ -19,11 +19,6 @@
|
|||
|
||||
namespace __asan {
|
||||
|
||||
#if !MAC_INTERPOSE_FUNCTIONS
|
||||
# error \
|
||||
Dynamic interposing library should be built with -DMAC_INTERPOSE_FUNCTIONS
|
||||
#endif
|
||||
|
||||
#define INTERPOSE_FUNCTION(function) \
|
||||
{ reinterpret_cast<const uptr>(WRAP(function)), \
|
||||
reinterpret_cast<const uptr>(function) }
|
||||
|
|
|
@ -6,20 +6,8 @@ set(INTERCEPTION_SOURCES
|
|||
interception_win.cc
|
||||
)
|
||||
|
||||
set(MACH_OVERRIDE_SOURCES
|
||||
mach_override/mach_override.c
|
||||
)
|
||||
|
||||
include_directories(..)
|
||||
|
||||
# Only add this C file if we're building on a Mac. Other source files can be
|
||||
# harmlessly compiled on any platform, but the C file is complained about due
|
||||
# to pedantic rules about empty translation units.
|
||||
if (APPLE)
|
||||
list(APPEND INTERCEPTION_SOURCES ${MACH_OVERRIDE_SOURCES})
|
||||
set_source_files_properties(${MACH_OVERRIDE_SOURCES} PROPERTIES COMPILE_FLAGS "-std=c99 ${INTERCEPTION_CFLAGS}")
|
||||
endif ()
|
||||
|
||||
set(INTERCEPTION_CFLAGS ${SANITIZER_COMMON_CFLAGS})
|
||||
|
||||
if(APPLE)
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#===------------------------------------------------------------------------===#
|
||||
|
||||
ModuleName := interception
|
||||
SubDirs := mach_override
|
||||
SubDirs :=
|
||||
|
||||
Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file)))
|
||||
ObjNames := $(Sources:%.cc=%.o)
|
||||
|
@ -17,7 +17,6 @@ Implementation := Generic
|
|||
|
||||
# FIXME: use automatic dependencies?
|
||||
Dependencies := $(wildcard $(Dir)/*.h)
|
||||
Dependencies += $(wildcard $(Dir)/mach_override/*.h)
|
||||
Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h)
|
||||
|
||||
# Define a convenience variable for all the interception functions.
|
||||
|
|
|
@ -30,18 +30,6 @@ typedef __sanitizer::s64 INTMAX_T;
|
|||
typedef __sanitizer::u64 OFF_T;
|
||||
typedef __sanitizer::u64 OFF64_T;
|
||||
|
||||
// How to use this library:
|
||||
// 1) Include this header to define your own interceptors
|
||||
// (see details below).
|
||||
// 2) Build all *.cc files and link against them.
|
||||
// On Mac you will also need to:
|
||||
// 3) Provide your own implementation for the following functions:
|
||||
// mach_error_t __interception::allocate_island(void **ptr,
|
||||
// size_t size,
|
||||
// void *hint);
|
||||
// mach_error_t __interception::deallocate_island(void *ptr);
|
||||
// See "interception_mac.h" for more details.
|
||||
|
||||
// How to add an interceptor:
|
||||
// Suppose you need to wrap/replace system function (generally, from libc):
|
||||
// int foo(const char *bar, double baz);
|
||||
|
@ -82,20 +70,12 @@ typedef __sanitizer::u64 OFF64_T;
|
|||
// This is not so on Mac OS, where the two-level namespace makes
|
||||
// our replacement functions invisible to other libraries. This may be overcomed
|
||||
// using the DYLD_FORCE_FLAT_NAMESPACE, but some errors loading the shared
|
||||
// libraries in Chromium were noticed when doing so. Instead we use
|
||||
// mach_override, a handy framework for patching functions at runtime.
|
||||
// To avoid possible name clashes, our replacement functions have
|
||||
// the "wrap_" prefix on Mac.
|
||||
// An alternative to function patching is to create a dylib containing a
|
||||
// __DATA,__interpose section that associates library functions with their
|
||||
// wrappers. When this dylib is preloaded before an executable using
|
||||
// DYLD_INSERT_LIBRARIES, it routes all the calls to interposed functions done
|
||||
// through stubs to the wrapper functions. Such a library is built with
|
||||
// -DMAC_INTERPOSE_FUNCTIONS=1.
|
||||
|
||||
#if !defined(MAC_INTERPOSE_FUNCTIONS) || !defined(__APPLE__)
|
||||
# define MAC_INTERPOSE_FUNCTIONS 0
|
||||
#endif
|
||||
// libraries in Chromium were noticed when doing so.
|
||||
// Instead we create a dylib containing a __DATA,__interpose section that
|
||||
// associates library functions with their wrappers. When this dylib is
|
||||
// preloaded before an executable using DYLD_INSERT_LIBRARIES, it routes all
|
||||
// the calls to interposed functions done through stubs to the wrapper
|
||||
// functions.
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# define WRAP(x) wrap_##x
|
||||
|
@ -122,7 +102,7 @@ typedef __sanitizer::u64 OFF64_T;
|
|||
__attribute__((weak, alias("__interceptor_" #func), visibility("default")));
|
||||
#endif
|
||||
|
||||
#if !MAC_INTERPOSE_FUNCTIONS
|
||||
#if !defined(__APPLE__)
|
||||
# define PTR_TO_REAL(x) real_##x
|
||||
# define REAL(x) __interception::PTR_TO_REAL(x)
|
||||
# define FUNC_TYPE(x) x##_f
|
||||
|
@ -132,11 +112,11 @@ typedef __sanitizer::u64 OFF64_T;
|
|||
namespace __interception { \
|
||||
extern FUNC_TYPE(func) PTR_TO_REAL(func); \
|
||||
}
|
||||
#else // MAC_INTERPOSE_FUNCTIONS
|
||||
#else // __APPLE__
|
||||
# define REAL(x) x
|
||||
# define DECLARE_REAL(ret_type, func, ...) \
|
||||
extern "C" ret_type func(__VA_ARGS__);
|
||||
#endif // MAC_INTERPOSE_FUNCTIONS
|
||||
#endif // __APPLE__
|
||||
|
||||
#define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...) \
|
||||
DECLARE_REAL(ret_type, func, __VA_ARGS__) \
|
||||
|
@ -146,7 +126,7 @@ typedef __sanitizer::u64 OFF64_T;
|
|||
// macros does its job. In exceptional cases you may need to call REAL(foo)
|
||||
// without defining INTERCEPTOR(..., foo, ...). For example, if you override
|
||||
// foo with an interceptor for other function.
|
||||
#if !MAC_INTERPOSE_FUNCTIONS
|
||||
#if !defined(__APPLE__)
|
||||
# define DEFINE_REAL(ret_type, func, ...) \
|
||||
typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \
|
||||
namespace __interception { \
|
||||
|
|
|
@ -15,17 +15,6 @@
|
|||
#ifdef __APPLE__
|
||||
|
||||
#include "interception.h"
|
||||
#include "mach_override/mach_override.h"
|
||||
|
||||
namespace __interception {
|
||||
bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) {
|
||||
*orig_old_func = 0;
|
||||
int res = __asan_mach_override_ptr_custom((void*)old_func, (void*)new_func,
|
||||
(void**)orig_old_func,
|
||||
__interception_allocate_island,
|
||||
__interception_deallocate_island);
|
||||
return (res == 0) && (*orig_old_func != 0);
|
||||
}
|
||||
} // namespace __interception
|
||||
|
||||
#endif // __APPLE__
|
||||
|
|
|
@ -21,29 +21,7 @@
|
|||
#ifndef INTERCEPTION_MAC_H
|
||||
#define INTERCEPTION_MAC_H
|
||||
|
||||
#include <mach/mach_error.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// Allocate memory for the escape island. This cannot be moved to
|
||||
// mach_override, because each user of interceptors may specify its
|
||||
// own memory range for escape islands.
|
||||
extern "C" {
|
||||
mach_error_t __interception_allocate_island(void **ptr, size_t unused_size,
|
||||
void *unused_hint);
|
||||
mach_error_t __interception_deallocate_island(void *ptr);
|
||||
} // extern "C"
|
||||
|
||||
namespace __interception {
|
||||
// returns true if the old function existed.
|
||||
bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func);
|
||||
} // namespace __interception
|
||||
|
||||
# define OVERRIDE_FUNCTION_MAC(old_func, new_func) \
|
||||
::__interception::OverrideFunction( \
|
||||
(::__interception::uptr)old_func, \
|
||||
(::__interception::uptr)new_func, \
|
||||
(::__interception::uptr*)((::__interception::uptr)&REAL(old_func)))
|
||||
# define INTERCEPT_FUNCTION_MAC(func) OVERRIDE_FUNCTION_MAC(func, WRAP(func))
|
||||
#define INTERCEPT_FUNCTION_MAC(func)
|
||||
|
||||
#endif // INTERCEPTION_MAC_H
|
||||
#endif // __APPLE__
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
|
||||
Some rights reserved: <http://opensource.org/licenses/mit-license.php>
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
#===- lib/interception/mach_override/Makefile.mk -----------*- Makefile -*--===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
ModuleName := interception
|
||||
SubDirs :=
|
||||
|
||||
Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
|
||||
ObjNames := $(Sources:%.c=%.o)
|
||||
|
||||
Implementation := Generic
|
||||
|
||||
# FIXME: use automatic dependencies?
|
||||
Dependencies := $(wildcard $(Dir)/*.h)
|
||||
|
||||
# Define a convenience variable for all the interception functions.
|
||||
InterceptionFunctions += $(Sources:%.c=%)
|
|
@ -1,9 +0,0 @@
|
|||
-- mach_override.c is taken from upstream version at
|
||||
https://github.com/rentzsch/mach_star/tree/f8e0c424b5be5cb641ded67c265e616157ae4bcf
|
||||
-- Added debugging code under DEBUG_DISASM.
|
||||
-- The files are guarded with #ifdef __APPLE__
|
||||
-- some opcodes are added in order to parse the library functions on Lion
|
||||
-- fixupInstructions() is extended to relocate relative calls, not only jumps
|
||||
-- mach_override_ptr is renamed to __asan_mach_override_ptr and
|
||||
other functions are marked as hidden.
|
||||
|
|
@ -1,970 +0,0 @@
|
|||
/*******************************************************************************
|
||||
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__
|
|
@ -1,140 +0,0 @@
|
|||
/*******************************************************************************
|
||||
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__
|
|
@ -19,7 +19,6 @@ Implementation := Generic
|
|||
# FIXME: use automatic dependencies?
|
||||
Dependencies := $(wildcard $(Dir)/*.h)
|
||||
Dependencies += $(wildcard $(Dir)/../../interception/*.h)
|
||||
Dependencies += $(wildcard $(Dir)/../../interception/mach_override/*.h)
|
||||
Dependencies += $(wildcard $(Dir)/../../sanitizer_common/*.h)
|
||||
|
||||
# Define a convenience variable for all the tsan functions.
|
||||
|
|
Loading…
Reference in New Issue