xpcom/build/mach_override.c

changeset 0
6474c204b198
     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

mercurial