Define the DISABLE_CAPS_LOCK and TOGGLE_CAPS_LOCK accelerators for OS_CHROMEOS only.
[chromium-blink-merge.git] / third_party / mach_override / mach_override.c
blob46d21528c138cf88a6889761c1d8db712f15588d
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_host.h>
13 #include <mach/mach_init.h>
14 #include <mach/vm_map.h>
15 #include <mach/vm_statistics.h>
16 #include <sys/mman.h>
18 #include <CoreServices/CoreServices.h>
20 /**************************
22 * Constants
24 **************************/
25 #pragma mark -
26 #pragma mark (Constants)
28 #if defined(__ppc__) || defined(__POWERPC__)
30 long kIslandTemplate[] = {
31 0x9001FFFC, // stw r0,-4(SP)
32 0x3C00DEAD, // lis r0,0xDEAD
33 0x6000BEEF, // ori r0,r0,0xBEEF
34 0x7C0903A6, // mtctr r0
35 0x8001FFFC, // lwz r0,-4(SP)
36 0x60000000, // nop ; optionally replaced
37 0x4E800420 // bctr
40 #define kAddressHi 3
41 #define kAddressLo 5
42 #define kInstructionHi 10
43 #define kInstructionLo 11
45 #elif defined(__i386__)
47 #define kOriginalInstructionsSize 16
49 char kIslandTemplate[] = {
50 // kOriginalInstructionsSize nop instructions so that we
51 // should have enough space to host original instructions
52 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
53 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
54 // Now the real jump instruction
55 0xE9, 0xEF, 0xBE, 0xAD, 0xDE
58 #define kInstructions 0
59 #define kJumpAddress kInstructions + kOriginalInstructionsSize + 1
60 #elif defined(__x86_64__)
62 #define kOriginalInstructionsSize 32
64 #define kJumpAddress kOriginalInstructionsSize + 6
66 char kIslandTemplate[] = {
67 // kOriginalInstructionsSize nop instructions so that we
68 // should have enough space to host original instructions
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 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
73 // Now the real jump instruction
74 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00,
75 0x00, 0x00, 0x00, 0x00,
76 0x00, 0x00, 0x00, 0x00
79 #endif
81 #define kAllocateHigh 1
82 #define kAllocateNormal 0
84 /**************************
86 * Data Types
88 **************************/
89 #pragma mark -
90 #pragma mark (Data Types)
92 typedef struct {
93 char instructions[sizeof(kIslandTemplate)];
94 int allocatedHigh;
95 } BranchIsland;
97 /**************************
99 * Funky Protos
101 **************************/
102 #pragma mark -
103 #pragma mark (Funky Protos)
105 mach_error_t
106 allocateBranchIsland(
107 BranchIsland **island,
108 int allocateHigh,
109 void *originalFunctionAddress);
111 mach_error_t
112 freeBranchIsland(
113 BranchIsland *island );
115 #if defined(__ppc__) || defined(__POWERPC__)
116 mach_error_t
117 setBranchIslandTarget(
118 BranchIsland *island,
119 const void *branchTo,
120 long instruction );
121 #endif
123 #if defined(__i386__) || defined(__x86_64__)
124 mach_error_t
125 setBranchIslandTarget_i386(
126 BranchIsland *island,
127 const void *branchTo,
128 char* instructions );
129 void
130 atomic_mov64(
131 uint64_t *targetAddress,
132 uint64_t value );
134 static Boolean
135 eatKnownInstructions(
136 unsigned char *code,
137 uint64_t *newInstruction,
138 int *howManyEaten,
139 char *originalInstructions,
140 int *originalInstructionCount,
141 uint8_t *originalInstructionSizes );
143 static void
144 fixupInstructions(
145 void *originalFunction,
146 void *escapeIsland,
147 void *instructionsToFix,
148 int instructionCount,
149 uint8_t *instructionSizes );
150 #endif
152 /*******************************************************************************
154 * Interface
156 *******************************************************************************/
157 #pragma mark -
158 #pragma mark (Interface)
160 #if defined(__i386__) || defined(__x86_64__)
161 mach_error_t makeIslandExecutable(void *address) {
162 mach_error_t err = err_none;
163 vm_size_t pageSize;
164 host_page_size( mach_host_self(), &pageSize );
165 uintptr_t page = (uintptr_t)address & ~(uintptr_t)(pageSize-1);
166 int e = err_none;
167 e |= mprotect((void *)page, pageSize, PROT_EXEC | PROT_READ);
168 e |= msync((void *)page, pageSize, MS_INVALIDATE );
169 if (e) {
170 err = err_cannot_override;
172 return err;
174 #endif
176 mach_error_t
177 mach_override_ptr(
178 void *originalFunctionAddress,
179 const void *overrideFunctionAddress,
180 void **originalFunctionReentryIsland )
182 assert( originalFunctionAddress );
183 assert( overrideFunctionAddress );
185 // this addresses overriding such functions as AudioOutputUnitStart()
186 // test with modified DefaultOutputUnit project
187 #if defined(__x86_64__)
188 for(;;){
189 if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp qword near [rip+0x????????]
190 originalFunctionAddress=*(void**)((char*)originalFunctionAddress+6+*(int32_t *)((uint16_t*)originalFunctionAddress+1));
191 else break;
193 #elif defined(__i386__)
194 for(;;){
195 if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp *0x????????
196 originalFunctionAddress=**(void***)((uint16_t*)originalFunctionAddress+1);
197 else break;
199 #endif
201 long *originalFunctionPtr = (long*) originalFunctionAddress;
202 mach_error_t err = err_none;
204 #if defined(__ppc__) || defined(__POWERPC__)
205 // Ensure first instruction isn't 'mfctr'.
206 #define kMFCTRMask 0xfc1fffff
207 #define kMFCTRInstruction 0x7c0903a6
209 long originalInstruction = *originalFunctionPtr;
210 if( !err && ((originalInstruction & kMFCTRMask) == kMFCTRInstruction) )
211 err = err_cannot_override;
212 #elif defined(__i386__) || defined(__x86_64__)
213 int eatenCount = 0;
214 int originalInstructionCount = 0;
215 char originalInstructions[kOriginalInstructionsSize];
216 uint8_t originalInstructionSizes[kOriginalInstructionsSize];
217 uint64_t jumpRelativeInstruction = 0; // JMP
219 Boolean overridePossible = eatKnownInstructions ((unsigned char *)originalFunctionPtr,
220 &jumpRelativeInstruction, &eatenCount,
221 originalInstructions, &originalInstructionCount,
222 originalInstructionSizes );
223 if (eatenCount > kOriginalInstructionsSize) {
224 //printf ("Too many instructions eaten\n");
225 overridePossible = false;
227 if (!overridePossible) err = err_cannot_override;
228 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
229 #endif
231 // Make the original function implementation writable.
232 if( !err ) {
233 err = vm_protect( mach_task_self(),
234 (vm_address_t) originalFunctionPtr, 8, false,
235 (VM_PROT_ALL | VM_PROT_COPY) );
236 if( err )
237 err = vm_protect( mach_task_self(),
238 (vm_address_t) originalFunctionPtr, 8, false,
239 (VM_PROT_DEFAULT | VM_PROT_COPY) );
241 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
243 // Allocate and target the escape island to the overriding function.
244 BranchIsland *escapeIsland = NULL;
245 if( !err )
246 err = allocateBranchIsland( &escapeIsland, kAllocateHigh, originalFunctionAddress );
247 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
250 #if defined(__ppc__) || defined(__POWERPC__)
251 if( !err )
252 err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 );
254 // Build the branch absolute instruction to the escape island.
255 long branchAbsoluteInstruction = 0; // Set to 0 just to silence warning.
256 if( !err ) {
257 long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF;
258 branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress;
260 #elif defined(__i386__) || defined(__x86_64__)
261 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
263 if( !err )
264 err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 );
266 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
267 // Build the jump relative instruction to the escape island
268 #endif
271 #if defined(__i386__) || defined(__x86_64__)
272 if (!err) {
273 uint32_t addressOffset = ((char*)escapeIsland - (char*)originalFunctionPtr - 5);
274 addressOffset = OSSwapInt32(addressOffset);
276 jumpRelativeInstruction |= 0xE900000000000000LL;
277 jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24;
278 jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction);
280 #endif
282 // Optionally allocate & return the reentry island. This may contain relocated
283 // jmp instructions and so has all the same addressing reachability requirements
284 // the escape island has to the original function, except the escape island is
285 // technically our original function.
286 BranchIsland *reentryIsland = NULL;
287 if( !err && originalFunctionReentryIsland ) {
288 err = allocateBranchIsland( &reentryIsland, kAllocateHigh, escapeIsland);
289 if( !err )
290 *originalFunctionReentryIsland = reentryIsland;
293 #if defined(__ppc__) || defined(__POWERPC__)
294 // Atomically:
295 // o If the reentry island was allocated:
296 // o Insert the original instruction into the reentry island.
297 // o Target the reentry island at the 2nd instruction of the
298 // original function.
299 // o Replace the original instruction with the branch absolute.
300 if( !err ) {
301 int escapeIslandEngaged = false;
302 do {
303 if( reentryIsland )
304 err = setBranchIslandTarget( reentryIsland,
305 (void*) (originalFunctionPtr+1), originalInstruction );
306 if( !err ) {
307 escapeIslandEngaged = CompareAndSwap( originalInstruction,
308 branchAbsoluteInstruction,
309 (UInt32*)originalFunctionPtr );
310 if( !escapeIslandEngaged ) {
311 // Someone replaced the instruction out from under us,
312 // re-read the instruction, make sure it's still not
313 // 'mfctr' and try again.
314 originalInstruction = *originalFunctionPtr;
315 if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction)
316 err = err_cannot_override;
319 } while( !err && !escapeIslandEngaged );
321 #elif defined(__i386__) || defined(__x86_64__)
322 // Atomically:
323 // o If the reentry island was allocated:
324 // o Insert the original instructions into the reentry island.
325 // o Target the reentry island at the first non-replaced
326 // instruction of the original function.
327 // o Replace the original first instructions with the jump relative.
329 // Note that on i386, we do not support someone else changing the code under our feet
330 if ( !err ) {
331 fixupInstructions(originalFunctionPtr, reentryIsland, originalInstructions,
332 originalInstructionCount, originalInstructionSizes );
334 if( reentryIsland )
335 err = setBranchIslandTarget_i386( reentryIsland,
336 (void*) ((char *)originalFunctionPtr+eatenCount), originalInstructions );
337 // try making islands executable before planting the jmp
338 #if defined(__x86_64__) || defined(__i386__)
339 if( !err )
340 err = makeIslandExecutable(escapeIsland);
341 if( !err && reentryIsland )
342 err = makeIslandExecutable(reentryIsland);
343 #endif
344 if ( !err )
345 atomic_mov64((uint64_t *)originalFunctionPtr, jumpRelativeInstruction);
347 mach_error_t prot_err = err_none;
348 prot_err = vm_protect( mach_task_self(),
349 (vm_address_t) originalFunctionPtr, 8, false,
350 (VM_PROT_READ | VM_PROT_EXECUTE) );
351 if (prot_err) fprintf(stderr, "err = %x %s:%d\n", prot_err, __FILE__, __LINE__);
353 #endif
355 // Clean up on error.
356 if( err ) {
357 if( reentryIsland )
358 freeBranchIsland( reentryIsland );
359 if( escapeIsland )
360 freeBranchIsland( escapeIsland );
363 return err;
366 /*******************************************************************************
368 * Implementation
370 *******************************************************************************/
371 #pragma mark -
372 #pragma mark (Implementation)
374 /*******************************************************************************
375 Implementation: Allocates memory for a branch island.
377 @param island <- The allocated island.
378 @param allocateHigh -> Whether to allocate the island at the end of the
379 address space (for use with the branch absolute
380 instruction).
381 @result <- mach_error_t
383 ***************************************************************************/
385 mach_error_t
386 allocateBranchIsland(
387 BranchIsland **island,
388 int allocateHigh,
389 void *originalFunctionAddress)
391 assert( island );
393 mach_error_t err = err_none;
395 if( allocateHigh ) {
396 vm_size_t pageSize;
397 err = host_page_size( mach_host_self(), &pageSize );
398 if( !err ) {
399 assert( sizeof( BranchIsland ) <= pageSize );
400 #if defined(__i386__)
401 vm_address_t page = 0;
402 mach_error_t err = vm_allocate( mach_task_self(), &page, pageSize, VM_FLAGS_ANYWHERE );
403 if( err == err_none ) {
404 *island = (BranchIsland*) page;
405 return err_none;
407 return err;
408 #else
410 #if defined(__ppc__) || defined(__POWERPC__)
411 vm_address_t first = 0xfeffffff;
412 vm_address_t last = 0xfe000000 + pageSize;
413 #elif defined(__x86_64__)
414 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?
415 vm_address_t last = 0x0;
416 #endif
418 vm_address_t page = first;
419 int allocated = 0;
420 vm_map_t task_self = mach_task_self();
422 while( !err && !allocated && page != last ) {
424 err = vm_allocate( task_self, &page, pageSize, 0 );
425 if( err == err_none )
426 allocated = 1;
427 else if( err == KERN_NO_SPACE ) {
428 #if defined(__x86_64__)
429 page -= pageSize;
430 #else
431 page += pageSize;
432 #endif
433 err = err_none;
436 if( allocated )
437 *island = (BranchIsland*) page;
438 else if( !allocated && !err )
439 err = KERN_NO_SPACE;
440 #endif
442 } else {
443 void *block = malloc( sizeof( BranchIsland ) );
444 if( block )
445 *island = block;
446 else
447 err = KERN_NO_SPACE;
449 if( !err )
450 (**island).allocatedHigh = allocateHigh;
452 return err;
455 /*******************************************************************************
456 Implementation: Deallocates memory for a branch island.
458 @param island -> The island to deallocate.
459 @result <- mach_error_t
461 ***************************************************************************/
463 mach_error_t
464 freeBranchIsland(
465 BranchIsland *island )
467 assert( island );
468 assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] );
469 assert( island->allocatedHigh );
471 mach_error_t err = err_none;
473 if( island->allocatedHigh ) {
474 vm_size_t pageSize;
475 err = host_page_size( mach_host_self(), &pageSize );
476 if( !err ) {
477 assert( sizeof( BranchIsland ) <= pageSize );
478 err = vm_deallocate(
479 mach_task_self(),
480 (vm_address_t) island, pageSize );
482 } else {
483 free( island );
486 return err;
489 /*******************************************************************************
490 Implementation: Sets the branch island's target, with an optional
491 instruction.
493 @param island -> The branch island to insert target into.
494 @param branchTo -> The address of the target.
495 @param instruction -> Optional instruction to execute prior to branch. Set
496 to zero for nop.
497 @result <- mach_error_t
499 ***************************************************************************/
500 #if defined(__ppc__) || defined(__POWERPC__)
501 mach_error_t
502 setBranchIslandTarget(
503 BranchIsland *island,
504 const void *branchTo,
505 long instruction )
507 // Copy over the template code.
508 bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
510 // Fill in the address.
511 ((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF;
512 ((short*)island->instructions)[kAddressHi]
513 = (((long) branchTo) >> 16) & 0x0000FFFF;
515 // Fill in the (optional) instuction.
516 if( instruction != 0 ) {
517 ((short*)island->instructions)[kInstructionLo]
518 = instruction & 0x0000FFFF;
519 ((short*)island->instructions)[kInstructionHi]
520 = (instruction >> 16) & 0x0000FFFF;
523 //MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) );
524 msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
526 return err_none;
528 #endif
530 #if defined(__i386__)
531 mach_error_t
532 setBranchIslandTarget_i386(
533 BranchIsland *island,
534 const void *branchTo,
535 char* instructions )
538 // Copy over the template code.
539 bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
541 // copy original instructions
542 if (instructions) {
543 bcopy (instructions, island->instructions + kInstructions, kOriginalInstructionsSize);
546 // Fill in the address.
547 int32_t addressOffset = (char *)branchTo - (island->instructions + kJumpAddress + 4);
548 *((int32_t *)(island->instructions + kJumpAddress)) = addressOffset;
550 msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
551 return err_none;
554 #elif defined(__x86_64__)
555 mach_error_t
556 setBranchIslandTarget_i386(
557 BranchIsland *island,
558 const void *branchTo,
559 char* instructions )
561 // Copy over the template code.
562 bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
564 // Copy original instructions.
565 if (instructions) {
566 bcopy (instructions, island->instructions, kOriginalInstructionsSize);
569 // Fill in the address.
570 *((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo;
571 msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
573 return err_none;
575 #endif
578 #if defined(__i386__) || defined(__x86_64__)
579 static Boolean
580 eatKnownInstructions(
581 unsigned char *code,
582 uint64_t *newInstruction,
583 int *howManyEaten,
584 char *originalInstructions,
585 int *originalInstructionCount,
586 uint8_t *originalInstructionSizes )
588 Boolean allInstructionsKnown = true;
589 int totalEaten = 0;
590 int remainsToEat = 5; // a JMP instruction takes 5 bytes
591 int instructionIndex = 0;
592 ud_t ud_obj;
594 if (howManyEaten) *howManyEaten = 0;
595 if (originalInstructionCount) *originalInstructionCount = 0;
596 ud_init(&ud_obj);
597 #if defined(__i386__)
598 ud_set_mode(&ud_obj, 32);
599 #else
600 ud_set_mode(&ud_obj, 64);
601 #endif
602 ud_set_input_buffer(&ud_obj, code, 64); // Assume that 'code' points to at least 64bytes of data.
603 while (remainsToEat > 0) {
604 if (!ud_disassemble(&ud_obj)) {
605 allInstructionsKnown = false;
606 fprintf(stderr, "mach_override: some instructions unknown! Need to update libudis86\n");
607 break;
610 // At this point, we've matched curInstr
611 int eaten = ud_insn_len(&ud_obj);
612 remainsToEat -= eaten;
613 totalEaten += eaten;
615 if (originalInstructionSizes) originalInstructionSizes[instructionIndex] = eaten;
616 instructionIndex += 1;
617 if (originalInstructionCount) *originalInstructionCount = instructionIndex;
621 if (howManyEaten) *howManyEaten = totalEaten;
623 if (originalInstructions) {
624 Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize);
626 if (enoughSpaceForOriginalInstructions) {
627 memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP
628 bcopy(code, originalInstructions, totalEaten);
629 } else {
630 // printf ("Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n");
631 return false;
635 if (allInstructionsKnown) {
636 // save last 3 bytes of first 64bits of codre we'll replace
637 uint64_t currentFirst64BitsOfCode = *((uint64_t *)code);
638 currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation
639 currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL;
641 // keep only last 3 instructions bytes, first 5 will be replaced by JMP instr
642 *newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes
643 *newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes
646 return allInstructionsKnown;
649 static void
650 fixupInstructions(
651 void *originalFunction,
652 void *escapeIsland,
653 void *instructionsToFix,
654 int instructionCount,
655 uint8_t *instructionSizes )
657 int index;
658 for (index = 0;index < instructionCount;index += 1)
660 if (*(uint8_t*)instructionsToFix == 0xE9) // 32-bit jump relative
662 uint32_t offset = (uintptr_t)originalFunction - (uintptr_t)escapeIsland;
663 uint32_t *jumpOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 1);
664 *jumpOffsetPtr += offset;
667 originalFunction = (void*)((uintptr_t)originalFunction + instructionSizes[index]);
668 escapeIsland = (void*)((uintptr_t)escapeIsland + instructionSizes[index]);
669 instructionsToFix = (void*)((uintptr_t)instructionsToFix + instructionSizes[index]);
673 #if defined(__i386__)
674 __asm(
675 ".text;"
676 ".align 2, 0x90;"
677 "_atomic_mov64:;"
678 " pushl %ebp;"
679 " movl %esp, %ebp;"
680 " pushl %esi;"
681 " pushl %ebx;"
682 " pushl %ecx;"
683 " pushl %eax;"
684 " pushl %edx;"
686 // atomic push of value to an address
687 // we use cmpxchg8b, which compares content of an address with
688 // edx:eax. If they are equal, it atomically puts 64bit value
689 // ecx:ebx in address.
690 // We thus put contents of address in edx:eax to force ecx:ebx
691 // in address
692 " mov 8(%ebp), %esi;" // esi contains target address
693 " mov 12(%ebp), %ebx;"
694 " mov 16(%ebp), %ecx;" // ecx:ebx now contains value to put in target address
695 " mov (%esi), %eax;"
696 " mov 4(%esi), %edx;" // edx:eax now contains value currently contained in target address
697 " lock; cmpxchg8b (%esi);" // atomic move.
699 // restore registers
700 " popl %edx;"
701 " popl %eax;"
702 " popl %ecx;"
703 " popl %ebx;"
704 " popl %esi;"
705 " popl %ebp;"
706 " ret"
708 #elif defined(__x86_64__)
709 void atomic_mov64(
710 uint64_t *targetAddress,
711 uint64_t value )
713 *targetAddress = value;
715 #endif
716 #endif