cygprofile: increase timeouts to allow showing web contents
[chromium-blink-merge.git] / third_party / mach_override / mach_override.c
blob85a75e5c2067d4f6458d669e8c4bb9ce0aa52ade
1 // mach_override.c semver:1.2.0
2 // Copyright (c) 2003-2012 Jonathan 'Wolf' Rentzsch: http://rentzsch.com
3 // Some rights reserved: http://opensource.org/licenses/mit
4 // https://github.com/rentzsch/mach_override
6 #include "mach_override.h"
7 #if defined(__i386__) || defined(__x86_64__)
8 #include "udis86.h"
9 #endif
11 #include <mach-o/dyld.h>
12 #include <mach/mach_init.h>
13 #include <mach/vm_map.h>
14 #include <mach/vm_statistics.h>
15 #include <sys/mman.h>
17 #include <CoreServices/CoreServices.h>
19 /**************************
21 * Constants
23 **************************/
24 #pragma mark -
25 #pragma mark (Constants)
27 #if defined(__ppc__) || defined(__POWERPC__)
29 long kIslandTemplate[] = {
30 0x9001FFFC, // stw r0,-4(SP)
31 0x3C00DEAD, // lis r0,0xDEAD
32 0x6000BEEF, // ori r0,r0,0xBEEF
33 0x7C0903A6, // mtctr r0
34 0x8001FFFC, // lwz r0,-4(SP)
35 0x60000000, // nop ; optionally replaced
36 0x4E800420 // bctr
39 #define kAddressHi 3
40 #define kAddressLo 5
41 #define kInstructionHi 10
42 #define kInstructionLo 11
44 #elif defined(__i386__)
46 #define kOriginalInstructionsSize 16
48 char kIslandTemplate[] = {
49 // kOriginalInstructionsSize nop instructions so that we
50 // should have enough space to host original instructions
51 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
52 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
53 // Now the real jump instruction
54 0xE9, 0xEF, 0xBE, 0xAD, 0xDE
57 #define kInstructions 0
58 #define kJumpAddress kInstructions + kOriginalInstructionsSize + 1
59 #elif defined(__x86_64__)
61 #define kOriginalInstructionsSize 32
63 #define kJumpAddress kOriginalInstructionsSize + 6
65 char kIslandTemplate[] = {
66 // kOriginalInstructionsSize nop instructions so that we
67 // should have enough space to host original instructions
68 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
69 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
70 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
71 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
72 // Now the real jump instruction
73 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00,
74 0x00, 0x00, 0x00, 0x00,
75 0x00, 0x00, 0x00, 0x00
78 #endif
80 #define kAllocateHigh 1
81 #define kAllocateNormal 0
83 /**************************
85 * Data Types
87 **************************/
88 #pragma mark -
89 #pragma mark (Data Types)
91 typedef struct {
92 char instructions[sizeof(kIslandTemplate)];
93 int allocatedHigh;
94 } BranchIsland;
96 /**************************
98 * Funky Protos
100 **************************/
101 #pragma mark -
102 #pragma mark (Funky Protos)
104 mach_error_t
105 allocateBranchIsland(
106 BranchIsland **island,
107 int allocateHigh,
108 void *originalFunctionAddress);
110 mach_error_t
111 freeBranchIsland(
112 BranchIsland *island );
114 #if defined(__ppc__) || defined(__POWERPC__)
115 mach_error_t
116 setBranchIslandTarget(
117 BranchIsland *island,
118 const void *branchTo,
119 long instruction );
120 #endif
122 #if defined(__i386__) || defined(__x86_64__)
123 mach_error_t
124 setBranchIslandTarget_i386(
125 BranchIsland *island,
126 const void *branchTo,
127 char* instructions );
128 void
129 atomic_mov64(
130 uint64_t *targetAddress,
131 uint64_t value );
133 static Boolean
134 eatKnownInstructions(
135 unsigned char *code,
136 uint64_t *newInstruction,
137 int *howManyEaten,
138 char *originalInstructions,
139 int *originalInstructionCount,
140 uint8_t *originalInstructionSizes );
142 static void
143 fixupInstructions(
144 void *originalFunction,
145 void *escapeIsland,
146 void *instructionsToFix,
147 int instructionCount,
148 uint8_t *instructionSizes );
149 #endif
151 /*******************************************************************************
153 * Interface
155 *******************************************************************************/
156 #pragma mark -
157 #pragma mark (Interface)
159 #if defined(__i386__) || defined(__x86_64__)
160 mach_error_t makeIslandExecutable(void *address) {
161 mach_error_t err = err_none;
162 uintptr_t page = (uintptr_t)address & ~(uintptr_t)(PAGE_SIZE - 1);
163 int e = err_none;
164 e |= mprotect((void *)page, PAGE_SIZE, PROT_EXEC | PROT_READ);
165 e |= msync((void *)page, PAGE_SIZE, MS_INVALIDATE );
166 if (e) {
167 err = err_cannot_override;
169 return err;
171 #endif
173 mach_error_t
174 mach_override_ptr(
175 void *originalFunctionAddress,
176 const void *overrideFunctionAddress,
177 void **originalFunctionReentryIsland )
179 assert( originalFunctionAddress );
180 assert( overrideFunctionAddress );
182 // this addresses overriding such functions as AudioOutputUnitStart()
183 // test with modified DefaultOutputUnit project
184 #if defined(__x86_64__)
185 for(;;){
186 if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp qword near [rip+0x????????]
187 originalFunctionAddress=*(void**)((char*)originalFunctionAddress+6+*(int32_t *)((uint16_t*)originalFunctionAddress+1));
188 else break;
190 #elif defined(__i386__)
191 for(;;){
192 if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp *0x????????
193 originalFunctionAddress=**(void***)((uint16_t*)originalFunctionAddress+1);
194 else break;
196 #endif
198 long *originalFunctionPtr = (long*) originalFunctionAddress;
199 mach_error_t err = err_none;
201 #if defined(__ppc__) || defined(__POWERPC__)
202 // Ensure first instruction isn't 'mfctr'.
203 #define kMFCTRMask 0xfc1fffff
204 #define kMFCTRInstruction 0x7c0903a6
206 long originalInstruction = *originalFunctionPtr;
207 if( !err && ((originalInstruction & kMFCTRMask) == kMFCTRInstruction) )
208 err = err_cannot_override;
209 #elif defined(__i386__) || defined(__x86_64__)
210 int eatenCount = 0;
211 int originalInstructionCount = 0;
212 char originalInstructions[kOriginalInstructionsSize];
213 uint8_t originalInstructionSizes[kOriginalInstructionsSize];
214 uint64_t jumpRelativeInstruction = 0; // JMP
216 Boolean overridePossible = eatKnownInstructions ((unsigned char *)originalFunctionPtr,
217 &jumpRelativeInstruction, &eatenCount,
218 originalInstructions, &originalInstructionCount,
219 originalInstructionSizes );
220 if (eatenCount > kOriginalInstructionsSize) {
221 //printf ("Too many instructions eaten\n");
222 overridePossible = false;
224 if (!overridePossible) err = err_cannot_override;
225 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
226 #endif
228 // Make the original function implementation writable.
229 if( !err ) {
230 err = vm_protect( mach_task_self(),
231 (vm_address_t) originalFunctionPtr, 8, false,
232 (VM_PROT_ALL | VM_PROT_COPY) );
233 if( err )
234 err = vm_protect( mach_task_self(),
235 (vm_address_t) originalFunctionPtr, 8, false,
236 (VM_PROT_DEFAULT | VM_PROT_COPY) );
238 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
240 // Allocate and target the escape island to the overriding function.
241 BranchIsland *escapeIsland = NULL;
242 if( !err )
243 err = allocateBranchIsland( &escapeIsland, kAllocateHigh, originalFunctionAddress );
244 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
247 #if defined(__ppc__) || defined(__POWERPC__)
248 if( !err )
249 err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 );
251 // Build the branch absolute instruction to the escape island.
252 long branchAbsoluteInstruction = 0; // Set to 0 just to silence warning.
253 if( !err ) {
254 long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF;
255 branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress;
257 #elif defined(__i386__) || defined(__x86_64__)
258 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
260 if( !err )
261 err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 );
263 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
264 // Build the jump relative instruction to the escape island
265 #endif
268 #if defined(__i386__) || defined(__x86_64__)
269 if (!err) {
270 uint32_t addressOffset = ((char*)escapeIsland - (char*)originalFunctionPtr - 5);
271 addressOffset = OSSwapInt32(addressOffset);
273 jumpRelativeInstruction |= 0xE900000000000000LL;
274 jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24;
275 jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction);
277 #endif
279 // Optionally allocate & return the reentry island. This may contain relocated
280 // jmp instructions and so has all the same addressing reachability requirements
281 // the escape island has to the original function, except the escape island is
282 // technically our original function.
283 BranchIsland *reentryIsland = NULL;
284 if( !err && originalFunctionReentryIsland ) {
285 err = allocateBranchIsland( &reentryIsland, kAllocateHigh, escapeIsland);
286 if( !err )
287 *originalFunctionReentryIsland = reentryIsland;
290 #if defined(__ppc__) || defined(__POWERPC__)
291 // Atomically:
292 // o If the reentry island was allocated:
293 // o Insert the original instruction into the reentry island.
294 // o Target the reentry island at the 2nd instruction of the
295 // original function.
296 // o Replace the original instruction with the branch absolute.
297 if( !err ) {
298 int escapeIslandEngaged = false;
299 do {
300 if( reentryIsland )
301 err = setBranchIslandTarget( reentryIsland,
302 (void*) (originalFunctionPtr+1), originalInstruction );
303 if( !err ) {
304 escapeIslandEngaged = CompareAndSwap( originalInstruction,
305 branchAbsoluteInstruction,
306 (UInt32*)originalFunctionPtr );
307 if( !escapeIslandEngaged ) {
308 // Someone replaced the instruction out from under us,
309 // re-read the instruction, make sure it's still not
310 // 'mfctr' and try again.
311 originalInstruction = *originalFunctionPtr;
312 if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction)
313 err = err_cannot_override;
316 } while( !err && !escapeIslandEngaged );
318 #elif defined(__i386__) || defined(__x86_64__)
319 // Atomically:
320 // o If the reentry island was allocated:
321 // o Insert the original instructions into the reentry island.
322 // o Target the reentry island at the first non-replaced
323 // instruction of the original function.
324 // o Replace the original first instructions with the jump relative.
326 // Note that on i386, we do not support someone else changing the code under our feet
327 if ( !err ) {
328 fixupInstructions(originalFunctionPtr, reentryIsland, originalInstructions,
329 originalInstructionCount, originalInstructionSizes );
331 if( reentryIsland )
332 err = setBranchIslandTarget_i386( reentryIsland,
333 (void*) ((char *)originalFunctionPtr+eatenCount), originalInstructions );
334 // try making islands executable before planting the jmp
335 #if defined(__x86_64__) || defined(__i386__)
336 if( !err )
337 err = makeIslandExecutable(escapeIsland);
338 if( !err && reentryIsland )
339 err = makeIslandExecutable(reentryIsland);
340 #endif
341 if ( !err )
342 atomic_mov64((uint64_t *)originalFunctionPtr, jumpRelativeInstruction);
343 mach_error_t prot_err = err_none;
344 prot_err = vm_protect( mach_task_self(),
345 (vm_address_t) originalFunctionPtr, 8, false,
346 (VM_PROT_READ | VM_PROT_EXECUTE) );
347 if(prot_err) fprintf(stderr, "err = %x %s:%d\n", prot_err, __FILE__, __LINE__);
349 #endif
351 // Clean up on error.
352 if( err ) {
353 if( reentryIsland )
354 freeBranchIsland( reentryIsland );
355 if( escapeIsland )
356 freeBranchIsland( escapeIsland );
359 return err;
362 /*******************************************************************************
364 * Implementation
366 *******************************************************************************/
367 #pragma mark -
368 #pragma mark (Implementation)
370 /*******************************************************************************
371 Implementation: Allocates memory for a branch island.
373 @param island <- The allocated island.
374 @param allocateHigh -> Whether to allocate the island at the end of the
375 address space (for use with the branch absolute
376 instruction).
377 @result <- mach_error_t
379 ***************************************************************************/
381 mach_error_t
382 allocateBranchIsland(
383 BranchIsland **island,
384 int allocateHigh,
385 void *originalFunctionAddress)
387 assert( island );
389 mach_error_t err = err_none;
391 if( allocateHigh ) {
392 assert( sizeof( BranchIsland ) <= PAGE_SIZE );
393 vm_address_t page = 0;
394 #if defined(__i386__)
395 err = vm_allocate( mach_task_self(), &page, PAGE_SIZE, VM_FLAGS_ANYWHERE );
396 if( err == err_none )
397 *island = (BranchIsland*) page;
398 #else
400 #if defined(__ppc__) || defined(__POWERPC__)
401 vm_address_t first = 0xfeffffff;
402 vm_address_t last = 0xfe000000 + PAGE_SIZE;
403 #elif defined(__x86_64__)
404 // 64-bit ASLR is in bits 13-28
405 vm_address_t first = ((uint64_t)originalFunctionAddress & ~( (0xFUL << 28) | (PAGE_SIZE - 1) ) ) | (0x1UL << 31);
406 vm_address_t last = (uint64_t)originalFunctionAddress & ~((0x1UL << 32) - 1);
407 #endif
409 page = first;
410 int allocated = 0;
411 vm_map_t task_self = mach_task_self();
413 while( !err && !allocated && page != last ) {
415 err = vm_allocate( task_self, &page, PAGE_SIZE, 0 );
416 if( err == err_none )
417 allocated = 1;
418 else if( err == KERN_NO_SPACE ) {
419 #if defined(__x86_64__)
420 page -= PAGE_SIZE;
421 #else
422 page += PAGE_SIZE;
423 #endif
424 err = err_none;
427 if( allocated )
428 *island = (BranchIsland*) page;
429 else if( !allocated && !err )
430 err = KERN_NO_SPACE;
431 #endif
432 } else {
433 void *block = malloc( sizeof( BranchIsland ) );
434 if( block )
435 *island = block;
436 else
437 err = KERN_NO_SPACE;
439 if( !err )
440 (**island).allocatedHigh = allocateHigh;
442 return err;
445 /*******************************************************************************
446 Implementation: Deallocates memory for a branch island.
448 @param island -> The island to deallocate.
449 @result <- mach_error_t
451 ***************************************************************************/
453 mach_error_t
454 freeBranchIsland(
455 BranchIsland *island )
457 assert( island );
458 assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] );
459 assert( island->allocatedHigh );
461 mach_error_t err = err_none;
463 if( island->allocatedHigh ) {
464 assert( sizeof( BranchIsland ) <= PAGE_SIZE );
465 err = vm_deallocate(mach_task_self(), (vm_address_t) island, PAGE_SIZE );
466 } else {
467 free( island );
470 return err;
473 /*******************************************************************************
474 Implementation: Sets the branch island's target, with an optional
475 instruction.
477 @param island -> The branch island to insert target into.
478 @param branchTo -> The address of the target.
479 @param instruction -> Optional instruction to execute prior to branch. Set
480 to zero for nop.
481 @result <- mach_error_t
483 ***************************************************************************/
484 #if defined(__ppc__) || defined(__POWERPC__)
485 mach_error_t
486 setBranchIslandTarget(
487 BranchIsland *island,
488 const void *branchTo,
489 long instruction )
491 // Copy over the template code.
492 bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
494 // Fill in the address.
495 ((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF;
496 ((short*)island->instructions)[kAddressHi]
497 = (((long) branchTo) >> 16) & 0x0000FFFF;
499 // Fill in the (optional) instuction.
500 if( instruction != 0 ) {
501 ((short*)island->instructions)[kInstructionLo]
502 = instruction & 0x0000FFFF;
503 ((short*)island->instructions)[kInstructionHi]
504 = (instruction >> 16) & 0x0000FFFF;
507 //MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) );
508 msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
510 return err_none;
512 #endif
514 #if defined(__i386__)
515 mach_error_t
516 setBranchIslandTarget_i386(
517 BranchIsland *island,
518 const void *branchTo,
519 char* instructions )
522 // Copy over the template code.
523 bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
525 // copy original instructions
526 if (instructions) {
527 bcopy (instructions, island->instructions + kInstructions, kOriginalInstructionsSize);
530 // Fill in the address.
531 int32_t addressOffset = (char *)branchTo - (island->instructions + kJumpAddress + 4);
532 *((int32_t *)(island->instructions + kJumpAddress)) = addressOffset;
534 msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
535 return err_none;
538 #elif defined(__x86_64__)
539 mach_error_t
540 setBranchIslandTarget_i386(
541 BranchIsland *island,
542 const void *branchTo,
543 char* instructions )
545 // Copy over the template code.
546 bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
548 // Copy original instructions.
549 if (instructions) {
550 bcopy (instructions, island->instructions, kOriginalInstructionsSize);
553 // Fill in the address.
554 *((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo;
555 msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
557 return err_none;
559 #endif
562 #if defined(__i386__) || defined(__x86_64__)
563 static Boolean
564 eatKnownInstructions(
565 unsigned char *code,
566 uint64_t *newInstruction,
567 int *howManyEaten,
568 char *originalInstructions,
569 int *originalInstructionCount,
570 uint8_t *originalInstructionSizes )
572 Boolean allInstructionsKnown = true;
573 int totalEaten = 0;
574 int remainsToEat = 5; // a JMP instruction takes 5 bytes
575 int instructionIndex = 0;
576 ud_t ud_obj;
578 if (howManyEaten) *howManyEaten = 0;
579 if (originalInstructionCount) *originalInstructionCount = 0;
580 ud_init(&ud_obj);
581 #if defined(__i386__)
582 ud_set_mode(&ud_obj, 32);
583 #else
584 ud_set_mode(&ud_obj, 64);
585 #endif
586 ud_set_input_buffer(&ud_obj, code, 64); // Assume that 'code' points to at least 64bytes of data.
587 while (remainsToEat > 0) {
588 if (!ud_disassemble(&ud_obj)) {
589 allInstructionsKnown = false;
590 fprintf(stderr, "mach_override: some instructions unknown! Need to update libudis86\n");
591 break;
594 // At this point, we've matched curInstr
595 int eaten = ud_insn_len(&ud_obj);
596 remainsToEat -= eaten;
597 totalEaten += eaten;
599 if (originalInstructionSizes) originalInstructionSizes[instructionIndex] = eaten;
600 instructionIndex += 1;
601 if (originalInstructionCount) *originalInstructionCount = instructionIndex;
605 if (howManyEaten) *howManyEaten = totalEaten;
607 if (originalInstructions) {
608 Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize);
610 if (enoughSpaceForOriginalInstructions) {
611 memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP
612 bcopy(code, originalInstructions, totalEaten);
613 } else {
614 // printf ("Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n");
615 return false;
619 if (allInstructionsKnown) {
620 // save last 3 bytes of first 64bits of codre we'll replace
621 uint64_t currentFirst64BitsOfCode = *((uint64_t *)code);
622 currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation
623 currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL;
625 // keep only last 3 instructions bytes, first 5 will be replaced by JMP instr
626 *newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes
627 *newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes
630 return allInstructionsKnown;
633 static void
634 fixupInstructions(
635 void *originalFunction,
636 void *escapeIsland,
637 void *instructionsToFix,
638 int instructionCount,
639 uint8_t *instructionSizes )
641 int index;
642 for (index = 0;index < instructionCount;index += 1)
644 if (*(uint8_t*)instructionsToFix == 0xE9) // 32-bit jump relative
646 uint32_t offset = (uintptr_t)originalFunction - (uintptr_t)escapeIsland;
647 uint32_t *jumpOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 1);
648 *jumpOffsetPtr += offset;
651 originalFunction = (void*)((uintptr_t)originalFunction + instructionSizes[index]);
652 escapeIsland = (void*)((uintptr_t)escapeIsland + instructionSizes[index]);
653 instructionsToFix = (void*)((uintptr_t)instructionsToFix + instructionSizes[index]);
657 #if defined(__i386__)
658 __asm(
659 ".text;"
660 ".align 2, 0x90;"
661 "_atomic_mov64:;"
662 " pushl %ebp;"
663 " movl %esp, %ebp;"
664 " pushl %esi;"
665 " pushl %ebx;"
666 " pushl %ecx;"
667 " pushl %eax;"
668 " pushl %edx;"
670 // atomic push of value to an address
671 // we use cmpxchg8b, which compares content of an address with
672 // edx:eax. If they are equal, it atomically puts 64bit value
673 // ecx:ebx in address.
674 // We thus put contents of address in edx:eax to force ecx:ebx
675 // in address
676 " mov 8(%ebp), %esi;" // esi contains target address
677 " mov 12(%ebp), %ebx;"
678 " mov 16(%ebp), %ecx;" // ecx:ebx now contains value to put in target address
679 " mov (%esi), %eax;"
680 " mov 4(%esi), %edx;" // edx:eax now contains value currently contained in target address
681 " lock; cmpxchg8b (%esi);" // atomic move.
683 // restore registers
684 " popl %edx;"
685 " popl %eax;"
686 " popl %ecx;"
687 " popl %ebx;"
688 " popl %esi;"
689 " popl %ebp;"
690 " ret"
692 #elif defined(__x86_64__)
693 void atomic_mov64(
694 uint64_t *targetAddress,
695 uint64_t value )
697 *targetAddress = value;
699 #endif
700 #endif