1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/build/mach_override.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,789 @@ 1.4 +// Copied from upstream at revision 195c13743fe0ebc658714e2a9567d86529f20443. 1.5 +// mach_override.c semver:1.2.0 1.6 +// Copyright (c) 2003-2012 Jonathan 'Wolf' Rentzsch: http://rentzsch.com 1.7 +// Some rights reserved: http://opensource.org/licenses/mit 1.8 +// https://github.com/rentzsch/mach_override 1.9 + 1.10 +#include "mach_override.h" 1.11 + 1.12 +#include <mach-o/dyld.h> 1.13 +#include <mach/mach_host.h> 1.14 +#include <mach/mach_init.h> 1.15 +#include <mach/vm_map.h> 1.16 +#include <sys/mman.h> 1.17 + 1.18 +#include <CoreServices/CoreServices.h> 1.19 + 1.20 +/************************** 1.21 +* 1.22 +* Constants 1.23 +* 1.24 +**************************/ 1.25 +#pragma mark - 1.26 +#pragma mark (Constants) 1.27 + 1.28 +#define kPageSize 4096 1.29 +#if defined(__ppc__) || defined(__POWERPC__) 1.30 + 1.31 +long kIslandTemplate[] = { 1.32 + 0x9001FFFC, // stw r0,-4(SP) 1.33 + 0x3C00DEAD, // lis r0,0xDEAD 1.34 + 0x6000BEEF, // ori r0,r0,0xBEEF 1.35 + 0x7C0903A6, // mtctr r0 1.36 + 0x8001FFFC, // lwz r0,-4(SP) 1.37 + 0x60000000, // nop ; optionally replaced 1.38 + 0x4E800420 // bctr 1.39 +}; 1.40 + 1.41 +#define kAddressHi 3 1.42 +#define kAddressLo 5 1.43 +#define kInstructionHi 10 1.44 +#define kInstructionLo 11 1.45 + 1.46 +#elif defined(__i386__) 1.47 + 1.48 +#define kOriginalInstructionsSize 16 1.49 +// On X86 we migh need to instert an add with a 32 bit immediate after the 1.50 +// original instructions. 1.51 +#define kMaxFixupSizeIncrease 5 1.52 + 1.53 +unsigned char kIslandTemplate[] = { 1.54 + // kOriginalInstructionsSize nop instructions so that we 1.55 + // should have enough space to host original instructions 1.56 + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 1.57 + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 1.58 + // Now the real jump instruction 1.59 + 0xE9, 0xEF, 0xBE, 0xAD, 0xDE 1.60 +}; 1.61 + 1.62 +#define kInstructions 0 1.63 +#define kJumpAddress kInstructions + kOriginalInstructionsSize + 1 1.64 +#elif defined(__x86_64__) 1.65 + 1.66 +#define kOriginalInstructionsSize 32 1.67 +// On X86-64 we never need to instert a new instruction. 1.68 +#define kMaxFixupSizeIncrease 0 1.69 + 1.70 +#define kJumpAddress kOriginalInstructionsSize + 6 1.71 + 1.72 +unsigned char kIslandTemplate[] = { 1.73 + // kOriginalInstructionsSize nop instructions so that we 1.74 + // should have enough space to host original instructions 1.75 + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 1.76 + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 1.77 + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 1.78 + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 1.79 + // Now the real jump instruction 1.80 + 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, 1.81 + 0x00, 0x00, 0x00, 0x00, 1.82 + 0x00, 0x00, 0x00, 0x00 1.83 +}; 1.84 + 1.85 +#endif 1.86 + 1.87 +/************************** 1.88 +* 1.89 +* Data Types 1.90 +* 1.91 +**************************/ 1.92 +#pragma mark - 1.93 +#pragma mark (Data Types) 1.94 + 1.95 +typedef struct { 1.96 + char instructions[sizeof(kIslandTemplate)]; 1.97 +} BranchIsland; 1.98 + 1.99 +/************************** 1.100 +* 1.101 +* Funky Protos 1.102 +* 1.103 +**************************/ 1.104 +#pragma mark - 1.105 +#pragma mark (Funky Protos) 1.106 + 1.107 +static mach_error_t 1.108 +allocateBranchIsland( 1.109 + BranchIsland **island, 1.110 + void *originalFunctionAddress); 1.111 + 1.112 + mach_error_t 1.113 +freeBranchIsland( 1.114 + BranchIsland *island ); 1.115 + 1.116 +#if defined(__ppc__) || defined(__POWERPC__) 1.117 + mach_error_t 1.118 +setBranchIslandTarget( 1.119 + BranchIsland *island, 1.120 + const void *branchTo, 1.121 + long instruction ); 1.122 +#endif 1.123 + 1.124 +#if defined(__i386__) || defined(__x86_64__) 1.125 +mach_error_t 1.126 +setBranchIslandTarget_i386( 1.127 + BranchIsland *island, 1.128 + const void *branchTo, 1.129 + char* instructions ); 1.130 +void 1.131 +atomic_mov64( 1.132 + uint64_t *targetAddress, 1.133 + uint64_t value ); 1.134 + 1.135 + static Boolean 1.136 +eatKnownInstructions( 1.137 + unsigned char *code, 1.138 + uint64_t *newInstruction, 1.139 + int *howManyEaten, 1.140 + char *originalInstructions, 1.141 + int *originalInstructionCount, 1.142 + uint8_t *originalInstructionSizes ); 1.143 + 1.144 + static void 1.145 +fixupInstructions( 1.146 + uint32_t offset, 1.147 + void *instructionsToFix, 1.148 + int instructionCount, 1.149 + uint8_t *instructionSizes ); 1.150 +#endif 1.151 + 1.152 +/******************************************************************************* 1.153 +* 1.154 +* Interface 1.155 +* 1.156 +*******************************************************************************/ 1.157 +#pragma mark - 1.158 +#pragma mark (Interface) 1.159 + 1.160 +#if defined(__i386__) || defined(__x86_64__) 1.161 +mach_error_t makeIslandExecutable(void *address) { 1.162 + mach_error_t err = err_none; 1.163 + uintptr_t page = (uintptr_t)address & ~(uintptr_t)(kPageSize-1); 1.164 + int e = err_none; 1.165 + e |= mprotect((void *)page, kPageSize, PROT_EXEC | PROT_READ | PROT_WRITE); 1.166 + e |= msync((void *)page, kPageSize, MS_INVALIDATE ); 1.167 + if (e) { 1.168 + err = err_cannot_override; 1.169 + } 1.170 + return err; 1.171 +} 1.172 +#endif 1.173 + 1.174 + mach_error_t 1.175 +mach_override_ptr( 1.176 + void *originalFunctionAddress, 1.177 + const void *overrideFunctionAddress, 1.178 + void **originalFunctionReentryIsland ) 1.179 +{ 1.180 + assert( originalFunctionAddress ); 1.181 + assert( overrideFunctionAddress ); 1.182 + 1.183 + // this addresses overriding such functions as AudioOutputUnitStart() 1.184 + // test with modified DefaultOutputUnit project 1.185 +#if defined(__x86_64__) 1.186 + for(;;){ 1.187 + if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp qword near [rip+0x????????] 1.188 + originalFunctionAddress=*(void**)((char*)originalFunctionAddress+6+*(int32_t *)((uint16_t*)originalFunctionAddress+1)); 1.189 + else break; 1.190 + } 1.191 +#elif defined(__i386__) 1.192 + for(;;){ 1.193 + if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp *0x???????? 1.194 + originalFunctionAddress=**(void***)((uint16_t*)originalFunctionAddress+1); 1.195 + else break; 1.196 + } 1.197 +#endif 1.198 + 1.199 + long *originalFunctionPtr = (long*) originalFunctionAddress; 1.200 + mach_error_t err = err_none; 1.201 + 1.202 +#if defined(__ppc__) || defined(__POWERPC__) 1.203 + // Ensure first instruction isn't 'mfctr'. 1.204 + #define kMFCTRMask 0xfc1fffff 1.205 + #define kMFCTRInstruction 0x7c0903a6 1.206 + 1.207 + long originalInstruction = *originalFunctionPtr; 1.208 + if( !err && ((originalInstruction & kMFCTRMask) == kMFCTRInstruction) ) 1.209 + err = err_cannot_override; 1.210 +#elif defined(__i386__) || defined(__x86_64__) 1.211 + int eatenCount = 0; 1.212 + int originalInstructionCount = 0; 1.213 + char originalInstructions[kOriginalInstructionsSize]; 1.214 + uint8_t originalInstructionSizes[kOriginalInstructionsSize]; 1.215 + uint64_t jumpRelativeInstruction = 0; // JMP 1.216 + 1.217 + Boolean overridePossible = eatKnownInstructions ((unsigned char *)originalFunctionPtr, 1.218 + &jumpRelativeInstruction, &eatenCount, 1.219 + originalInstructions, &originalInstructionCount, 1.220 + originalInstructionSizes ); 1.221 + if (eatenCount + kMaxFixupSizeIncrease > kOriginalInstructionsSize) { 1.222 + //printf ("Too many instructions eaten\n"); 1.223 + overridePossible = false; 1.224 + } 1.225 + if (!overridePossible) err = err_cannot_override; 1.226 + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); 1.227 +#endif 1.228 + 1.229 + // Make the original function implementation writable. 1.230 + if( !err ) { 1.231 + err = vm_protect( mach_task_self(), 1.232 + (vm_address_t) originalFunctionPtr, 8, false, 1.233 + (VM_PROT_ALL | VM_PROT_COPY) ); 1.234 + if( err ) 1.235 + err = vm_protect( mach_task_self(), 1.236 + (vm_address_t) originalFunctionPtr, 8, false, 1.237 + (VM_PROT_DEFAULT | VM_PROT_COPY) ); 1.238 + } 1.239 + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); 1.240 + 1.241 + // Allocate and target the escape island to the overriding function. 1.242 + BranchIsland *escapeIsland = NULL; 1.243 + if( !err ) 1.244 + err = allocateBranchIsland( &escapeIsland, originalFunctionAddress ); 1.245 + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); 1.246 + 1.247 + 1.248 +#if defined(__ppc__) || defined(__POWERPC__) 1.249 + if( !err ) 1.250 + err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 ); 1.251 + 1.252 + // Build the branch absolute instruction to the escape island. 1.253 + long branchAbsoluteInstruction = 0; // Set to 0 just to silence warning. 1.254 + if( !err ) { 1.255 + long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF; 1.256 + branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress; 1.257 + } 1.258 +#elif defined(__i386__) || defined(__x86_64__) 1.259 + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); 1.260 + 1.261 + if( !err ) 1.262 + err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 ); 1.263 + 1.264 + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); 1.265 + // Build the jump relative instruction to the escape island 1.266 +#endif 1.267 + 1.268 + 1.269 +#if defined(__i386__) || defined(__x86_64__) 1.270 + if (!err) { 1.271 + uint32_t addressOffset = ((char*)escapeIsland - (char*)originalFunctionPtr - 5); 1.272 + addressOffset = OSSwapInt32(addressOffset); 1.273 + 1.274 + jumpRelativeInstruction |= 0xE900000000000000LL; 1.275 + jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24; 1.276 + jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction); 1.277 + } 1.278 +#endif 1.279 + 1.280 + // Optionally allocate & return the reentry island. This may contain relocated 1.281 + // jmp instructions and so has all the same addressing reachability requirements 1.282 + // the escape island has to the original function, except the escape island is 1.283 + // technically our original function. 1.284 + BranchIsland *reentryIsland = NULL; 1.285 + if( !err && originalFunctionReentryIsland ) { 1.286 + err = allocateBranchIsland( &reentryIsland, escapeIsland); 1.287 + if( !err ) 1.288 + *originalFunctionReentryIsland = reentryIsland; 1.289 + } 1.290 + 1.291 +#if defined(__ppc__) || defined(__POWERPC__) 1.292 + // Atomically: 1.293 + // o If the reentry island was allocated: 1.294 + // o Insert the original instruction into the reentry island. 1.295 + // o Target the reentry island at the 2nd instruction of the 1.296 + // original function. 1.297 + // o Replace the original instruction with the branch absolute. 1.298 + if( !err ) { 1.299 + int escapeIslandEngaged = false; 1.300 + do { 1.301 + if( reentryIsland ) 1.302 + err = setBranchIslandTarget( reentryIsland, 1.303 + (void*) (originalFunctionPtr+1), originalInstruction ); 1.304 + if( !err ) { 1.305 + escapeIslandEngaged = CompareAndSwap( originalInstruction, 1.306 + branchAbsoluteInstruction, 1.307 + (UInt32*)originalFunctionPtr ); 1.308 + if( !escapeIslandEngaged ) { 1.309 + // Someone replaced the instruction out from under us, 1.310 + // re-read the instruction, make sure it's still not 1.311 + // 'mfctr' and try again. 1.312 + originalInstruction = *originalFunctionPtr; 1.313 + if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction) 1.314 + err = err_cannot_override; 1.315 + } 1.316 + } 1.317 + } while( !err && !escapeIslandEngaged ); 1.318 + } 1.319 +#elif defined(__i386__) || defined(__x86_64__) 1.320 + // Atomically: 1.321 + // o If the reentry island was allocated: 1.322 + // o Insert the original instructions into the reentry island. 1.323 + // o Target the reentry island at the first non-replaced 1.324 + // instruction of the original function. 1.325 + // o Replace the original first instructions with the jump relative. 1.326 + // 1.327 + // Note that on i386, we do not support someone else changing the code under our feet 1.328 + if ( !err ) { 1.329 + uint32_t offset = (uintptr_t)originalFunctionPtr - (uintptr_t)reentryIsland; 1.330 + fixupInstructions(offset, originalInstructions, 1.331 + originalInstructionCount, originalInstructionSizes ); 1.332 + 1.333 + if( reentryIsland ) 1.334 + err = setBranchIslandTarget_i386( reentryIsland, 1.335 + (void*) ((char *)originalFunctionPtr+eatenCount), originalInstructions ); 1.336 + // try making islands executable before planting the jmp 1.337 +#if defined(__x86_64__) || defined(__i386__) 1.338 + if( !err ) 1.339 + err = makeIslandExecutable(escapeIsland); 1.340 + if( !err && reentryIsland ) 1.341 + err = makeIslandExecutable(reentryIsland); 1.342 +#endif 1.343 + if ( !err ) 1.344 + atomic_mov64((uint64_t *)originalFunctionPtr, jumpRelativeInstruction); 1.345 + } 1.346 +#endif 1.347 + 1.348 + // Clean up on error. 1.349 + if( err ) { 1.350 + if( reentryIsland ) 1.351 + freeBranchIsland( reentryIsland ); 1.352 + if( escapeIsland ) 1.353 + freeBranchIsland( escapeIsland ); 1.354 + } 1.355 + 1.356 + return err; 1.357 +} 1.358 + 1.359 +/******************************************************************************* 1.360 +* 1.361 +* Implementation 1.362 +* 1.363 +*******************************************************************************/ 1.364 +#pragma mark - 1.365 +#pragma mark (Implementation) 1.366 + 1.367 +static bool jump_in_range(intptr_t from, intptr_t to) { 1.368 + intptr_t field_value = to - from - 5; 1.369 + int32_t field_value_32 = field_value; 1.370 + return field_value == field_value_32; 1.371 +} 1.372 + 1.373 +/******************************************************************************* 1.374 + Implementation: Allocates memory for a branch island. 1.375 + 1.376 + @param island <- The allocated island. 1.377 + @result <- mach_error_t 1.378 + 1.379 + ***************************************************************************/ 1.380 + 1.381 +static mach_error_t 1.382 +allocateBranchIslandAux( 1.383 + BranchIsland **island, 1.384 + void *originalFunctionAddress, 1.385 + bool forward) 1.386 +{ 1.387 + assert( island ); 1.388 + assert( sizeof( BranchIsland ) <= kPageSize ); 1.389 + 1.390 + vm_map_t task_self = mach_task_self(); 1.391 + vm_address_t original_address = (vm_address_t) originalFunctionAddress; 1.392 + vm_address_t address = original_address; 1.393 + 1.394 + for (;;) { 1.395 + vm_size_t vmsize = 0; 1.396 + memory_object_name_t object = 0; 1.397 + kern_return_t kr = 0; 1.398 + vm_region_flavor_t flavor = VM_REGION_BASIC_INFO; 1.399 + // Find the region the address is in. 1.400 +#if __WORDSIZE == 32 1.401 + vm_region_basic_info_data_t info; 1.402 + mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT; 1.403 + kr = vm_region(task_self, &address, &vmsize, flavor, 1.404 + (vm_region_info_t)&info, &info_count, &object); 1.405 +#else 1.406 + vm_region_basic_info_data_64_t info; 1.407 + mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64; 1.408 + kr = vm_region_64(task_self, &address, &vmsize, flavor, 1.409 + (vm_region_info_t)&info, &info_count, &object); 1.410 +#endif 1.411 + if (kr != KERN_SUCCESS) 1.412 + return kr; 1.413 + assert((address & (kPageSize - 1)) == 0); 1.414 + 1.415 + // Go to the first page before or after this region 1.416 + vm_address_t new_address = forward ? address + vmsize : address - kPageSize; 1.417 +#if __WORDSIZE == 64 1.418 + if(!jump_in_range(original_address, new_address)) 1.419 + break; 1.420 +#endif 1.421 + address = new_address; 1.422 + 1.423 + // Try to allocate this page. 1.424 + kr = vm_allocate(task_self, &address, kPageSize, 0); 1.425 + if (kr == KERN_SUCCESS) { 1.426 + *island = (BranchIsland*) address; 1.427 + return err_none; 1.428 + } 1.429 + if (kr != KERN_NO_SPACE) 1.430 + return kr; 1.431 + } 1.432 + 1.433 + return KERN_NO_SPACE; 1.434 +} 1.435 + 1.436 +static mach_error_t 1.437 +allocateBranchIsland( 1.438 + BranchIsland **island, 1.439 + void *originalFunctionAddress) 1.440 +{ 1.441 + mach_error_t err = 1.442 + allocateBranchIslandAux(island, originalFunctionAddress, true); 1.443 + if (!err) 1.444 + return err; 1.445 + return allocateBranchIslandAux(island, originalFunctionAddress, false); 1.446 +} 1.447 + 1.448 + 1.449 +/******************************************************************************* 1.450 + Implementation: Deallocates memory for a branch island. 1.451 + 1.452 + @param island -> The island to deallocate. 1.453 + @result <- mach_error_t 1.454 + 1.455 + ***************************************************************************/ 1.456 + 1.457 + mach_error_t 1.458 +freeBranchIsland( 1.459 + BranchIsland *island ) 1.460 +{ 1.461 + assert( island ); 1.462 + assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] ); 1.463 + assert( sizeof( BranchIsland ) <= kPageSize ); 1.464 + return vm_deallocate( mach_task_self(), (vm_address_t) island, 1.465 + kPageSize ); 1.466 +} 1.467 + 1.468 +/******************************************************************************* 1.469 + Implementation: Sets the branch island's target, with an optional 1.470 + instruction. 1.471 + 1.472 + @param island -> The branch island to insert target into. 1.473 + @param branchTo -> The address of the target. 1.474 + @param instruction -> Optional instruction to execute prior to branch. Set 1.475 + to zero for nop. 1.476 + @result <- mach_error_t 1.477 + 1.478 + ***************************************************************************/ 1.479 +#if defined(__ppc__) || defined(__POWERPC__) 1.480 + mach_error_t 1.481 +setBranchIslandTarget( 1.482 + BranchIsland *island, 1.483 + const void *branchTo, 1.484 + long instruction ) 1.485 +{ 1.486 + // Copy over the template code. 1.487 + bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); 1.488 + 1.489 + // Fill in the address. 1.490 + ((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF; 1.491 + ((short*)island->instructions)[kAddressHi] 1.492 + = (((long) branchTo) >> 16) & 0x0000FFFF; 1.493 + 1.494 + // Fill in the (optional) instuction. 1.495 + if( instruction != 0 ) { 1.496 + ((short*)island->instructions)[kInstructionLo] 1.497 + = instruction & 0x0000FFFF; 1.498 + ((short*)island->instructions)[kInstructionHi] 1.499 + = (instruction >> 16) & 0x0000FFFF; 1.500 + } 1.501 + 1.502 + //MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) ); 1.503 + msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); 1.504 + 1.505 + return err_none; 1.506 +} 1.507 +#endif 1.508 + 1.509 +#if defined(__i386__) 1.510 + mach_error_t 1.511 +setBranchIslandTarget_i386( 1.512 + BranchIsland *island, 1.513 + const void *branchTo, 1.514 + char* instructions ) 1.515 +{ 1.516 + 1.517 + // Copy over the template code. 1.518 + bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); 1.519 + 1.520 + // copy original instructions 1.521 + if (instructions) { 1.522 + bcopy (instructions, island->instructions + kInstructions, kOriginalInstructionsSize); 1.523 + } 1.524 + 1.525 + // Fill in the address. 1.526 + int32_t addressOffset = (char *)branchTo - (island->instructions + kJumpAddress + 4); 1.527 + *((int32_t *)(island->instructions + kJumpAddress)) = addressOffset; 1.528 + 1.529 + msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); 1.530 + return err_none; 1.531 +} 1.532 + 1.533 +#elif defined(__x86_64__) 1.534 +mach_error_t 1.535 +setBranchIslandTarget_i386( 1.536 + BranchIsland *island, 1.537 + const void *branchTo, 1.538 + char* instructions ) 1.539 +{ 1.540 + // Copy over the template code. 1.541 + bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); 1.542 + 1.543 + // Copy original instructions. 1.544 + if (instructions) { 1.545 + bcopy (instructions, island->instructions, kOriginalInstructionsSize); 1.546 + } 1.547 + 1.548 + // Fill in the address. 1.549 + *((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo; 1.550 + msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); 1.551 + 1.552 + return err_none; 1.553 +} 1.554 +#endif 1.555 + 1.556 + 1.557 +#if defined(__i386__) || defined(__x86_64__) 1.558 +// simplistic instruction matching 1.559 +typedef struct { 1.560 + unsigned int length; // max 15 1.561 + unsigned char mask[15]; // sequence of bytes in memory order 1.562 + unsigned char constraint[15]; // sequence of bytes in memory order 1.563 +} AsmInstructionMatch; 1.564 + 1.565 +#if defined(__i386__) 1.566 +static AsmInstructionMatch possibleInstructions[] = { 1.567 + { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x???????? 1.568 + { 0x5, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0x55, 0x89, 0xe5, 0xc9, 0xc3} }, // push %ebp; mov %esp,%ebp; leave; ret 1.569 + { 0x1, {0xFF}, {0x90} }, // nop 1.570 + { 0x1, {0xFF}, {0x55} }, // push %esp 1.571 + { 0x2, {0xFF, 0xFF}, {0x89, 0xE5} }, // mov %esp,%ebp 1.572 + { 0x1, {0xFF}, {0x53} }, // push %ebx 1.573 + { 0x3, {0xFF, 0xFF, 0x00}, {0x83, 0xEC, 0x00} }, // sub 0x??, %esp 1.574 + { 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x81, 0xEC, 0x00, 0x00, 0x00, 0x00} }, // sub 0x??, %esp with 32bit immediate 1.575 + { 0x1, {0xFF}, {0x57} }, // push %edi 1.576 + { 0x1, {0xFF}, {0x56} }, // push %esi 1.577 + { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax 1.578 + { 0x3, {0xFF, 0x4F, 0x00}, {0x8B, 0x45, 0x00} }, // mov $imm(%ebp), %reg 1.579 + { 0x3, {0xFF, 0x4C, 0x00}, {0x8B, 0x40, 0x00} }, // mov $imm(%eax-%edx), %reg 1.580 + { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x8B, 0x4C, 0x24, 0x00} }, // mov $imm(%esp), %ecx 1.581 + { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %eax 1.582 + { 0x6, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0xE8, 0x00, 0x00, 0x00, 0x00, 0x58} }, // call $imm; pop %eax 1.583 + { 0x0 } 1.584 +}; 1.585 +#elif defined(__x86_64__) 1.586 +static AsmInstructionMatch possibleInstructions[] = { 1.587 + { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x???????? 1.588 + { 0x1, {0xFF}, {0x90} }, // nop 1.589 + { 0x1, {0xF8}, {0x50} }, // push %rX 1.590 + { 0x3, {0xFF, 0xFF, 0xFF}, {0x48, 0x89, 0xE5} }, // mov %rsp,%rbp 1.591 + { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xEC, 0x00} }, // sub 0x??, %rsp 1.592 + { 0x4, {0xFB, 0xFF, 0x00, 0x00}, {0x48, 0x89, 0x00, 0x00} }, // move onto rbp 1.593 + { 0x4, {0xFF, 0xFF, 0xFF, 0xFF}, {0x40, 0x0f, 0xbe, 0xce} }, // movsbl %sil, %ecx 1.594 + { 0x2, {0xFF, 0x00}, {0x41, 0x00} }, // push %rXX 1.595 + { 0x2, {0xFF, 0x00}, {0x85, 0x00} }, // test %rX,%rX 1.596 + { 0x5, {0xF8, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %reg 1.597 + { 0x3, {0xFF, 0xFF, 0x00}, {0xFF, 0x77, 0x00} }, // pushq $imm(%rdi) 1.598 + { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax 1.599 + { 0x2, {0xFF, 0xFF}, {0x89, 0xF8} }, // mov %edi, %eax 1.600 + 1.601 + //leaq offset(%rip),%rax 1.602 + { 0x7, {0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x48, 0x8d, 0x05, 0x00, 0x00, 0x00, 0x00} }, 1.603 + 1.604 + { 0x0 } 1.605 +}; 1.606 +#endif 1.607 + 1.608 +static Boolean codeMatchesInstruction(unsigned char *code, AsmInstructionMatch* instruction) 1.609 +{ 1.610 + Boolean match = true; 1.611 + 1.612 + size_t i; 1.613 + for (i=0; i<instruction->length; i++) { 1.614 + unsigned char mask = instruction->mask[i]; 1.615 + unsigned char constraint = instruction->constraint[i]; 1.616 + unsigned char codeValue = code[i]; 1.617 + 1.618 + match = ((codeValue & mask) == constraint); 1.619 + if (!match) break; 1.620 + } 1.621 + 1.622 + return match; 1.623 +} 1.624 + 1.625 +#if defined(__i386__) || defined(__x86_64__) 1.626 + static Boolean 1.627 +eatKnownInstructions( 1.628 + unsigned char *code, 1.629 + uint64_t *newInstruction, 1.630 + int *howManyEaten, 1.631 + char *originalInstructions, 1.632 + int *originalInstructionCount, 1.633 + uint8_t *originalInstructionSizes ) 1.634 +{ 1.635 + Boolean allInstructionsKnown = true; 1.636 + int totalEaten = 0; 1.637 + unsigned char* ptr = code; 1.638 + int remainsToEat = 5; // a JMP instruction takes 5 bytes 1.639 + int instructionIndex = 0; 1.640 + 1.641 + if (howManyEaten) *howManyEaten = 0; 1.642 + if (originalInstructionCount) *originalInstructionCount = 0; 1.643 + while (remainsToEat > 0) { 1.644 + Boolean curInstructionKnown = false; 1.645 + 1.646 + // See if instruction matches one we know 1.647 + AsmInstructionMatch* curInstr = possibleInstructions; 1.648 + do { 1.649 + if ((curInstructionKnown = codeMatchesInstruction(ptr, curInstr))) break; 1.650 + curInstr++; 1.651 + } while (curInstr->length > 0); 1.652 + 1.653 + // if all instruction matches failed, we don't know current instruction then, stop here 1.654 + if (!curInstructionKnown) { 1.655 + allInstructionsKnown = false; 1.656 + fprintf(stderr, "mach_override: some instructions unknown! Need to update mach_override.c\n"); 1.657 + break; 1.658 + } 1.659 + 1.660 + // At this point, we've matched curInstr 1.661 + int eaten = curInstr->length; 1.662 + ptr += eaten; 1.663 + remainsToEat -= eaten; 1.664 + totalEaten += eaten; 1.665 + 1.666 + if (originalInstructionSizes) originalInstructionSizes[instructionIndex] = eaten; 1.667 + instructionIndex += 1; 1.668 + if (originalInstructionCount) *originalInstructionCount = instructionIndex; 1.669 + } 1.670 + 1.671 + 1.672 + if (howManyEaten) *howManyEaten = totalEaten; 1.673 + 1.674 + if (originalInstructions) { 1.675 + Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize); 1.676 + 1.677 + if (enoughSpaceForOriginalInstructions) { 1.678 + memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP 1.679 + bcopy(code, originalInstructions, totalEaten); 1.680 + } else { 1.681 + // printf ("Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n"); 1.682 + return false; 1.683 + } 1.684 + } 1.685 + 1.686 + if (allInstructionsKnown) { 1.687 + // save last 3 bytes of first 64bits of codre we'll replace 1.688 + uint64_t currentFirst64BitsOfCode = *((uint64_t *)code); 1.689 + currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation 1.690 + currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL; 1.691 + 1.692 + // keep only last 3 instructions bytes, first 5 will be replaced by JMP instr 1.693 + *newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes 1.694 + *newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes 1.695 + } 1.696 + 1.697 + return allInstructionsKnown; 1.698 +} 1.699 + 1.700 + static void 1.701 +fixupInstructions( 1.702 + uint32_t offset, 1.703 + void *instructionsToFix, 1.704 + int instructionCount, 1.705 + uint8_t *instructionSizes ) 1.706 +{ 1.707 + // The start of "leaq offset(%rip),%rax" 1.708 + static const uint8_t LeaqHeader[] = {0x48, 0x8d, 0x05}; 1.709 + 1.710 + int index; 1.711 + for (index = 0;index < instructionCount;index += 1) 1.712 + { 1.713 + if (*(uint8_t*)instructionsToFix == 0xE9) // 32-bit jump relative 1.714 + { 1.715 + uint32_t *jumpOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 1); 1.716 + *jumpOffsetPtr += offset; 1.717 + } 1.718 + 1.719 + // leaq offset(%rip),%rax 1.720 + if (memcmp(instructionsToFix, LeaqHeader, 3) == 0) { 1.721 + uint32_t *LeaqOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 3); 1.722 + *LeaqOffsetPtr += offset; 1.723 + } 1.724 + 1.725 + // 32-bit call relative to the next addr; pop %eax 1.726 + if (*(uint8_t*)instructionsToFix == 0xE8) 1.727 + { 1.728 + // Just this call is larger than the jump we use, so we 1.729 + // know this is the last instruction. 1.730 + assert(index == (instructionCount - 1)); 1.731 + assert(instructionSizes[index] == 6); 1.732 + 1.733 + // Insert "addl $offset, %eax" in the end so that when 1.734 + // we jump to the rest of the function %eax has the 1.735 + // value it would have if eip had been pushed by the 1.736 + // call in its original position. 1.737 + uint8_t *op = instructionsToFix; 1.738 + op += 6; 1.739 + *op = 0x05; // addl 1.740 + uint32_t *addImmPtr = (uint32_t*)(op + 1); 1.741 + *addImmPtr = offset; 1.742 + } 1.743 + 1.744 + instructionsToFix = (void*)((uintptr_t)instructionsToFix + instructionSizes[index]); 1.745 + } 1.746 +} 1.747 +#endif 1.748 + 1.749 +#if defined(__i386__) 1.750 +__asm( 1.751 + ".text;" 1.752 + ".align 2, 0x90;" 1.753 + "_atomic_mov64:;" 1.754 + " pushl %ebp;" 1.755 + " movl %esp, %ebp;" 1.756 + " pushl %esi;" 1.757 + " pushl %ebx;" 1.758 + " pushl %ecx;" 1.759 + " pushl %eax;" 1.760 + " pushl %edx;" 1.761 + 1.762 + // atomic push of value to an address 1.763 + // we use cmpxchg8b, which compares content of an address with 1.764 + // edx:eax. If they are equal, it atomically puts 64bit value 1.765 + // ecx:ebx in address. 1.766 + // We thus put contents of address in edx:eax to force ecx:ebx 1.767 + // in address 1.768 + " mov 8(%ebp), %esi;" // esi contains target address 1.769 + " mov 12(%ebp), %ebx;" 1.770 + " mov 16(%ebp), %ecx;" // ecx:ebx now contains value to put in target address 1.771 + " mov (%esi), %eax;" 1.772 + " mov 4(%esi), %edx;" // edx:eax now contains value currently contained in target address 1.773 + " lock; cmpxchg8b (%esi);" // atomic move. 1.774 + 1.775 + // restore registers 1.776 + " popl %edx;" 1.777 + " popl %eax;" 1.778 + " popl %ecx;" 1.779 + " popl %ebx;" 1.780 + " popl %esi;" 1.781 + " popl %ebp;" 1.782 + " ret" 1.783 +); 1.784 +#elif defined(__x86_64__) 1.785 +void atomic_mov64( 1.786 + uint64_t *targetAddress, 1.787 + uint64_t value ) 1.788 +{ 1.789 + *targetAddress = value; 1.790 +} 1.791 +#endif 1.792 +#endif