michael@0: /******************************************************************************* michael@0: mach_override.h michael@0: Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: michael@0: Some rights reserved: michael@0: michael@0: ***************************************************************************/ michael@0: michael@0: /***************************************************************************//** michael@0: @mainpage mach_override michael@0: @author Jonathan 'Wolf' Rentzsch: michael@0: michael@0: This package, coded in C to the Mach API, allows you to override ("patch") michael@0: program- and system-supplied functions at runtime. You can fully replace michael@0: functions with your implementations, or merely head- or tail-patch the michael@0: original implementations. michael@0: michael@0: Use it by #include'ing mach_override.h from your .c, .m or .mm file(s). michael@0: michael@0: @todo Discontinue use of Carbon's MakeDataExecutable() and michael@0: CompareAndSwap() calls and start using the Mach equivalents, if they michael@0: exist. If they don't, write them and roll them in. That way, this michael@0: code will be pure Mach, which will make it easier to use everywhere. michael@0: Update: MakeDataExecutable() has been replaced by michael@0: msync(MS_INVALIDATE). There is an OSCompareAndSwap in libkern, but michael@0: I'm currently unsure if I can link against it. May have to roll in michael@0: my own version... michael@0: @todo Stop using an entire 4K high-allocated VM page per 28-byte escape michael@0: branch island. Done right, this will dramatically speed up escape michael@0: island allocations when they number over 250. Then again, if you're michael@0: overriding more than 250 functions, maybe speed isn't your main michael@0: concern... michael@0: @todo Add detection of: b, bl, bla, bc, bcl, bcla, bcctrl, bclrl michael@0: first-instructions. Initially, we should refuse to override michael@0: functions beginning with these instructions. Eventually, we should michael@0: dynamically rewrite them to make them position-independent. michael@0: @todo Write mach_unoverride(), which would remove an override placed on a michael@0: function. Must be multiple-override aware, which means an almost michael@0: complete rewrite under the covers, because the target address can't michael@0: be spread across two load instructions like it is now since it will michael@0: need to be atomically updatable. michael@0: @todo Add non-rentry variants of overrides to test_mach_override. michael@0: michael@0: ***************************************************************************/ michael@0: michael@0: #ifndef _mach_override_ michael@0: #define _mach_override_ michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #ifdef __cplusplus michael@0: extern "C" { michael@0: #endif michael@0: michael@0: /** michael@0: Returned if the function to be overrided begins with a 'mfctr' instruction. michael@0: */ michael@0: #define err_cannot_override (err_local|1) michael@0: michael@0: /************************************************************************************//** michael@0: Dynamically overrides the function implementation referenced by michael@0: originalFunctionAddress with the implentation pointed to by overrideFunctionAddress. michael@0: Optionally returns a pointer to a "reentry island" which, if jumped to, will resume michael@0: the original implementation. michael@0: michael@0: @param originalFunctionAddress -> Required address of the function to michael@0: override (with overrideFunctionAddress). michael@0: @param overrideFunctionAddress -> Required address to the overriding michael@0: function. michael@0: @param originalFunctionReentryIsland <- Optional pointer to pointer to the michael@0: reentry island. Can be nullptr. michael@0: @result <- err_cannot_override if the original michael@0: function's implementation begins with michael@0: the 'mfctr' instruction. michael@0: michael@0: ************************************************************************************/ michael@0: michael@0: mach_error_t michael@0: mach_override_ptr( michael@0: void *originalFunctionAddress, michael@0: const void *overrideFunctionAddress, michael@0: void **originalFunctionReentryIsland ); michael@0: michael@0: /************************************************************************************//** michael@0: michael@0: michael@0: ************************************************************************************/ michael@0: michael@0: #ifdef __cplusplus michael@0: michael@0: #define MACH_OVERRIDE( ORIGINAL_FUNCTION_RETURN_TYPE, ORIGINAL_FUNCTION_NAME, ORIGINAL_FUNCTION_ARGS, ERR ) \ michael@0: { \ michael@0: static ORIGINAL_FUNCTION_RETURN_TYPE (*ORIGINAL_FUNCTION_NAME##_reenter)ORIGINAL_FUNCTION_ARGS; \ michael@0: static bool ORIGINAL_FUNCTION_NAME##_overriden = false; \ michael@0: class mach_override_class__##ORIGINAL_FUNCTION_NAME { \ michael@0: public: \ michael@0: static kern_return_t override(void *originalFunctionPtr) { \ michael@0: kern_return_t result = err_none; \ michael@0: if (!ORIGINAL_FUNCTION_NAME##_overriden) { \ michael@0: ORIGINAL_FUNCTION_NAME##_overriden = true; \ michael@0: result = mach_override_ptr( (void*)originalFunctionPtr, \ michael@0: (void*)mach_override_class__##ORIGINAL_FUNCTION_NAME::replacement, \ michael@0: (void**)&ORIGINAL_FUNCTION_NAME##_reenter ); \ michael@0: } \ michael@0: return result; \ michael@0: } \ michael@0: static ORIGINAL_FUNCTION_RETURN_TYPE replacement ORIGINAL_FUNCTION_ARGS { michael@0: michael@0: #define END_MACH_OVERRIDE( ORIGINAL_FUNCTION_NAME ) \ michael@0: } \ michael@0: }; \ michael@0: \ michael@0: err = mach_override_class__##ORIGINAL_FUNCTION_NAME::override((void*)ORIGINAL_FUNCTION_NAME); \ michael@0: } michael@0: michael@0: #endif michael@0: michael@0: #ifdef __cplusplus michael@0: } michael@0: #endif michael@0: #endif // _mach_override_