diff options
-rw-r--r-- | chrome/chrome_renderer.gypi | 8 | ||||
-rw-r--r-- | chrome/renderer/DEPS | 1 | ||||
-rw-r--r-- | chrome/renderer/renderer_main.cc | 34 | ||||
-rw-r--r-- | third_party/mach_override/LICENSE | 2 | ||||
-rw-r--r-- | third_party/mach_override/README.chromium | 29 | ||||
-rw-r--r-- | third_party/mach_override/chromium.diff | 139 | ||||
-rw-r--r-- | third_party/mach_override/mach_override.c | 682 | ||||
-rw-r--r-- | third_party/mach_override/mach_override.gyp | 28 | ||||
-rw-r--r-- | third_party/mach_override/mach_override.h | 121 |
9 files changed, 1041 insertions, 3 deletions
diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi index 1530296..5bcb197 100644 --- a/chrome/chrome_renderer.gypi +++ b/chrome/chrome_renderer.gypi @@ -184,7 +184,11 @@ 'renderer/nacl_desc_wrapper_chrome.cc', ], }], - # Linux-specific rules. + ['OS=="mac"', { + 'dependencies': [ + '../third_party/mach_override/mach_override.gyp:mach_override', + ], + }], ['OS=="linux"', { 'conditions': [ [ 'linux_use_tcmalloc==1', { @@ -199,13 +203,11 @@ '../sandbox/sandbox.gyp:sandbox', ], }], - # BSD-specific rules. ['OS=="openbsd" or OS=="freebsd"', { 'dependencies': [ '../build/linux/system.gyp:gtk', ], }], - # Windows-specific rules. ['OS=="win"', { 'include_dirs': [ '<(DEPTH)/third_party/wtl/include', diff --git a/chrome/renderer/DEPS b/chrome/renderer/DEPS index 3025ecf..f928926 100644 --- a/chrome/renderer/DEPS +++ b/chrome/renderer/DEPS @@ -18,6 +18,7 @@ include_rules = [ "+webkit/plugins", "+v8/include", "+third_party/cld/encodings/compact_lang_det/win", + "+third_party/mach_override", "+third_party/npapi/bindings", "+third_party/sqlite", "+third_party/tcmalloc", diff --git a/chrome/renderer/renderer_main.cc b/chrome/renderer/renderer_main.cc index df75219e..c72fe20 100644 --- a/chrome/renderer/renderer_main.cc +++ b/chrome/renderer/renderer_main.cc @@ -38,14 +38,46 @@ #include "ui/base/ui_base_switches.h" #if defined(OS_MACOSX) +#include <Carbon/Carbon.h> // TISCreateInputSourceList + #include "base/eintr_wrapper.h" +#include "base/sys_info.h" #include "chrome/app/breakpad_mac.h" +#include "third_party/mach_override/mach_override.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" #endif // OS_MACOSX #if defined(OS_MACOSX) namespace { +CFArrayRef ChromeTISCreateInputSourceList( + CFDictionaryRef properties, + Boolean includeAllInstalled) { + CFTypeRef values[] = { CFSTR("") }; + return CFArrayCreate( + kCFAllocatorDefault, values, arraysize(values), &kCFTypeArrayCallBacks); +} + +void InstallFrameworkHacks() { + int32 os_major, os_minor, os_bugfix; + base::SysInfo::OperatingSystemVersionNumbers( + &os_major, &os_minor, &os_bugfix); + + // See http://crbug.com/31225 + // TODO: Don't do this on newer OS X revisions that have a fix for + // http://openradar.appspot.com/radar?id=1156410 + if (os_major == 10 && os_minor >= 6) { + // Chinese Handwriting was introduced in 10.6. Since doing this override + // regresses page cycler memory usage on 10.5, don't do the unnecessary + // override there. + mach_error_t err = mach_override_ptr( + (void*)&TISCreateInputSourceList, + (void*)&ChromeTISCreateInputSourceList, + NULL); + CHECK_EQ(err_none, err); + } +} + // TODO(viettrungluu): crbug.com/28547: The following signal handling is needed, // as a stopgap, to avoid leaking due to not releasing Breakpad properly. // Without this problem, this could all be eliminated. Remove when Breakpad is @@ -231,6 +263,8 @@ int RendererMain(const MainFunctionParams& parameters) { memset(&action, 0, sizeof(action)); action.sa_handler = SIGTERMHandler; CHECK(sigaction(SIGTERM, &action, NULL) == 0); + + InstallFrameworkHacks(); #endif // OS_MACOSX #if defined(OS_CHROMEOS) diff --git a/third_party/mach_override/LICENSE b/third_party/mach_override/LICENSE new file mode 100644 index 0000000..5f6ba7e --- /dev/null +++ b/third_party/mach_override/LICENSE @@ -0,0 +1,2 @@ +Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com> +Some rights reserved: <http://opensource.org/licenses/mit-license.php> diff --git a/third_party/mach_override/README.chromium b/third_party/mach_override/README.chromium new file mode 100644 index 0000000..385ba92 --- /dev/null +++ b/third_party/mach_override/README.chromium @@ -0,0 +1,29 @@ +Name: mach_override +Short Name: Part of the mach_star project +Version: Unknown +URL: https://github.com/rentzsch/mach_star +Date: 04/11/2011 +Revision: 2cd209e090640220b48e5f97e7a366165f9de3f9 +License: MIT +Security Critical: Yes. + + +Description: +This is the mach_override part of the lastest (as of 04/11/2011) revision of +mach_star, namely: + + https://github.com/rentzsch/mach_star/tree/2cd209e090640220b48e5f97e7a366165f9de3f9 + +This package is used to replace framework functions with different +implementations at run time. + + +Local Modifications: + +* Minor fixes to make the file buildable with -std=c99 -Wall -Werror +* Support for |sub %esp| with a 32bit immediate +* atomic_mov64 is no longer .globl +* Removed function mach_override() because it used deprecated functions +* Added a gyp file + +See chromium.diff for details. diff --git a/third_party/mach_override/chromium.diff b/third_party/mach_override/chromium.diff new file mode 100644 index 0000000..2f65cea --- /dev/null +++ b/third_party/mach_override/chromium.diff @@ -0,0 +1,139 @@ +--- /Users/thakis/src/mach_star/mach_override/mach_override.c 2011-04-11 09:35:25.000000000 -0700 ++++ third_party/mach_override/mach_override.c 2011-04-12 16:21:19.000000000 -0700 +@@ -145,36 +145,6 @@ + #pragma mark - + #pragma mark (Interface) + +- mach_error_t +-mach_override( +- char *originalFunctionSymbolName, +- const char *originalFunctionLibraryNameHint, +- const void *overrideFunctionAddress, +- void **originalFunctionReentryIsland ) +-{ +- assert( originalFunctionSymbolName ); +- assert( strlen( originalFunctionSymbolName ) ); +- assert( overrideFunctionAddress ); +- +- // Lookup the original function's code pointer. +- long *originalFunctionPtr; +- if( originalFunctionLibraryNameHint ) +- _dyld_lookup_and_bind_with_hint( +- originalFunctionSymbolName, +- originalFunctionLibraryNameHint, +- (void*) &originalFunctionPtr, +- NULL ); +- else +- _dyld_lookup_and_bind( +- originalFunctionSymbolName, +- (void*) &originalFunctionPtr, +- NULL ); +- +- //printf ("In mach_override\n"); +- return mach_override_ptr( originalFunctionPtr, overrideFunctionAddress, +- originalFunctionReentryIsland ); +-} +- + #if defined(__x86_64__) + mach_error_t makeIslandExecutable(void *address) { + mach_error_t err = err_none; +@@ -563,8 +533,10 @@ + { 0x2, {0xFF, 0xFF}, {0x89, 0xE5} }, // mov %esp,%ebp + { 0x1, {0xFF}, {0x53} }, // push %ebx + { 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 + { 0x1, {0xFF}, {0x57} }, // push %edi + { 0x1, {0xFF}, {0x56} }, // push %esi ++ { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax + { 0x0 } + }; + #elif defined(__x86_64__) +@@ -584,7 +556,7 @@ + { + Boolean match = true; + +- int i; ++ size_t i; + for (i=0; i<instruction->length; i++) { + unsigned char mask = instruction->mask[i]; + unsigned char constraint = instruction->constraint[i]; +@@ -617,7 +589,7 @@ + // See if instruction matches one we know + AsmInstructionMatch* curInstr = possibleInstructions; + do { +- if (curInstructionKnown = codeMatchesInstruction(ptr, curInstr)) break; ++ if ((curInstructionKnown = codeMatchesInstruction(ptr, curInstr))) break; + curInstr++; + } while (curInstr->length > 0); + +@@ -665,10 +637,9 @@ + #endif + + #if defined(__i386__) +-asm( ++__asm( + ".text;" + ".align 2, 0x90;" +- ".globl _atomic_mov64;" + "_atomic_mov64:;" + " pushl %ebp;" + " movl %esp, %ebp;" +@@ -708,4 +679,4 @@ + *targetAddress = value; + } + #endif +-#endif +\ No newline at end of file ++#endif +--- /Users/thakis/src/mach_star/mach_override/mach_override.h 2011-04-11 09:35:25.000000000 -0700 ++++ third_party/mach_override/mach_override.h 2011-04-12 14:17:34.000000000 -0700 +@@ -57,42 +57,6 @@ + */ + #define err_cannot_override (err_local|1) + +-/***************************************************************************//** +- Dynamically overrides the function implementation referenced by +- originalFunctionSymbolName 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 originalFunctionSymbolName -> Required symbol name of the +- function to override (with +- overrideFunctionAddress). +- Remember, C function name +- symbols are prepended with an +- underscore. +- @param originalFunctionLibraryNameHint -> Optional name of the library +- which contains +- originalFunctionSymbolName. Can +- be NULL, but this may result in +- the wrong function being +- overridden and/or a crash. +- @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. +- +- ***************************************************************************/ +- +- mach_error_t +-mach_override( +- char *originalFunctionSymbolName, +- const char *originalFunctionLibraryNameHint, +- const void *overrideFunctionAddress, +- void **originalFunctionReentryIsland ); +- + /************************************************************************************//** + Dynamically overrides the function implementation referenced by + originalFunctionAddress with the implentation pointed to by overrideFunctionAddress. +@@ -154,4 +118,4 @@ + #ifdef __cplusplus + } + #endif +-#endif // _mach_override_ +\ No newline at end of file ++#endif // _mach_override_ diff --git a/third_party/mach_override/mach_override.c b/third_party/mach_override/mach_override.c new file mode 100644 index 0000000..30b6afd --- /dev/null +++ b/third_party/mach_override/mach_override.c @@ -0,0 +1,682 @@ +/******************************************************************************* + mach_override.c + Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com> + Some rights reserved: <http://opensource.org/licenses/mit-license.php> + + ***************************************************************************/ + +#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> + +/************************** +* +* Constants +* +**************************/ +#pragma mark - +#pragma mark (Constants) + +#if defined(__ppc__) || defined(__POWERPC__) + +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 + +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 + +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) + + mach_error_t +allocateBranchIsland( + BranchIsland **island, + int allocateHigh, + void *originalFunctionAddress); + + mach_error_t +freeBranchIsland( + BranchIsland *island ); + +#if defined(__ppc__) || defined(__POWERPC__) + mach_error_t +setBranchIslandTarget( + BranchIsland *island, + const void *branchTo, + long instruction ); +#endif + +#if defined(__i386__) || defined(__x86_64__) +mach_error_t +setBranchIslandTarget_i386( + BranchIsland *island, + const void *branchTo, + char* instructions ); +void +atomic_mov64( + uint64_t *targetAddress, + uint64_t value ); + + static Boolean +eatKnownInstructions( + unsigned char *code, + uint64_t *newInstruction, + int *howManyEaten, + char *originalInstructions ); +#endif + +/******************************************************************************* +* +* Interface +* +*******************************************************************************/ +#pragma mark - +#pragma mark (Interface) + +#if defined(__x86_64__) +mach_error_t makeIslandExecutable(void *address) { + mach_error_t err = err_none; + vm_size_t pageSize; + host_page_size( mach_host_self(), &pageSize ); + uint64_t page = (uint64_t)address & ~(uint64_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 + + mach_error_t +mach_override_ptr( + void *originalFunctionAddress, + const void *overrideFunctionAddress, + void **originalFunctionReentryIsland ) +{ + assert( originalFunctionAddress ); + assert( overrideFunctionAddress ); + + 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; + char originalInstructions[kOriginalInstructionsSize]; + uint64_t jumpRelativeInstruction = 0; // JMP + + Boolean overridePossible = eatKnownInstructions ((unsigned char *)originalFunctionPtr, + &jumpRelativeInstruction, &eatenCount, originalInstructions); + if (eatenCount > kOriginalInstructionsSize) { + //printf ("Too many instructions eaten\n"); + overridePossible = false; + } + if (!overridePossible) err = err_cannot_override; + if (err) printf("err = %x %d\n", err, __LINE__); +#endif + + // Make the original function implementation writable. + if( !err ) { + err = vm_protect( mach_task_self(), + (vm_address_t) originalFunctionPtr, + sizeof(long), false, (VM_PROT_ALL | VM_PROT_COPY) ); + if( err ) + err = vm_protect( mach_task_self(), + (vm_address_t) originalFunctionPtr, sizeof(long), false, + (VM_PROT_DEFAULT | VM_PROT_COPY) ); + } + if (err) printf("err = %x %d\n", err, __LINE__); + + // Allocate and target the escape island to the overriding function. + BranchIsland *escapeIsland = NULL; + if( !err ) + err = allocateBranchIsland( &escapeIsland, kAllocateHigh, originalFunctionAddress ); + if (err) printf("err = %x %d\n", err, __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) printf("err = %x %d\n", err, __LINE__); + + if( !err ) + err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 ); + + if (err) printf("err = %x %d\n", err, __LINE__); + // Build the jump relative instruction to the escape island +#endif + + +#if defined(__i386__) || defined(__x86_64__) + if (!err) { + uint32_t addressOffset = ((void*)escapeIsland - (void*)originalFunctionPtr - 5); + addressOffset = OSSwapInt32(addressOffset); + + jumpRelativeInstruction |= 0xE900000000000000LL; + jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24; + jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction); + } +#endif + + // Optionally allocate & return the reentry island. + BranchIsland *reentryIsland = NULL; + if( !err && originalFunctionReentryIsland ) { + err = allocateBranchIsland( &reentryIsland, kAllocateNormal, NULL); + 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 ) { + if( reentryIsland ) + err = setBranchIslandTarget_i386( reentryIsland, + (void*) ((char *)originalFunctionPtr+eatenCount), originalInstructions ); + if ( !err ) + atomic_mov64((uint64_t *)originalFunctionPtr, jumpRelativeInstruction); + } +#endif + + // Clean up on error. + if( err ) { + if( reentryIsland ) + freeBranchIsland( reentryIsland ); + if( escapeIsland ) + freeBranchIsland( escapeIsland ); + } + +#if defined(__x86_64__) + err = makeIslandExecutable(escapeIsland); + err = makeIslandExecutable(reentryIsland); +#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 + + ***************************************************************************/ + + 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(__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 = 0xfeffffff; + vm_address_t last = 0xfe000000 + pageSize; +#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 = (void*) 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 + + ***************************************************************************/ + + 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__) + 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__) + 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__) +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[] = { + { 0x1, {0xFF}, {0x90} }, // nop + { 0x1, {0xFF}, {0x55} }, // push %esp + { 0x2, {0xFF, 0xFF}, {0x89, 0xE5} }, // mov %esp,%ebp + { 0x1, {0xFF}, {0x53} }, // push %ebx + { 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 + { 0x1, {0xFF}, {0x57} }, // push %edi + { 0x1, {0xFF}, {0x56} }, // push %esi + { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax + { 0x0 } +}; +#elif defined(__x86_64__) +static AsmInstructionMatch possibleInstructions[] = { + { 0x1, {0xFF}, {0x90} }, // nop + { 0x1, {0xF8}, {0x50} }, // push %rX + { 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, 0x00, 0x00}, {0x48, 0x89, 0x00, 0x00} }, // move onto rbp + { 0x2, {0xFF, 0x00}, {0x41, 0x00} }, // push %rXX + { 0x2, {0xFF, 0x00}, {0x85, 0x00} }, // test %rX,%rX + { 0x0 } +}; +#endif + +static Boolean codeMatchesInstruction(unsigned char *code, AsmInstructionMatch* instruction) +{ + Boolean match = true; + + size_t i; + for (i=0; i<instruction->length; i++) { + unsigned char mask = instruction->mask[i]; + unsigned char constraint = instruction->constraint[i]; + unsigned char codeValue = code[i]; + + match = ((codeValue & mask) == constraint); + if (!match) break; + } + + return match; +} + +#if defined(__i386__) || defined(__x86_64__) + static Boolean +eatKnownInstructions( + unsigned char *code, + uint64_t* newInstruction, + int* howManyEaten, + char* originalInstructions ) +{ + Boolean allInstructionsKnown = true; + int totalEaten = 0; + unsigned char* ptr = code; + int remainsToEat = 5; // a JMP instruction takes 5 bytes + + if (howManyEaten) *howManyEaten = 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; + break; + } + + // At this point, we've matched curInstr + int eaten = curInstr->length; + ptr += eaten; + remainsToEat -= eaten; + totalEaten += eaten; + } + + + 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 { + // printf ("Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n"); + 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; +} +#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 diff --git a/third_party/mach_override/mach_override.gyp b/third_party/mach_override/mach_override.gyp new file mode 100644 index 0000000..3dbb1d1 --- /dev/null +++ b/third_party/mach_override/mach_override.gyp @@ -0,0 +1,28 @@ +# Copyright (c) 2011 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + ], + 'conditions': [ + ['OS=="mac"', { + 'targets' : [ + { + 'target_name' : 'mach_override', + 'type': '<(library)', + 'sources': [ + 'mach_override.c', + 'mach_override.h', + ], + }, + ], + }], + ], +} + +# Local Variables: +# tab-width:2 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/third_party/mach_override/mach_override.h b/third_party/mach_override/mach_override.h new file mode 100644 index 0000000..76fdb1b --- /dev/null +++ b/third_party/mach_override/mach_override.h @@ -0,0 +1,121 @@ +/******************************************************************************* + 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. + + ***************************************************************************/ + +#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. + + ************************************************************************************/ + + mach_error_t +mach_override_ptr( + void *originalFunctionAddress, + const void *overrideFunctionAddress, + void **originalFunctionReentryIsland ); + +/************************************************************************************//** + + + ************************************************************************************/ + +#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_ |