xpcom/build/mach_override.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial