Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /******************************************************************************* |
michael@0 | 2 | mach_override.h |
michael@0 | 3 | Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com> |
michael@0 | 4 | Some rights reserved: <http://opensource.org/licenses/mit-license.php> |
michael@0 | 5 | |
michael@0 | 6 | ***************************************************************************/ |
michael@0 | 7 | |
michael@0 | 8 | /***************************************************************************//** |
michael@0 | 9 | @mainpage mach_override |
michael@0 | 10 | @author Jonathan 'Wolf' Rentzsch: <http://rentzsch.com> |
michael@0 | 11 | |
michael@0 | 12 | This package, coded in C to the Mach API, allows you to override ("patch") |
michael@0 | 13 | program- and system-supplied functions at runtime. You can fully replace |
michael@0 | 14 | functions with your implementations, or merely head- or tail-patch the |
michael@0 | 15 | original implementations. |
michael@0 | 16 | |
michael@0 | 17 | Use it by #include'ing mach_override.h from your .c, .m or .mm file(s). |
michael@0 | 18 | |
michael@0 | 19 | @todo Discontinue use of Carbon's MakeDataExecutable() and |
michael@0 | 20 | CompareAndSwap() calls and start using the Mach equivalents, if they |
michael@0 | 21 | exist. If they don't, write them and roll them in. That way, this |
michael@0 | 22 | code will be pure Mach, which will make it easier to use everywhere. |
michael@0 | 23 | Update: MakeDataExecutable() has been replaced by |
michael@0 | 24 | msync(MS_INVALIDATE). There is an OSCompareAndSwap in libkern, but |
michael@0 | 25 | I'm currently unsure if I can link against it. May have to roll in |
michael@0 | 26 | my own version... |
michael@0 | 27 | @todo Stop using an entire 4K high-allocated VM page per 28-byte escape |
michael@0 | 28 | branch island. Done right, this will dramatically speed up escape |
michael@0 | 29 | island allocations when they number over 250. Then again, if you're |
michael@0 | 30 | overriding more than 250 functions, maybe speed isn't your main |
michael@0 | 31 | concern... |
michael@0 | 32 | @todo Add detection of: b, bl, bla, bc, bcl, bcla, bcctrl, bclrl |
michael@0 | 33 | first-instructions. Initially, we should refuse to override |
michael@0 | 34 | functions beginning with these instructions. Eventually, we should |
michael@0 | 35 | dynamically rewrite them to make them position-independent. |
michael@0 | 36 | @todo Write mach_unoverride(), which would remove an override placed on a |
michael@0 | 37 | function. Must be multiple-override aware, which means an almost |
michael@0 | 38 | complete rewrite under the covers, because the target address can't |
michael@0 | 39 | be spread across two load instructions like it is now since it will |
michael@0 | 40 | need to be atomically updatable. |
michael@0 | 41 | @todo Add non-rentry variants of overrides to test_mach_override. |
michael@0 | 42 | |
michael@0 | 43 | ***************************************************************************/ |
michael@0 | 44 | |
michael@0 | 45 | #ifndef _mach_override_ |
michael@0 | 46 | #define _mach_override_ |
michael@0 | 47 | |
michael@0 | 48 | #include <sys/types.h> |
michael@0 | 49 | #include <mach/error.h> |
michael@0 | 50 | |
michael@0 | 51 | #ifdef __cplusplus |
michael@0 | 52 | extern "C" { |
michael@0 | 53 | #endif |
michael@0 | 54 | |
michael@0 | 55 | /** |
michael@0 | 56 | Returned if the function to be overrided begins with a 'mfctr' instruction. |
michael@0 | 57 | */ |
michael@0 | 58 | #define err_cannot_override (err_local|1) |
michael@0 | 59 | |
michael@0 | 60 | /************************************************************************************//** |
michael@0 | 61 | Dynamically overrides the function implementation referenced by |
michael@0 | 62 | originalFunctionAddress with the implentation pointed to by overrideFunctionAddress. |
michael@0 | 63 | Optionally returns a pointer to a "reentry island" which, if jumped to, will resume |
michael@0 | 64 | the original implementation. |
michael@0 | 65 | |
michael@0 | 66 | @param originalFunctionAddress -> Required address of the function to |
michael@0 | 67 | override (with overrideFunctionAddress). |
michael@0 | 68 | @param overrideFunctionAddress -> Required address to the overriding |
michael@0 | 69 | function. |
michael@0 | 70 | @param originalFunctionReentryIsland <- Optional pointer to pointer to the |
michael@0 | 71 | reentry island. Can be nullptr. |
michael@0 | 72 | @result <- err_cannot_override if the original |
michael@0 | 73 | function's implementation begins with |
michael@0 | 74 | the 'mfctr' instruction. |
michael@0 | 75 | |
michael@0 | 76 | ************************************************************************************/ |
michael@0 | 77 | |
michael@0 | 78 | mach_error_t |
michael@0 | 79 | mach_override_ptr( |
michael@0 | 80 | void *originalFunctionAddress, |
michael@0 | 81 | const void *overrideFunctionAddress, |
michael@0 | 82 | void **originalFunctionReentryIsland ); |
michael@0 | 83 | |
michael@0 | 84 | /************************************************************************************//** |
michael@0 | 85 | |
michael@0 | 86 | |
michael@0 | 87 | ************************************************************************************/ |
michael@0 | 88 | |
michael@0 | 89 | #ifdef __cplusplus |
michael@0 | 90 | |
michael@0 | 91 | #define MACH_OVERRIDE( ORIGINAL_FUNCTION_RETURN_TYPE, ORIGINAL_FUNCTION_NAME, ORIGINAL_FUNCTION_ARGS, ERR ) \ |
michael@0 | 92 | { \ |
michael@0 | 93 | static ORIGINAL_FUNCTION_RETURN_TYPE (*ORIGINAL_FUNCTION_NAME##_reenter)ORIGINAL_FUNCTION_ARGS; \ |
michael@0 | 94 | static bool ORIGINAL_FUNCTION_NAME##_overriden = false; \ |
michael@0 | 95 | class mach_override_class__##ORIGINAL_FUNCTION_NAME { \ |
michael@0 | 96 | public: \ |
michael@0 | 97 | static kern_return_t override(void *originalFunctionPtr) { \ |
michael@0 | 98 | kern_return_t result = err_none; \ |
michael@0 | 99 | if (!ORIGINAL_FUNCTION_NAME##_overriden) { \ |
michael@0 | 100 | ORIGINAL_FUNCTION_NAME##_overriden = true; \ |
michael@0 | 101 | result = mach_override_ptr( (void*)originalFunctionPtr, \ |
michael@0 | 102 | (void*)mach_override_class__##ORIGINAL_FUNCTION_NAME::replacement, \ |
michael@0 | 103 | (void**)&ORIGINAL_FUNCTION_NAME##_reenter ); \ |
michael@0 | 104 | } \ |
michael@0 | 105 | return result; \ |
michael@0 | 106 | } \ |
michael@0 | 107 | static ORIGINAL_FUNCTION_RETURN_TYPE replacement ORIGINAL_FUNCTION_ARGS { |
michael@0 | 108 | |
michael@0 | 109 | #define END_MACH_OVERRIDE( ORIGINAL_FUNCTION_NAME ) \ |
michael@0 | 110 | } \ |
michael@0 | 111 | }; \ |
michael@0 | 112 | \ |
michael@0 | 113 | err = mach_override_class__##ORIGINAL_FUNCTION_NAME::override((void*)ORIGINAL_FUNCTION_NAME); \ |
michael@0 | 114 | } |
michael@0 | 115 | |
michael@0 | 116 | #endif |
michael@0 | 117 | |
michael@0 | 118 | #ifdef __cplusplus |
michael@0 | 119 | } |
michael@0 | 120 | #endif |
michael@0 | 121 | #endif // _mach_override_ |