Bug 1861985 - Let the parent process handle validating command encoders. r=webgpu...
[gecko.git] / xpcom / build / mach_override.c
blob1ece045e2c31366fe19052b0a96e670fcedc2b93
1 // Copied from upstream at revision 195c13743fe0ebc658714e2a9567d86529f20443.
2 // mach_override.c semver:1.2.0
3 // Copyright (c) 2003-2012 Jonathan 'Wolf' Rentzsch: http://rentzsch.com
4 // Some rights reserved: http://opensource.org/licenses/mit
5 // https://github.com/rentzsch/mach_override
7 #include "mach_override.h"
9 #include <mach-o/dyld.h>
10 #include <mach/mach_host.h>
11 #include <mach/mach_init.h>
12 #include <mach/vm_map.h>
13 #include <sys/mman.h>
15 #include <CoreServices/CoreServices.h>
17 /**************************
19 * Constants
21 **************************/
22 #pragma mark -
23 #pragma mark (Constants)
25 #define kPageSize 4096
26 #if defined(__ppc__) || defined(__POWERPC__)
28 long kIslandTemplate[] = {
29 0x9001FFFC, // stw r0,-4(SP)
30 0x3C00DEAD, // lis r0,0xDEAD
31 0x6000BEEF, // ori r0,r0,0xBEEF
32 0x7C0903A6, // mtctr r0
33 0x8001FFFC, // lwz r0,-4(SP)
34 0x60000000, // nop ; optionally replaced
35 0x4E800420 // bctr
38 #define kAddressHi 3
39 #define kAddressLo 5
40 #define kInstructionHi 10
41 #define kInstructionLo 11
43 #elif defined(__i386__)
45 #define kOriginalInstructionsSize 16
46 // On X86 we migh need to instert an add with a 32 bit immediate after the
47 // original instructions.
48 #define kMaxFixupSizeIncrease 5
50 unsigned char kIslandTemplate[] = {
51 // kOriginalInstructionsSize nop instructions so that we
52 // should have enough space to host original instructions
53 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
54 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
55 // Now the real jump instruction
56 0xE9, 0xEF, 0xBE, 0xAD, 0xDE
59 #define kInstructions 0
60 #define kJumpAddress kInstructions + kOriginalInstructionsSize + 1
61 #elif defined(__x86_64__)
63 #define kOriginalInstructionsSize 32
64 // On X86-64 we never need to instert a new instruction.
65 #define kMaxFixupSizeIncrease 0
67 #define kJumpAddress kOriginalInstructionsSize + 6
69 unsigned char kIslandTemplate[] = {
70 // kOriginalInstructionsSize nop instructions so that we
71 // should have enough space to host original instructions
72 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
73 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
74 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
75 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
76 // Now the real jump instruction
77 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00,
78 0x00, 0x00, 0x00, 0x00,
79 0x00, 0x00, 0x00, 0x00
82 #endif
84 /**************************
86 * Data Types
88 **************************/
89 #pragma mark -
90 #pragma mark (Data Types)
92 typedef struct {
93 char instructions[sizeof(kIslandTemplate)];
94 } BranchIsland;
96 /**************************
98 * Funky Protos
100 **************************/
101 #pragma mark -
102 #pragma mark (Funky Protos)
104 static mach_error_t
105 allocateBranchIsland(
106 BranchIsland **island,
107 void *originalFunctionAddress);
109 mach_error_t
110 freeBranchIsland(
111 BranchIsland *island );
113 #if defined(__ppc__) || defined(__POWERPC__)
114 mach_error_t
115 setBranchIslandTarget(
116 BranchIsland *island,
117 const void *branchTo,
118 long instruction );
119 #endif
121 #if defined(__i386__) || defined(__x86_64__)
122 mach_error_t
123 setBranchIslandTarget_i386(
124 BranchIsland *island,
125 const void *branchTo,
126 char* instructions );
127 void
128 atomic_mov64(
129 uint64_t *targetAddress,
130 uint64_t value );
132 static Boolean
133 eatKnownInstructions(
134 unsigned char *code,
135 uint64_t *newInstruction,
136 int *howManyEaten,
137 char *originalInstructions,
138 int *originalInstructionCount,
139 uint8_t *originalInstructionSizes );
141 static void
142 fixupInstructions(
143 uint32_t offset,
144 void *instructionsToFix,
145 int instructionCount,
146 uint8_t *instructionSizes );
147 #endif
149 /*******************************************************************************
151 * Interface
153 *******************************************************************************/
154 #pragma mark -
155 #pragma mark (Interface)
157 #if defined(__i386__) || defined(__x86_64__)
158 mach_error_t makeIslandExecutable(void *address) {
159 mach_error_t err = err_none;
160 uintptr_t page = (uintptr_t)address & ~(uintptr_t)(kPageSize-1);
161 int e = err_none;
162 e |= mprotect((void *)page, kPageSize, PROT_EXEC | PROT_READ | PROT_WRITE);
163 e |= msync((void *)page, kPageSize, MS_INVALIDATE );
164 if (e) {
165 err = err_cannot_override;
167 return err;
169 #endif
171 mach_error_t
172 mach_override_ptr(
173 void *originalFunctionAddress,
174 const void *overrideFunctionAddress,
175 void **originalFunctionReentryIsland )
177 assert( originalFunctionAddress );
178 assert( overrideFunctionAddress );
180 // this addresses overriding such functions as AudioOutputUnitStart()
181 // test with modified DefaultOutputUnit project
182 #if defined(__x86_64__)
183 for(;;){
184 if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp qword near [rip+0x????????]
185 originalFunctionAddress=*(void**)((char*)originalFunctionAddress+6+*(int32_t *)((uint16_t*)originalFunctionAddress+1));
186 else break;
188 #elif defined(__i386__)
189 for(;;){
190 if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp *0x????????
191 originalFunctionAddress=**(void***)((uint16_t*)originalFunctionAddress+1);
192 else break;
194 #endif
196 long *originalFunctionPtr = (long*) originalFunctionAddress;
197 mach_error_t err = err_none;
199 #if defined(__ppc__) || defined(__POWERPC__)
200 // Ensure first instruction isn't 'mfctr'.
201 #define kMFCTRMask 0xfc1fffff
202 #define kMFCTRInstruction 0x7c0903a6
204 long originalInstruction = *originalFunctionPtr;
205 if( !err && ((originalInstruction & kMFCTRMask) == kMFCTRInstruction) )
206 err = err_cannot_override;
207 #elif defined(__i386__) || defined(__x86_64__)
208 int eatenCount = 0;
209 int originalInstructionCount = 0;
210 char originalInstructions[kOriginalInstructionsSize];
211 uint8_t originalInstructionSizes[kOriginalInstructionsSize];
212 uint64_t jumpRelativeInstruction = 0; // JMP
214 Boolean overridePossible = eatKnownInstructions ((unsigned char *)originalFunctionPtr,
215 &jumpRelativeInstruction, &eatenCount,
216 originalInstructions, &originalInstructionCount,
217 originalInstructionSizes );
218 if (eatenCount + kMaxFixupSizeIncrease > kOriginalInstructionsSize) {
219 //printf ("Too many instructions eaten\n");
220 overridePossible = false;
222 if (!overridePossible) err = err_cannot_override;
223 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
224 #endif
226 // Make the original function implementation writable.
227 if( !err ) {
228 err = vm_protect( mach_task_self(),
229 (vm_address_t) originalFunctionPtr, 8, false,
230 (VM_PROT_ALL | VM_PROT_COPY) );
231 if( err )
232 err = vm_protect( mach_task_self(),
233 (vm_address_t) originalFunctionPtr, 8, false,
234 (VM_PROT_DEFAULT | VM_PROT_COPY) );
236 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
238 // Allocate and target the escape island to the overriding function.
239 BranchIsland *escapeIsland = NULL;
240 if( !err ) {
241 err = allocateBranchIsland( &escapeIsland, originalFunctionAddress );
242 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
245 #if defined(__ppc__) || defined(__POWERPC__)
246 if( !err )
247 err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 );
249 // Build the branch absolute instruction to the escape island.
250 long branchAbsoluteInstruction = 0; // Set to 0 just to silence warning.
251 if( !err ) {
252 long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF;
253 branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress;
255 #elif defined(__i386__) || defined(__x86_64__)
256 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
258 if( !err )
259 err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 );
261 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
262 // Build the jump relative instruction to the escape island
263 #endif
266 #if defined(__i386__) || defined(__x86_64__)
267 if (!err) {
268 uint32_t addressOffset = ((char*)escapeIsland - (char*)originalFunctionPtr - 5);
269 addressOffset = OSSwapInt32(addressOffset);
271 jumpRelativeInstruction |= 0xE900000000000000LL;
272 jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24;
273 jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction);
275 #endif
277 // Optionally allocate & return the reentry island. This may contain relocated
278 // jmp instructions and so has all the same addressing reachability requirements
279 // the escape island has to the original function, except the escape island is
280 // technically our original function.
281 BranchIsland *reentryIsland = NULL;
282 if( !err && originalFunctionReentryIsland ) {
283 err = allocateBranchIsland( &reentryIsland, escapeIsland);
284 if( !err )
285 *originalFunctionReentryIsland = reentryIsland;
288 #if defined(__ppc__) || defined(__POWERPC__)
289 // Atomically:
290 // o If the reentry island was allocated:
291 // o Insert the original instruction into the reentry island.
292 // o Target the reentry island at the 2nd instruction of the
293 // original function.
294 // o Replace the original instruction with the branch absolute.
295 if( !err ) {
296 int escapeIslandEngaged = false;
297 do {
298 if( reentryIsland )
299 err = setBranchIslandTarget( reentryIsland,
300 (void*) (originalFunctionPtr+1), originalInstruction );
301 if( !err ) {
302 escapeIslandEngaged = CompareAndSwap( originalInstruction,
303 branchAbsoluteInstruction,
304 (UInt32*)originalFunctionPtr );
305 if( !escapeIslandEngaged ) {
306 // Someone replaced the instruction out from under us,
307 // re-read the instruction, make sure it's still not
308 // 'mfctr' and try again.
309 originalInstruction = *originalFunctionPtr;
310 if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction)
311 err = err_cannot_override;
314 } while( !err && !escapeIslandEngaged );
316 #elif defined(__i386__) || defined(__x86_64__)
317 // Atomically:
318 // o If the reentry island was allocated:
319 // o Insert the original instructions into the reentry island.
320 // o Target the reentry island at the first non-replaced
321 // instruction of the original function.
322 // o Replace the original first instructions with the jump relative.
324 // Note that on i386, we do not support someone else changing the code under our feet
325 if ( !err ) {
326 uint32_t offset = (uintptr_t)originalFunctionPtr - (uintptr_t)reentryIsland;
327 fixupInstructions(offset, originalInstructions,
328 originalInstructionCount, originalInstructionSizes );
330 if( reentryIsland )
331 err = setBranchIslandTarget_i386( reentryIsland,
332 (void*) ((char *)originalFunctionPtr+eatenCount), originalInstructions );
333 // try making islands executable before planting the jmp
334 #if defined(__x86_64__) || defined(__i386__)
335 if( !err )
336 err = makeIslandExecutable(escapeIsland);
337 if( !err && reentryIsland )
338 err = makeIslandExecutable(reentryIsland);
339 #endif
340 if ( !err )
341 atomic_mov64((uint64_t *)originalFunctionPtr, jumpRelativeInstruction);
343 #endif
345 // Clean up on error.
346 if( err ) {
347 if( reentryIsland )
348 freeBranchIsland( reentryIsland );
349 if( escapeIsland )
350 freeBranchIsland( escapeIsland );
353 return err;
356 /*******************************************************************************
358 * Implementation
360 *******************************************************************************/
361 #pragma mark -
362 #pragma mark (Implementation)
364 static bool jump_in_range(intptr_t from, intptr_t to) {
365 intptr_t field_value = to - from - 5;
366 int32_t field_value_32 = field_value;
367 return field_value == field_value_32;
370 /*******************************************************************************
371 Implementation: Allocates memory for a branch island.
373 @param island <- The allocated island.
374 @result <- mach_error_t
376 ***************************************************************************/
378 static mach_error_t
379 allocateBranchIslandAux(
380 BranchIsland **island,
381 void *originalFunctionAddress,
382 bool forward)
384 assert( island );
385 assert( sizeof( BranchIsland ) <= kPageSize );
387 vm_map_t task_self = mach_task_self();
388 vm_address_t original_address = (vm_address_t) originalFunctionAddress;
389 vm_address_t address = original_address;
391 for (;;) {
392 vm_size_t vmsize = 0;
393 memory_object_name_t object = 0;
394 kern_return_t kr = 0;
395 vm_region_flavor_t flavor = VM_REGION_BASIC_INFO;
396 // Find the region the address is in.
397 #if __WORDSIZE == 32
398 vm_region_basic_info_data_t info;
399 mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT;
400 kr = vm_region(task_self, &address, &vmsize, flavor,
401 (vm_region_info_t)&info, &info_count, &object);
402 #else
403 vm_region_basic_info_data_64_t info;
404 mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
405 kr = vm_region_64(task_self, &address, &vmsize, flavor,
406 (vm_region_info_t)&info, &info_count, &object);
407 #endif
408 if (kr != KERN_SUCCESS)
409 return kr;
410 assert((address & (kPageSize - 1)) == 0);
412 // Go to the first page before or after this region
413 vm_address_t new_address = forward ? address + vmsize : address - kPageSize;
414 #if __WORDSIZE == 64
415 if(!jump_in_range(original_address, new_address))
416 break;
417 #endif
418 address = new_address;
420 // Try to allocate this page.
421 kr = vm_allocate(task_self, &address, kPageSize, 0);
422 if (kr == KERN_SUCCESS) {
423 *island = (BranchIsland*) address;
424 return err_none;
426 if (kr != KERN_NO_SPACE)
427 return kr;
430 return KERN_NO_SPACE;
433 static mach_error_t
434 allocateBranchIsland(
435 BranchIsland **island,
436 void *originalFunctionAddress)
438 mach_error_t err =
439 allocateBranchIslandAux(island, originalFunctionAddress, true);
440 if (!err)
441 return err;
442 return allocateBranchIslandAux(island, originalFunctionAddress, false);
446 /*******************************************************************************
447 Implementation: Deallocates memory for a branch island.
449 @param island -> The island to deallocate.
450 @result <- mach_error_t
452 ***************************************************************************/
454 mach_error_t
455 freeBranchIsland(
456 BranchIsland *island )
458 assert( island );
459 assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] );
460 assert( sizeof( BranchIsland ) <= kPageSize );
461 return vm_deallocate( mach_task_self(), (vm_address_t) island,
462 kPageSize );
465 /*******************************************************************************
466 Implementation: Sets the branch island's target, with an optional
467 instruction.
469 @param island -> The branch island to insert target into.
470 @param branchTo -> The address of the target.
471 @param instruction -> Optional instruction to execute prior to branch. Set
472 to zero for nop.
473 @result <- mach_error_t
475 ***************************************************************************/
476 #if defined(__ppc__) || defined(__POWERPC__)
477 mach_error_t
478 setBranchIslandTarget(
479 BranchIsland *island,
480 const void *branchTo,
481 long instruction )
483 // Copy over the template code.
484 bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
486 // Fill in the address.
487 ((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF;
488 ((short*)island->instructions)[kAddressHi]
489 = (((long) branchTo) >> 16) & 0x0000FFFF;
491 // Fill in the (optional) instuction.
492 if( instruction != 0 ) {
493 ((short*)island->instructions)[kInstructionLo]
494 = instruction & 0x0000FFFF;
495 ((short*)island->instructions)[kInstructionHi]
496 = (instruction >> 16) & 0x0000FFFF;
499 //MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) );
500 msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
502 return err_none;
504 #endif
506 #if defined(__i386__)
507 mach_error_t
508 setBranchIslandTarget_i386(
509 BranchIsland *island,
510 const void *branchTo,
511 char* instructions )
514 // Copy over the template code.
515 bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
517 // copy original instructions
518 if (instructions) {
519 bcopy (instructions, island->instructions + kInstructions, kOriginalInstructionsSize);
522 // Fill in the address.
523 int32_t addressOffset = (char *)branchTo - (island->instructions + kJumpAddress + 4);
524 *((int32_t *)(island->instructions + kJumpAddress)) = addressOffset;
526 msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
527 return err_none;
530 #elif defined(__x86_64__)
531 mach_error_t
532 setBranchIslandTarget_i386(
533 BranchIsland *island,
534 const void *branchTo,
535 char* instructions )
537 // Copy over the template code.
538 bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
540 // Copy original instructions.
541 if (instructions) {
542 bcopy (instructions, island->instructions, kOriginalInstructionsSize);
545 // Fill in the address.
546 *((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo;
547 msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
549 return err_none;
551 #endif
554 #if defined(__i386__) || defined(__x86_64__)
555 // simplistic instruction matching
556 typedef struct {
557 unsigned int length; // max 15
558 unsigned char mask[15]; // sequence of bytes in memory order
559 unsigned char constraint[15]; // sequence of bytes in memory order
560 } AsmInstructionMatch;
562 #if defined(__i386__)
563 static AsmInstructionMatch possibleInstructions[] = {
564 // clang-format off
565 { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x????????
566 { 0x5, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0x55, 0x89, 0xe5, 0xc9, 0xc3} }, // push %ebp; mov %esp,%ebp; leave; ret
567 { 0x1, {0xFF}, {0x90} }, // nop
568 { 0x1, {0xFF}, {0x55} }, // push %esp
569 { 0x2, {0xFF, 0xFF}, {0x89, 0xE5} }, // mov %esp,%ebp
570 { 0x1, {0xFF}, {0x53} }, // push %ebx
571 { 0x3, {0xFF, 0xFF, 0x00}, {0x83, 0xEC, 0x00} }, // sub 0x??, %esp
572 { 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x81, 0xEC, 0x00, 0x00, 0x00, 0x00} }, // sub 0x??, %esp with 32bit immediate
573 { 0x1, {0xFF}, {0x57} }, // push %edi
574 { 0x1, {0xFF}, {0x56} }, // push %esi
575 { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax
576 { 0x3, {0xFF, 0x4F, 0x00}, {0x8B, 0x45, 0x00} }, // mov $imm(%ebp), %reg
577 { 0x3, {0xFF, 0x4C, 0x00}, {0x8B, 0x40, 0x00} }, // mov $imm(%eax-%edx), %reg
578 { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x8B, 0x4C, 0x24, 0x00} }, // mov $imm(%esp), %ecx
579 { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %eax
580 { 0x6, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0xE8, 0x00, 0x00, 0x00, 0x00, 0x58} }, // call $imm; pop %eax
581 { 0x0 }
582 // clang-format on
584 #elif defined(__x86_64__)
585 static AsmInstructionMatch possibleInstructions[] = {
586 // clang-format off
587 { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x????????
588 { 0x1, {0xFF}, {0x90} }, // nop
589 { 0x1, {0xF8}, {0x50} }, // push %rX
590 { 0x3, {0xFF, 0xFF, 0xFF}, {0x48, 0x89, 0xE5} }, // mov %rsp,%rbp
591 { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xEC, 0x00} }, // sub 0x??, %rsp
592 { 0x4, {0xFB, 0xFF, 0x00, 0x00}, {0x48, 0x89, 0x00, 0x00} }, // move onto rbp
593 { 0x4, {0xFF, 0xFF, 0xFF, 0xFF}, {0x40, 0x0f, 0xbe, 0xce} }, // movsbl %sil, %ecx
594 { 0x2, {0xFF, 0x00}, {0x41, 0x00} }, // push %rXX
595 { 0x2, {0xFF, 0x00}, {0x85, 0x00} }, // test %rX,%rX
596 { 0x5, {0xF8, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %reg
597 { 0x3, {0xFF, 0xFF, 0x00}, {0xFF, 0x77, 0x00} }, // pushq $imm(%rdi)
598 { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax
599 { 0x2, {0xFF, 0xFF}, {0x89, 0xF8} }, // mov %edi, %eax
601 //leaq offset(%rip),%rax
602 { 0x7, {0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x48, 0x8d, 0x05, 0x00, 0x00, 0x00, 0x00} },
604 { 0x0 }
605 // clang-format on
607 #endif
609 static Boolean codeMatchesInstruction(unsigned char *code, AsmInstructionMatch* instruction)
611 Boolean match = true;
613 size_t i;
614 for (i=0; i<instruction->length; i++) {
615 unsigned char mask = instruction->mask[i];
616 unsigned char constraint = instruction->constraint[i];
617 unsigned char codeValue = code[i];
619 match = ((codeValue & mask) == constraint);
620 if (!match) break;
623 return match;
626 #if defined(__i386__) || defined(__x86_64__)
627 static Boolean
628 eatKnownInstructions(
629 unsigned char *code,
630 uint64_t *newInstruction,
631 int *howManyEaten,
632 char *originalInstructions,
633 int *originalInstructionCount,
634 uint8_t *originalInstructionSizes )
636 Boolean allInstructionsKnown = true;
637 int totalEaten = 0;
638 unsigned char* ptr = code;
639 int remainsToEat = 5; // a JMP instruction takes 5 bytes
640 int instructionIndex = 0;
642 if (howManyEaten) *howManyEaten = 0;
643 if (originalInstructionCount) *originalInstructionCount = 0;
644 while (remainsToEat > 0) {
645 Boolean curInstructionKnown = false;
647 // See if instruction matches one we know
648 AsmInstructionMatch* curInstr = possibleInstructions;
649 do {
650 if ((curInstructionKnown = codeMatchesInstruction(ptr, curInstr))) break;
651 curInstr++;
652 } while (curInstr->length > 0);
654 // if all instruction matches failed, we don't know current instruction then, stop here
655 if (!curInstructionKnown) {
656 allInstructionsKnown = false;
657 fprintf(stderr, "mach_override: some instructions unknown! Need to update mach_override.c\n");
658 break;
661 // At this point, we've matched curInstr
662 int eaten = curInstr->length;
663 ptr += eaten;
664 remainsToEat -= eaten;
665 totalEaten += eaten;
667 if (originalInstructionSizes) originalInstructionSizes[instructionIndex] = eaten;
668 instructionIndex += 1;
669 if (originalInstructionCount) *originalInstructionCount = instructionIndex;
673 if (howManyEaten) *howManyEaten = totalEaten;
675 if (originalInstructions) {
676 Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize);
678 if (enoughSpaceForOriginalInstructions) {
679 memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP
680 bcopy(code, originalInstructions, totalEaten);
681 } else {
682 // printf ("Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n");
683 return false;
687 if (allInstructionsKnown) {
688 // save last 3 bytes of first 64bits of codre we'll replace
689 uint64_t currentFirst64BitsOfCode = *((uint64_t *)code);
690 currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation
691 currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL;
693 // keep only last 3 instructions bytes, first 5 will be replaced by JMP instr
694 *newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes
695 *newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes
698 return allInstructionsKnown;
701 static void
702 fixupInstructions(
703 uint32_t offset,
704 void *instructionsToFix,
705 int instructionCount,
706 uint8_t *instructionSizes )
708 // The start of "leaq offset(%rip),%rax"
709 static const uint8_t LeaqHeader[] = {0x48, 0x8d, 0x05};
711 int index;
712 for (index = 0;index < instructionCount;index += 1)
714 if (*(uint8_t*)instructionsToFix == 0xE9) // 32-bit jump relative
716 uint32_t *jumpOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 1);
717 *jumpOffsetPtr += offset;
720 // leaq offset(%rip),%rax
721 if (memcmp(instructionsToFix, LeaqHeader, 3) == 0) {
722 uint32_t *LeaqOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 3);
723 *LeaqOffsetPtr += offset;
726 // 32-bit call relative to the next addr; pop %eax
727 if (*(uint8_t*)instructionsToFix == 0xE8)
729 // Just this call is larger than the jump we use, so we
730 // know this is the last instruction.
731 assert(index == (instructionCount - 1));
732 assert(instructionSizes[index] == 6);
734 // Insert "addl $offset, %eax" in the end so that when
735 // we jump to the rest of the function %eax has the
736 // value it would have if eip had been pushed by the
737 // call in its original position.
738 uint8_t *op = instructionsToFix;
739 op += 6;
740 *op = 0x05; // addl
741 uint32_t *addImmPtr = (uint32_t*)(op + 1);
742 *addImmPtr = offset;
745 instructionsToFix = (void*)((uintptr_t)instructionsToFix + instructionSizes[index]);
748 #endif
750 #if defined(__i386__)
751 __asm(
752 ".text;"
753 ".align 2, 0x90;"
754 "_atomic_mov64:;"
755 " pushl %ebp;"
756 " movl %esp, %ebp;"
757 " pushl %esi;"
758 " pushl %ebx;"
759 " pushl %ecx;"
760 " pushl %eax;"
761 " pushl %edx;"
763 // atomic push of value to an address
764 // we use cmpxchg8b, which compares content of an address with
765 // edx:eax. If they are equal, it atomically puts 64bit value
766 // ecx:ebx in address.
767 // We thus put contents of address in edx:eax to force ecx:ebx
768 // in address
769 " mov 8(%ebp), %esi;" // esi contains target address
770 " mov 12(%ebp), %ebx;"
771 " mov 16(%ebp), %ecx;" // ecx:ebx now contains value to put in target address
772 " mov (%esi), %eax;"
773 " mov 4(%esi), %edx;" // edx:eax now contains value currently contained in target address
774 " lock; cmpxchg8b (%esi);" // atomic move.
776 // restore registers
777 " popl %edx;"
778 " popl %eax;"
779 " popl %ecx;"
780 " popl %ebx;"
781 " popl %esi;"
782 " popl %ebp;"
783 " ret"
785 #elif defined(__x86_64__)
786 void atomic_mov64(
787 uint64_t *targetAddress,
788 uint64_t value )
790 *targetAddress = value;
792 #endif
793 #endif