xpcom/build/mach_override.c

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:f4d3846ebe69
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
6
7 #include "mach_override.h"
8
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>
14
15 #include <CoreServices/CoreServices.h>
16
17 /**************************
18 *
19 * Constants
20 *
21 **************************/
22 #pragma mark -
23 #pragma mark (Constants)
24
25 #define kPageSize 4096
26 #if defined(__ppc__) || defined(__POWERPC__)
27
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
36 };
37
38 #define kAddressHi 3
39 #define kAddressLo 5
40 #define kInstructionHi 10
41 #define kInstructionLo 11
42
43 #elif defined(__i386__)
44
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
49
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
57 };
58
59 #define kInstructions 0
60 #define kJumpAddress kInstructions + kOriginalInstructionsSize + 1
61 #elif defined(__x86_64__)
62
63 #define kOriginalInstructionsSize 32
64 // On X86-64 we never need to instert a new instruction.
65 #define kMaxFixupSizeIncrease 0
66
67 #define kJumpAddress kOriginalInstructionsSize + 6
68
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
80 };
81
82 #endif
83
84 /**************************
85 *
86 * Data Types
87 *
88 **************************/
89 #pragma mark -
90 #pragma mark (Data Types)
91
92 typedef struct {
93 char instructions[sizeof(kIslandTemplate)];
94 } BranchIsland;
95
96 /**************************
97 *
98 * Funky Protos
99 *
100 **************************/
101 #pragma mark -
102 #pragma mark (Funky Protos)
103
104 static mach_error_t
105 allocateBranchIsland(
106 BranchIsland **island,
107 void *originalFunctionAddress);
108
109 mach_error_t
110 freeBranchIsland(
111 BranchIsland *island );
112
113 #if defined(__ppc__) || defined(__POWERPC__)
114 mach_error_t
115 setBranchIslandTarget(
116 BranchIsland *island,
117 const void *branchTo,
118 long instruction );
119 #endif
120
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 );
131
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 );
140
141 static void
142 fixupInstructions(
143 uint32_t offset,
144 void *instructionsToFix,
145 int instructionCount,
146 uint8_t *instructionSizes );
147 #endif
148
149 /*******************************************************************************
150 *
151 * Interface
152 *
153 *******************************************************************************/
154 #pragma mark -
155 #pragma mark (Interface)
156
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;
166 }
167 return err;
168 }
169 #endif
170
171 mach_error_t
172 mach_override_ptr(
173 void *originalFunctionAddress,
174 const void *overrideFunctionAddress,
175 void **originalFunctionReentryIsland )
176 {
177 assert( originalFunctionAddress );
178 assert( overrideFunctionAddress );
179
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;
187 }
188 #elif defined(__i386__)
189 for(;;){
190 if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp *0x????????
191 originalFunctionAddress=**(void***)((uint16_t*)originalFunctionAddress+1);
192 else break;
193 }
194 #endif
195
196 long *originalFunctionPtr = (long*) originalFunctionAddress;
197 mach_error_t err = err_none;
198
199 #if defined(__ppc__) || defined(__POWERPC__)
200 // Ensure first instruction isn't 'mfctr'.
201 #define kMFCTRMask 0xfc1fffff
202 #define kMFCTRInstruction 0x7c0903a6
203
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
213
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;
221 }
222 if (!overridePossible) err = err_cannot_override;
223 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
224 #endif
225
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) );
235 }
236 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
237
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__);
243
244
245 #if defined(__ppc__) || defined(__POWERPC__)
246 if( !err )
247 err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 );
248
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;
254 }
255 #elif defined(__i386__) || defined(__x86_64__)
256 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
257
258 if( !err )
259 err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 );
260
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
264
265
266 #if defined(__i386__) || defined(__x86_64__)
267 if (!err) {
268 uint32_t addressOffset = ((char*)escapeIsland - (char*)originalFunctionPtr - 5);
269 addressOffset = OSSwapInt32(addressOffset);
270
271 jumpRelativeInstruction |= 0xE900000000000000LL;
272 jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24;
273 jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction);
274 }
275 #endif
276
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;
286 }
287
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;
312 }
313 }
314 } while( !err && !escapeIslandEngaged );
315 }
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.
323 //
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 );
329
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);
342 }
343 #endif
344
345 // Clean up on error.
346 if( err ) {
347 if( reentryIsland )
348 freeBranchIsland( reentryIsland );
349 if( escapeIsland )
350 freeBranchIsland( escapeIsland );
351 }
352
353 return err;
354 }
355
356 /*******************************************************************************
357 *
358 * Implementation
359 *
360 *******************************************************************************/
361 #pragma mark -
362 #pragma mark (Implementation)
363
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;
368 }
369
370 /*******************************************************************************
371 Implementation: Allocates memory for a branch island.
372
373 @param island <- The allocated island.
374 @result <- mach_error_t
375
376 ***************************************************************************/
377
378 static mach_error_t
379 allocateBranchIslandAux(
380 BranchIsland **island,
381 void *originalFunctionAddress,
382 bool forward)
383 {
384 assert( island );
385 assert( sizeof( BranchIsland ) <= kPageSize );
386
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;
390
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);
411
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;
419
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;
425 }
426 if (kr != KERN_NO_SPACE)
427 return kr;
428 }
429
430 return KERN_NO_SPACE;
431 }
432
433 static mach_error_t
434 allocateBranchIsland(
435 BranchIsland **island,
436 void *originalFunctionAddress)
437 {
438 mach_error_t err =
439 allocateBranchIslandAux(island, originalFunctionAddress, true);
440 if (!err)
441 return err;
442 return allocateBranchIslandAux(island, originalFunctionAddress, false);
443 }
444
445
446 /*******************************************************************************
447 Implementation: Deallocates memory for a branch island.
448
449 @param island -> The island to deallocate.
450 @result <- mach_error_t
451
452 ***************************************************************************/
453
454 mach_error_t
455 freeBranchIsland(
456 BranchIsland *island )
457 {
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 );
463 }
464
465 /*******************************************************************************
466 Implementation: Sets the branch island's target, with an optional
467 instruction.
468
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
474
475 ***************************************************************************/
476 #if defined(__ppc__) || defined(__POWERPC__)
477 mach_error_t
478 setBranchIslandTarget(
479 BranchIsland *island,
480 const void *branchTo,
481 long instruction )
482 {
483 // Copy over the template code.
484 bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
485
486 // Fill in the address.
487 ((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF;
488 ((short*)island->instructions)[kAddressHi]
489 = (((long) branchTo) >> 16) & 0x0000FFFF;
490
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;
497 }
498
499 //MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) );
500 msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
501
502 return err_none;
503 }
504 #endif
505
506 #if defined(__i386__)
507 mach_error_t
508 setBranchIslandTarget_i386(
509 BranchIsland *island,
510 const void *branchTo,
511 char* instructions )
512 {
513
514 // Copy over the template code.
515 bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
516
517 // copy original instructions
518 if (instructions) {
519 bcopy (instructions, island->instructions + kInstructions, kOriginalInstructionsSize);
520 }
521
522 // Fill in the address.
523 int32_t addressOffset = (char *)branchTo - (island->instructions + kJumpAddress + 4);
524 *((int32_t *)(island->instructions + kJumpAddress)) = addressOffset;
525
526 msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
527 return err_none;
528 }
529
530 #elif defined(__x86_64__)
531 mach_error_t
532 setBranchIslandTarget_i386(
533 BranchIsland *island,
534 const void *branchTo,
535 char* instructions )
536 {
537 // Copy over the template code.
538 bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
539
540 // Copy original instructions.
541 if (instructions) {
542 bcopy (instructions, island->instructions, kOriginalInstructionsSize);
543 }
544
545 // Fill in the address.
546 *((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo;
547 msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
548
549 return err_none;
550 }
551 #endif
552
553
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;
561
562 #if defined(__i386__)
563 static AsmInstructionMatch possibleInstructions[] = {
564 { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x????????
565 { 0x5, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0x55, 0x89, 0xe5, 0xc9, 0xc3} }, // push %ebp; mov %esp,%ebp; leave; ret
566 { 0x1, {0xFF}, {0x90} }, // nop
567 { 0x1, {0xFF}, {0x55} }, // push %esp
568 { 0x2, {0xFF, 0xFF}, {0x89, 0xE5} }, // mov %esp,%ebp
569 { 0x1, {0xFF}, {0x53} }, // push %ebx
570 { 0x3, {0xFF, 0xFF, 0x00}, {0x83, 0xEC, 0x00} }, // sub 0x??, %esp
571 { 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x81, 0xEC, 0x00, 0x00, 0x00, 0x00} }, // sub 0x??, %esp with 32bit immediate
572 { 0x1, {0xFF}, {0x57} }, // push %edi
573 { 0x1, {0xFF}, {0x56} }, // push %esi
574 { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax
575 { 0x3, {0xFF, 0x4F, 0x00}, {0x8B, 0x45, 0x00} }, // mov $imm(%ebp), %reg
576 { 0x3, {0xFF, 0x4C, 0x00}, {0x8B, 0x40, 0x00} }, // mov $imm(%eax-%edx), %reg
577 { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x8B, 0x4C, 0x24, 0x00} }, // mov $imm(%esp), %ecx
578 { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %eax
579 { 0x6, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0xE8, 0x00, 0x00, 0x00, 0x00, 0x58} }, // call $imm; pop %eax
580 { 0x0 }
581 };
582 #elif defined(__x86_64__)
583 static AsmInstructionMatch possibleInstructions[] = {
584 { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x????????
585 { 0x1, {0xFF}, {0x90} }, // nop
586 { 0x1, {0xF8}, {0x50} }, // push %rX
587 { 0x3, {0xFF, 0xFF, 0xFF}, {0x48, 0x89, 0xE5} }, // mov %rsp,%rbp
588 { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xEC, 0x00} }, // sub 0x??, %rsp
589 { 0x4, {0xFB, 0xFF, 0x00, 0x00}, {0x48, 0x89, 0x00, 0x00} }, // move onto rbp
590 { 0x4, {0xFF, 0xFF, 0xFF, 0xFF}, {0x40, 0x0f, 0xbe, 0xce} }, // movsbl %sil, %ecx
591 { 0x2, {0xFF, 0x00}, {0x41, 0x00} }, // push %rXX
592 { 0x2, {0xFF, 0x00}, {0x85, 0x00} }, // test %rX,%rX
593 { 0x5, {0xF8, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %reg
594 { 0x3, {0xFF, 0xFF, 0x00}, {0xFF, 0x77, 0x00} }, // pushq $imm(%rdi)
595 { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax
596 { 0x2, {0xFF, 0xFF}, {0x89, 0xF8} }, // mov %edi, %eax
597
598 //leaq offset(%rip),%rax
599 { 0x7, {0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x48, 0x8d, 0x05, 0x00, 0x00, 0x00, 0x00} },
600
601 { 0x0 }
602 };
603 #endif
604
605 static Boolean codeMatchesInstruction(unsigned char *code, AsmInstructionMatch* instruction)
606 {
607 Boolean match = true;
608
609 size_t i;
610 for (i=0; i<instruction->length; i++) {
611 unsigned char mask = instruction->mask[i];
612 unsigned char constraint = instruction->constraint[i];
613 unsigned char codeValue = code[i];
614
615 match = ((codeValue & mask) == constraint);
616 if (!match) break;
617 }
618
619 return match;
620 }
621
622 #if defined(__i386__) || defined(__x86_64__)
623 static Boolean
624 eatKnownInstructions(
625 unsigned char *code,
626 uint64_t *newInstruction,
627 int *howManyEaten,
628 char *originalInstructions,
629 int *originalInstructionCount,
630 uint8_t *originalInstructionSizes )
631 {
632 Boolean allInstructionsKnown = true;
633 int totalEaten = 0;
634 unsigned char* ptr = code;
635 int remainsToEat = 5; // a JMP instruction takes 5 bytes
636 int instructionIndex = 0;
637
638 if (howManyEaten) *howManyEaten = 0;
639 if (originalInstructionCount) *originalInstructionCount = 0;
640 while (remainsToEat > 0) {
641 Boolean curInstructionKnown = false;
642
643 // See if instruction matches one we know
644 AsmInstructionMatch* curInstr = possibleInstructions;
645 do {
646 if ((curInstructionKnown = codeMatchesInstruction(ptr, curInstr))) break;
647 curInstr++;
648 } while (curInstr->length > 0);
649
650 // if all instruction matches failed, we don't know current instruction then, stop here
651 if (!curInstructionKnown) {
652 allInstructionsKnown = false;
653 fprintf(stderr, "mach_override: some instructions unknown! Need to update mach_override.c\n");
654 break;
655 }
656
657 // At this point, we've matched curInstr
658 int eaten = curInstr->length;
659 ptr += eaten;
660 remainsToEat -= eaten;
661 totalEaten += eaten;
662
663 if (originalInstructionSizes) originalInstructionSizes[instructionIndex] = eaten;
664 instructionIndex += 1;
665 if (originalInstructionCount) *originalInstructionCount = instructionIndex;
666 }
667
668
669 if (howManyEaten) *howManyEaten = totalEaten;
670
671 if (originalInstructions) {
672 Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize);
673
674 if (enoughSpaceForOriginalInstructions) {
675 memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP
676 bcopy(code, originalInstructions, totalEaten);
677 } else {
678 // printf ("Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n");
679 return false;
680 }
681 }
682
683 if (allInstructionsKnown) {
684 // save last 3 bytes of first 64bits of codre we'll replace
685 uint64_t currentFirst64BitsOfCode = *((uint64_t *)code);
686 currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation
687 currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL;
688
689 // keep only last 3 instructions bytes, first 5 will be replaced by JMP instr
690 *newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes
691 *newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes
692 }
693
694 return allInstructionsKnown;
695 }
696
697 static void
698 fixupInstructions(
699 uint32_t offset,
700 void *instructionsToFix,
701 int instructionCount,
702 uint8_t *instructionSizes )
703 {
704 // The start of "leaq offset(%rip),%rax"
705 static const uint8_t LeaqHeader[] = {0x48, 0x8d, 0x05};
706
707 int index;
708 for (index = 0;index < instructionCount;index += 1)
709 {
710 if (*(uint8_t*)instructionsToFix == 0xE9) // 32-bit jump relative
711 {
712 uint32_t *jumpOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 1);
713 *jumpOffsetPtr += offset;
714 }
715
716 // leaq offset(%rip),%rax
717 if (memcmp(instructionsToFix, LeaqHeader, 3) == 0) {
718 uint32_t *LeaqOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 3);
719 *LeaqOffsetPtr += offset;
720 }
721
722 // 32-bit call relative to the next addr; pop %eax
723 if (*(uint8_t*)instructionsToFix == 0xE8)
724 {
725 // Just this call is larger than the jump we use, so we
726 // know this is the last instruction.
727 assert(index == (instructionCount - 1));
728 assert(instructionSizes[index] == 6);
729
730 // Insert "addl $offset, %eax" in the end so that when
731 // we jump to the rest of the function %eax has the
732 // value it would have if eip had been pushed by the
733 // call in its original position.
734 uint8_t *op = instructionsToFix;
735 op += 6;
736 *op = 0x05; // addl
737 uint32_t *addImmPtr = (uint32_t*)(op + 1);
738 *addImmPtr = offset;
739 }
740
741 instructionsToFix = (void*)((uintptr_t)instructionsToFix + instructionSizes[index]);
742 }
743 }
744 #endif
745
746 #if defined(__i386__)
747 __asm(
748 ".text;"
749 ".align 2, 0x90;"
750 "_atomic_mov64:;"
751 " pushl %ebp;"
752 " movl %esp, %ebp;"
753 " pushl %esi;"
754 " pushl %ebx;"
755 " pushl %ecx;"
756 " pushl %eax;"
757 " pushl %edx;"
758
759 // atomic push of value to an address
760 // we use cmpxchg8b, which compares content of an address with
761 // edx:eax. If they are equal, it atomically puts 64bit value
762 // ecx:ebx in address.
763 // We thus put contents of address in edx:eax to force ecx:ebx
764 // in address
765 " mov 8(%ebp), %esi;" // esi contains target address
766 " mov 12(%ebp), %ebx;"
767 " mov 16(%ebp), %ecx;" // ecx:ebx now contains value to put in target address
768 " mov (%esi), %eax;"
769 " mov 4(%esi), %edx;" // edx:eax now contains value currently contained in target address
770 " lock; cmpxchg8b (%esi);" // atomic move.
771
772 // restore registers
773 " popl %edx;"
774 " popl %eax;"
775 " popl %ecx;"
776 " popl %ebx;"
777 " popl %esi;"
778 " popl %ebp;"
779 " ret"
780 );
781 #elif defined(__x86_64__)
782 void atomic_mov64(
783 uint64_t *targetAddress,
784 uint64_t value )
785 {
786 *targetAddress = value;
787 }
788 #endif
789 #endif

mercurial