michael@0: // Copied from upstream at revision 195c13743fe0ebc658714e2a9567d86529f20443. michael@0: // mach_override.c semver:1.2.0 michael@0: // Copyright (c) 2003-2012 Jonathan 'Wolf' Rentzsch: http://rentzsch.com michael@0: // Some rights reserved: http://opensource.org/licenses/mit michael@0: // https://github.com/rentzsch/mach_override michael@0: michael@0: #include "mach_override.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: michael@0: /************************** michael@0: * michael@0: * Constants michael@0: * michael@0: **************************/ michael@0: #pragma mark - michael@0: #pragma mark (Constants) michael@0: michael@0: #define kPageSize 4096 michael@0: #if defined(__ppc__) || defined(__POWERPC__) michael@0: michael@0: long kIslandTemplate[] = { michael@0: 0x9001FFFC, // stw r0,-4(SP) michael@0: 0x3C00DEAD, // lis r0,0xDEAD michael@0: 0x6000BEEF, // ori r0,r0,0xBEEF michael@0: 0x7C0903A6, // mtctr r0 michael@0: 0x8001FFFC, // lwz r0,-4(SP) michael@0: 0x60000000, // nop ; optionally replaced michael@0: 0x4E800420 // bctr michael@0: }; michael@0: michael@0: #define kAddressHi 3 michael@0: #define kAddressLo 5 michael@0: #define kInstructionHi 10 michael@0: #define kInstructionLo 11 michael@0: michael@0: #elif defined(__i386__) michael@0: michael@0: #define kOriginalInstructionsSize 16 michael@0: // On X86 we migh need to instert an add with a 32 bit immediate after the michael@0: // original instructions. michael@0: #define kMaxFixupSizeIncrease 5 michael@0: michael@0: unsigned char kIslandTemplate[] = { michael@0: // kOriginalInstructionsSize nop instructions so that we michael@0: // should have enough space to host original instructions michael@0: 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, michael@0: 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, michael@0: // Now the real jump instruction michael@0: 0xE9, 0xEF, 0xBE, 0xAD, 0xDE michael@0: }; michael@0: michael@0: #define kInstructions 0 michael@0: #define kJumpAddress kInstructions + kOriginalInstructionsSize + 1 michael@0: #elif defined(__x86_64__) michael@0: michael@0: #define kOriginalInstructionsSize 32 michael@0: // On X86-64 we never need to instert a new instruction. michael@0: #define kMaxFixupSizeIncrease 0 michael@0: michael@0: #define kJumpAddress kOriginalInstructionsSize + 6 michael@0: michael@0: unsigned char kIslandTemplate[] = { michael@0: // kOriginalInstructionsSize nop instructions so that we michael@0: // should have enough space to host original instructions michael@0: 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, michael@0: 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, michael@0: 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, michael@0: 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, michael@0: // Now the real jump instruction michael@0: 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, michael@0: 0x00, 0x00, 0x00, 0x00, michael@0: 0x00, 0x00, 0x00, 0x00 michael@0: }; michael@0: michael@0: #endif michael@0: michael@0: /************************** michael@0: * michael@0: * Data Types michael@0: * michael@0: **************************/ michael@0: #pragma mark - michael@0: #pragma mark (Data Types) michael@0: michael@0: typedef struct { michael@0: char instructions[sizeof(kIslandTemplate)]; michael@0: } BranchIsland; michael@0: michael@0: /************************** michael@0: * michael@0: * Funky Protos michael@0: * michael@0: **************************/ michael@0: #pragma mark - michael@0: #pragma mark (Funky Protos) michael@0: michael@0: static mach_error_t michael@0: allocateBranchIsland( michael@0: BranchIsland **island, michael@0: void *originalFunctionAddress); michael@0: michael@0: mach_error_t michael@0: freeBranchIsland( michael@0: BranchIsland *island ); michael@0: michael@0: #if defined(__ppc__) || defined(__POWERPC__) michael@0: mach_error_t michael@0: setBranchIslandTarget( michael@0: BranchIsland *island, michael@0: const void *branchTo, michael@0: long instruction ); michael@0: #endif michael@0: michael@0: #if defined(__i386__) || defined(__x86_64__) michael@0: mach_error_t michael@0: setBranchIslandTarget_i386( michael@0: BranchIsland *island, michael@0: const void *branchTo, michael@0: char* instructions ); michael@0: void michael@0: atomic_mov64( michael@0: uint64_t *targetAddress, michael@0: uint64_t value ); michael@0: michael@0: static Boolean michael@0: eatKnownInstructions( michael@0: unsigned char *code, michael@0: uint64_t *newInstruction, michael@0: int *howManyEaten, michael@0: char *originalInstructions, michael@0: int *originalInstructionCount, michael@0: uint8_t *originalInstructionSizes ); michael@0: michael@0: static void michael@0: fixupInstructions( michael@0: uint32_t offset, michael@0: void *instructionsToFix, michael@0: int instructionCount, michael@0: uint8_t *instructionSizes ); michael@0: #endif michael@0: michael@0: /******************************************************************************* michael@0: * michael@0: * Interface michael@0: * michael@0: *******************************************************************************/ michael@0: #pragma mark - michael@0: #pragma mark (Interface) michael@0: michael@0: #if defined(__i386__) || defined(__x86_64__) michael@0: mach_error_t makeIslandExecutable(void *address) { michael@0: mach_error_t err = err_none; michael@0: uintptr_t page = (uintptr_t)address & ~(uintptr_t)(kPageSize-1); michael@0: int e = err_none; michael@0: e |= mprotect((void *)page, kPageSize, PROT_EXEC | PROT_READ | PROT_WRITE); michael@0: e |= msync((void *)page, kPageSize, MS_INVALIDATE ); michael@0: if (e) { michael@0: err = err_cannot_override; michael@0: } michael@0: return err; michael@0: } michael@0: #endif michael@0: michael@0: mach_error_t michael@0: mach_override_ptr( michael@0: void *originalFunctionAddress, michael@0: const void *overrideFunctionAddress, michael@0: void **originalFunctionReentryIsland ) michael@0: { michael@0: assert( originalFunctionAddress ); michael@0: assert( overrideFunctionAddress ); michael@0: michael@0: // this addresses overriding such functions as AudioOutputUnitStart() michael@0: // test with modified DefaultOutputUnit project michael@0: #if defined(__x86_64__) michael@0: for(;;){ michael@0: if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp qword near [rip+0x????????] michael@0: originalFunctionAddress=*(void**)((char*)originalFunctionAddress+6+*(int32_t *)((uint16_t*)originalFunctionAddress+1)); michael@0: else break; michael@0: } michael@0: #elif defined(__i386__) michael@0: for(;;){ michael@0: if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp *0x???????? michael@0: originalFunctionAddress=**(void***)((uint16_t*)originalFunctionAddress+1); michael@0: else break; michael@0: } michael@0: #endif michael@0: michael@0: long *originalFunctionPtr = (long*) originalFunctionAddress; michael@0: mach_error_t err = err_none; michael@0: michael@0: #if defined(__ppc__) || defined(__POWERPC__) michael@0: // Ensure first instruction isn't 'mfctr'. michael@0: #define kMFCTRMask 0xfc1fffff michael@0: #define kMFCTRInstruction 0x7c0903a6 michael@0: michael@0: long originalInstruction = *originalFunctionPtr; michael@0: if( !err && ((originalInstruction & kMFCTRMask) == kMFCTRInstruction) ) michael@0: err = err_cannot_override; michael@0: #elif defined(__i386__) || defined(__x86_64__) michael@0: int eatenCount = 0; michael@0: int originalInstructionCount = 0; michael@0: char originalInstructions[kOriginalInstructionsSize]; michael@0: uint8_t originalInstructionSizes[kOriginalInstructionsSize]; michael@0: uint64_t jumpRelativeInstruction = 0; // JMP michael@0: michael@0: Boolean overridePossible = eatKnownInstructions ((unsigned char *)originalFunctionPtr, michael@0: &jumpRelativeInstruction, &eatenCount, michael@0: originalInstructions, &originalInstructionCount, michael@0: originalInstructionSizes ); michael@0: if (eatenCount + kMaxFixupSizeIncrease > kOriginalInstructionsSize) { michael@0: //printf ("Too many instructions eaten\n"); michael@0: overridePossible = false; michael@0: } michael@0: if (!overridePossible) err = err_cannot_override; michael@0: if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); michael@0: #endif michael@0: michael@0: // Make the original function implementation writable. michael@0: if( !err ) { michael@0: err = vm_protect( mach_task_self(), michael@0: (vm_address_t) originalFunctionPtr, 8, false, michael@0: (VM_PROT_ALL | VM_PROT_COPY) ); michael@0: if( err ) michael@0: err = vm_protect( mach_task_self(), michael@0: (vm_address_t) originalFunctionPtr, 8, false, michael@0: (VM_PROT_DEFAULT | VM_PROT_COPY) ); michael@0: } michael@0: if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); michael@0: michael@0: // Allocate and target the escape island to the overriding function. michael@0: BranchIsland *escapeIsland = NULL; michael@0: if( !err ) michael@0: err = allocateBranchIsland( &escapeIsland, originalFunctionAddress ); michael@0: if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); michael@0: michael@0: michael@0: #if defined(__ppc__) || defined(__POWERPC__) michael@0: if( !err ) michael@0: err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 ); michael@0: michael@0: // Build the branch absolute instruction to the escape island. michael@0: long branchAbsoluteInstruction = 0; // Set to 0 just to silence warning. michael@0: if( !err ) { michael@0: long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF; michael@0: branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress; michael@0: } michael@0: #elif defined(__i386__) || defined(__x86_64__) michael@0: if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); michael@0: michael@0: if( !err ) michael@0: err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 ); michael@0: michael@0: if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); michael@0: // Build the jump relative instruction to the escape island michael@0: #endif michael@0: michael@0: michael@0: #if defined(__i386__) || defined(__x86_64__) michael@0: if (!err) { michael@0: uint32_t addressOffset = ((char*)escapeIsland - (char*)originalFunctionPtr - 5); michael@0: addressOffset = OSSwapInt32(addressOffset); michael@0: michael@0: jumpRelativeInstruction |= 0xE900000000000000LL; michael@0: jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24; michael@0: jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction); michael@0: } michael@0: #endif michael@0: michael@0: // Optionally allocate & return the reentry island. This may contain relocated michael@0: // jmp instructions and so has all the same addressing reachability requirements michael@0: // the escape island has to the original function, except the escape island is michael@0: // technically our original function. michael@0: BranchIsland *reentryIsland = NULL; michael@0: if( !err && originalFunctionReentryIsland ) { michael@0: err = allocateBranchIsland( &reentryIsland, escapeIsland); michael@0: if( !err ) michael@0: *originalFunctionReentryIsland = reentryIsland; michael@0: } michael@0: michael@0: #if defined(__ppc__) || defined(__POWERPC__) michael@0: // Atomically: michael@0: // o If the reentry island was allocated: michael@0: // o Insert the original instruction into the reentry island. michael@0: // o Target the reentry island at the 2nd instruction of the michael@0: // original function. michael@0: // o Replace the original instruction with the branch absolute. michael@0: if( !err ) { michael@0: int escapeIslandEngaged = false; michael@0: do { michael@0: if( reentryIsland ) michael@0: err = setBranchIslandTarget( reentryIsland, michael@0: (void*) (originalFunctionPtr+1), originalInstruction ); michael@0: if( !err ) { michael@0: escapeIslandEngaged = CompareAndSwap( originalInstruction, michael@0: branchAbsoluteInstruction, michael@0: (UInt32*)originalFunctionPtr ); michael@0: if( !escapeIslandEngaged ) { michael@0: // Someone replaced the instruction out from under us, michael@0: // re-read the instruction, make sure it's still not michael@0: // 'mfctr' and try again. michael@0: originalInstruction = *originalFunctionPtr; michael@0: if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction) michael@0: err = err_cannot_override; michael@0: } michael@0: } michael@0: } while( !err && !escapeIslandEngaged ); michael@0: } michael@0: #elif defined(__i386__) || defined(__x86_64__) michael@0: // Atomically: michael@0: // o If the reentry island was allocated: michael@0: // o Insert the original instructions into the reentry island. michael@0: // o Target the reentry island at the first non-replaced michael@0: // instruction of the original function. michael@0: // o Replace the original first instructions with the jump relative. michael@0: // michael@0: // Note that on i386, we do not support someone else changing the code under our feet michael@0: if ( !err ) { michael@0: uint32_t offset = (uintptr_t)originalFunctionPtr - (uintptr_t)reentryIsland; michael@0: fixupInstructions(offset, originalInstructions, michael@0: originalInstructionCount, originalInstructionSizes ); michael@0: michael@0: if( reentryIsland ) michael@0: err = setBranchIslandTarget_i386( reentryIsland, michael@0: (void*) ((char *)originalFunctionPtr+eatenCount), originalInstructions ); michael@0: // try making islands executable before planting the jmp michael@0: #if defined(__x86_64__) || defined(__i386__) michael@0: if( !err ) michael@0: err = makeIslandExecutable(escapeIsland); michael@0: if( !err && reentryIsland ) michael@0: err = makeIslandExecutable(reentryIsland); michael@0: #endif michael@0: if ( !err ) michael@0: atomic_mov64((uint64_t *)originalFunctionPtr, jumpRelativeInstruction); michael@0: } michael@0: #endif michael@0: michael@0: // Clean up on error. michael@0: if( err ) { michael@0: if( reentryIsland ) michael@0: freeBranchIsland( reentryIsland ); michael@0: if( escapeIsland ) michael@0: freeBranchIsland( escapeIsland ); michael@0: } michael@0: michael@0: return err; michael@0: } michael@0: michael@0: /******************************************************************************* michael@0: * michael@0: * Implementation michael@0: * michael@0: *******************************************************************************/ michael@0: #pragma mark - michael@0: #pragma mark (Implementation) michael@0: michael@0: static bool jump_in_range(intptr_t from, intptr_t to) { michael@0: intptr_t field_value = to - from - 5; michael@0: int32_t field_value_32 = field_value; michael@0: return field_value == field_value_32; michael@0: } michael@0: michael@0: /******************************************************************************* michael@0: Implementation: Allocates memory for a branch island. michael@0: michael@0: @param island <- The allocated island. michael@0: @result <- mach_error_t michael@0: michael@0: ***************************************************************************/ michael@0: michael@0: static mach_error_t michael@0: allocateBranchIslandAux( michael@0: BranchIsland **island, michael@0: void *originalFunctionAddress, michael@0: bool forward) michael@0: { michael@0: assert( island ); michael@0: assert( sizeof( BranchIsland ) <= kPageSize ); michael@0: michael@0: vm_map_t task_self = mach_task_self(); michael@0: vm_address_t original_address = (vm_address_t) originalFunctionAddress; michael@0: vm_address_t address = original_address; michael@0: michael@0: for (;;) { michael@0: vm_size_t vmsize = 0; michael@0: memory_object_name_t object = 0; michael@0: kern_return_t kr = 0; michael@0: vm_region_flavor_t flavor = VM_REGION_BASIC_INFO; michael@0: // Find the region the address is in. michael@0: #if __WORDSIZE == 32 michael@0: vm_region_basic_info_data_t info; michael@0: mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT; michael@0: kr = vm_region(task_self, &address, &vmsize, flavor, michael@0: (vm_region_info_t)&info, &info_count, &object); michael@0: #else michael@0: vm_region_basic_info_data_64_t info; michael@0: mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64; michael@0: kr = vm_region_64(task_self, &address, &vmsize, flavor, michael@0: (vm_region_info_t)&info, &info_count, &object); michael@0: #endif michael@0: if (kr != KERN_SUCCESS) michael@0: return kr; michael@0: assert((address & (kPageSize - 1)) == 0); michael@0: michael@0: // Go to the first page before or after this region michael@0: vm_address_t new_address = forward ? address + vmsize : address - kPageSize; michael@0: #if __WORDSIZE == 64 michael@0: if(!jump_in_range(original_address, new_address)) michael@0: break; michael@0: #endif michael@0: address = new_address; michael@0: michael@0: // Try to allocate this page. michael@0: kr = vm_allocate(task_self, &address, kPageSize, 0); michael@0: if (kr == KERN_SUCCESS) { michael@0: *island = (BranchIsland*) address; michael@0: return err_none; michael@0: } michael@0: if (kr != KERN_NO_SPACE) michael@0: return kr; michael@0: } michael@0: michael@0: return KERN_NO_SPACE; michael@0: } michael@0: michael@0: static mach_error_t michael@0: allocateBranchIsland( michael@0: BranchIsland **island, michael@0: void *originalFunctionAddress) michael@0: { michael@0: mach_error_t err = michael@0: allocateBranchIslandAux(island, originalFunctionAddress, true); michael@0: if (!err) michael@0: return err; michael@0: return allocateBranchIslandAux(island, originalFunctionAddress, false); michael@0: } michael@0: michael@0: michael@0: /******************************************************************************* michael@0: Implementation: Deallocates memory for a branch island. michael@0: michael@0: @param island -> The island to deallocate. michael@0: @result <- mach_error_t michael@0: michael@0: ***************************************************************************/ michael@0: michael@0: mach_error_t michael@0: freeBranchIsland( michael@0: BranchIsland *island ) michael@0: { michael@0: assert( island ); michael@0: assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] ); michael@0: assert( sizeof( BranchIsland ) <= kPageSize ); michael@0: return vm_deallocate( mach_task_self(), (vm_address_t) island, michael@0: kPageSize ); michael@0: } michael@0: michael@0: /******************************************************************************* michael@0: Implementation: Sets the branch island's target, with an optional michael@0: instruction. michael@0: michael@0: @param island -> The branch island to insert target into. michael@0: @param branchTo -> The address of the target. michael@0: @param instruction -> Optional instruction to execute prior to branch. Set michael@0: to zero for nop. michael@0: @result <- mach_error_t michael@0: michael@0: ***************************************************************************/ michael@0: #if defined(__ppc__) || defined(__POWERPC__) michael@0: mach_error_t michael@0: setBranchIslandTarget( michael@0: BranchIsland *island, michael@0: const void *branchTo, michael@0: long instruction ) michael@0: { michael@0: // Copy over the template code. michael@0: bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); michael@0: michael@0: // Fill in the address. michael@0: ((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF; michael@0: ((short*)island->instructions)[kAddressHi] michael@0: = (((long) branchTo) >> 16) & 0x0000FFFF; michael@0: michael@0: // Fill in the (optional) instuction. michael@0: if( instruction != 0 ) { michael@0: ((short*)island->instructions)[kInstructionLo] michael@0: = instruction & 0x0000FFFF; michael@0: ((short*)island->instructions)[kInstructionHi] michael@0: = (instruction >> 16) & 0x0000FFFF; michael@0: } michael@0: michael@0: //MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) ); michael@0: msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); michael@0: michael@0: return err_none; michael@0: } michael@0: #endif michael@0: michael@0: #if defined(__i386__) michael@0: mach_error_t michael@0: setBranchIslandTarget_i386( michael@0: BranchIsland *island, michael@0: const void *branchTo, michael@0: char* instructions ) michael@0: { michael@0: michael@0: // Copy over the template code. michael@0: bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); michael@0: michael@0: // copy original instructions michael@0: if (instructions) { michael@0: bcopy (instructions, island->instructions + kInstructions, kOriginalInstructionsSize); michael@0: } michael@0: michael@0: // Fill in the address. michael@0: int32_t addressOffset = (char *)branchTo - (island->instructions + kJumpAddress + 4); michael@0: *((int32_t *)(island->instructions + kJumpAddress)) = addressOffset; michael@0: michael@0: msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); michael@0: return err_none; michael@0: } michael@0: michael@0: #elif defined(__x86_64__) michael@0: mach_error_t michael@0: setBranchIslandTarget_i386( michael@0: BranchIsland *island, michael@0: const void *branchTo, michael@0: char* instructions ) michael@0: { michael@0: // Copy over the template code. michael@0: bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); michael@0: michael@0: // Copy original instructions. michael@0: if (instructions) { michael@0: bcopy (instructions, island->instructions, kOriginalInstructionsSize); michael@0: } michael@0: michael@0: // Fill in the address. michael@0: *((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo; michael@0: msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); michael@0: michael@0: return err_none; michael@0: } michael@0: #endif michael@0: michael@0: michael@0: #if defined(__i386__) || defined(__x86_64__) michael@0: // simplistic instruction matching michael@0: typedef struct { michael@0: unsigned int length; // max 15 michael@0: unsigned char mask[15]; // sequence of bytes in memory order michael@0: unsigned char constraint[15]; // sequence of bytes in memory order michael@0: } AsmInstructionMatch; michael@0: michael@0: #if defined(__i386__) michael@0: static AsmInstructionMatch possibleInstructions[] = { michael@0: { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x???????? michael@0: { 0x5, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0x55, 0x89, 0xe5, 0xc9, 0xc3} }, // push %ebp; mov %esp,%ebp; leave; ret michael@0: { 0x1, {0xFF}, {0x90} }, // nop michael@0: { 0x1, {0xFF}, {0x55} }, // push %esp michael@0: { 0x2, {0xFF, 0xFF}, {0x89, 0xE5} }, // mov %esp,%ebp michael@0: { 0x1, {0xFF}, {0x53} }, // push %ebx michael@0: { 0x3, {0xFF, 0xFF, 0x00}, {0x83, 0xEC, 0x00} }, // sub 0x??, %esp michael@0: { 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x81, 0xEC, 0x00, 0x00, 0x00, 0x00} }, // sub 0x??, %esp with 32bit immediate michael@0: { 0x1, {0xFF}, {0x57} }, // push %edi michael@0: { 0x1, {0xFF}, {0x56} }, // push %esi michael@0: { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax michael@0: { 0x3, {0xFF, 0x4F, 0x00}, {0x8B, 0x45, 0x00} }, // mov $imm(%ebp), %reg michael@0: { 0x3, {0xFF, 0x4C, 0x00}, {0x8B, 0x40, 0x00} }, // mov $imm(%eax-%edx), %reg michael@0: { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x8B, 0x4C, 0x24, 0x00} }, // mov $imm(%esp), %ecx michael@0: { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %eax michael@0: { 0x6, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0xE8, 0x00, 0x00, 0x00, 0x00, 0x58} }, // call $imm; pop %eax michael@0: { 0x0 } michael@0: }; michael@0: #elif defined(__x86_64__) michael@0: static AsmInstructionMatch possibleInstructions[] = { michael@0: { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x???????? michael@0: { 0x1, {0xFF}, {0x90} }, // nop michael@0: { 0x1, {0xF8}, {0x50} }, // push %rX michael@0: { 0x3, {0xFF, 0xFF, 0xFF}, {0x48, 0x89, 0xE5} }, // mov %rsp,%rbp michael@0: { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xEC, 0x00} }, // sub 0x??, %rsp michael@0: { 0x4, {0xFB, 0xFF, 0x00, 0x00}, {0x48, 0x89, 0x00, 0x00} }, // move onto rbp michael@0: { 0x4, {0xFF, 0xFF, 0xFF, 0xFF}, {0x40, 0x0f, 0xbe, 0xce} }, // movsbl %sil, %ecx michael@0: { 0x2, {0xFF, 0x00}, {0x41, 0x00} }, // push %rXX michael@0: { 0x2, {0xFF, 0x00}, {0x85, 0x00} }, // test %rX,%rX michael@0: { 0x5, {0xF8, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %reg michael@0: { 0x3, {0xFF, 0xFF, 0x00}, {0xFF, 0x77, 0x00} }, // pushq $imm(%rdi) michael@0: { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax michael@0: { 0x2, {0xFF, 0xFF}, {0x89, 0xF8} }, // mov %edi, %eax michael@0: michael@0: //leaq offset(%rip),%rax michael@0: { 0x7, {0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x48, 0x8d, 0x05, 0x00, 0x00, 0x00, 0x00} }, michael@0: michael@0: { 0x0 } michael@0: }; michael@0: #endif michael@0: michael@0: static Boolean codeMatchesInstruction(unsigned char *code, AsmInstructionMatch* instruction) michael@0: { michael@0: Boolean match = true; michael@0: michael@0: size_t i; michael@0: for (i=0; ilength; i++) { michael@0: unsigned char mask = instruction->mask[i]; michael@0: unsigned char constraint = instruction->constraint[i]; michael@0: unsigned char codeValue = code[i]; michael@0: michael@0: match = ((codeValue & mask) == constraint); michael@0: if (!match) break; michael@0: } michael@0: michael@0: return match; michael@0: } michael@0: michael@0: #if defined(__i386__) || defined(__x86_64__) michael@0: static Boolean michael@0: eatKnownInstructions( michael@0: unsigned char *code, michael@0: uint64_t *newInstruction, michael@0: int *howManyEaten, michael@0: char *originalInstructions, michael@0: int *originalInstructionCount, michael@0: uint8_t *originalInstructionSizes ) michael@0: { michael@0: Boolean allInstructionsKnown = true; michael@0: int totalEaten = 0; michael@0: unsigned char* ptr = code; michael@0: int remainsToEat = 5; // a JMP instruction takes 5 bytes michael@0: int instructionIndex = 0; michael@0: michael@0: if (howManyEaten) *howManyEaten = 0; michael@0: if (originalInstructionCount) *originalInstructionCount = 0; michael@0: while (remainsToEat > 0) { michael@0: Boolean curInstructionKnown = false; michael@0: michael@0: // See if instruction matches one we know michael@0: AsmInstructionMatch* curInstr = possibleInstructions; michael@0: do { michael@0: if ((curInstructionKnown = codeMatchesInstruction(ptr, curInstr))) break; michael@0: curInstr++; michael@0: } while (curInstr->length > 0); michael@0: michael@0: // if all instruction matches failed, we don't know current instruction then, stop here michael@0: if (!curInstructionKnown) { michael@0: allInstructionsKnown = false; michael@0: fprintf(stderr, "mach_override: some instructions unknown! Need to update mach_override.c\n"); michael@0: break; michael@0: } michael@0: michael@0: // At this point, we've matched curInstr michael@0: int eaten = curInstr->length; michael@0: ptr += eaten; michael@0: remainsToEat -= eaten; michael@0: totalEaten += eaten; michael@0: michael@0: if (originalInstructionSizes) originalInstructionSizes[instructionIndex] = eaten; michael@0: instructionIndex += 1; michael@0: if (originalInstructionCount) *originalInstructionCount = instructionIndex; michael@0: } michael@0: michael@0: michael@0: if (howManyEaten) *howManyEaten = totalEaten; michael@0: michael@0: if (originalInstructions) { michael@0: Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize); michael@0: michael@0: if (enoughSpaceForOriginalInstructions) { michael@0: memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP michael@0: bcopy(code, originalInstructions, totalEaten); michael@0: } else { michael@0: // printf ("Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n"); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (allInstructionsKnown) { michael@0: // save last 3 bytes of first 64bits of codre we'll replace michael@0: uint64_t currentFirst64BitsOfCode = *((uint64_t *)code); michael@0: currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation michael@0: currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL; michael@0: michael@0: // keep only last 3 instructions bytes, first 5 will be replaced by JMP instr michael@0: *newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes michael@0: *newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes michael@0: } michael@0: michael@0: return allInstructionsKnown; michael@0: } michael@0: michael@0: static void michael@0: fixupInstructions( michael@0: uint32_t offset, michael@0: void *instructionsToFix, michael@0: int instructionCount, michael@0: uint8_t *instructionSizes ) michael@0: { michael@0: // The start of "leaq offset(%rip),%rax" michael@0: static const uint8_t LeaqHeader[] = {0x48, 0x8d, 0x05}; michael@0: michael@0: int index; michael@0: for (index = 0;index < instructionCount;index += 1) michael@0: { michael@0: if (*(uint8_t*)instructionsToFix == 0xE9) // 32-bit jump relative michael@0: { michael@0: uint32_t *jumpOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 1); michael@0: *jumpOffsetPtr += offset; michael@0: } michael@0: michael@0: // leaq offset(%rip),%rax michael@0: if (memcmp(instructionsToFix, LeaqHeader, 3) == 0) { michael@0: uint32_t *LeaqOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 3); michael@0: *LeaqOffsetPtr += offset; michael@0: } michael@0: michael@0: // 32-bit call relative to the next addr; pop %eax michael@0: if (*(uint8_t*)instructionsToFix == 0xE8) michael@0: { michael@0: // Just this call is larger than the jump we use, so we michael@0: // know this is the last instruction. michael@0: assert(index == (instructionCount - 1)); michael@0: assert(instructionSizes[index] == 6); michael@0: michael@0: // Insert "addl $offset, %eax" in the end so that when michael@0: // we jump to the rest of the function %eax has the michael@0: // value it would have if eip had been pushed by the michael@0: // call in its original position. michael@0: uint8_t *op = instructionsToFix; michael@0: op += 6; michael@0: *op = 0x05; // addl michael@0: uint32_t *addImmPtr = (uint32_t*)(op + 1); michael@0: *addImmPtr = offset; michael@0: } michael@0: michael@0: instructionsToFix = (void*)((uintptr_t)instructionsToFix + instructionSizes[index]); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: #if defined(__i386__) michael@0: __asm( michael@0: ".text;" michael@0: ".align 2, 0x90;" michael@0: "_atomic_mov64:;" michael@0: " pushl %ebp;" michael@0: " movl %esp, %ebp;" michael@0: " pushl %esi;" michael@0: " pushl %ebx;" michael@0: " pushl %ecx;" michael@0: " pushl %eax;" michael@0: " pushl %edx;" michael@0: michael@0: // atomic push of value to an address michael@0: // we use cmpxchg8b, which compares content of an address with michael@0: // edx:eax. If they are equal, it atomically puts 64bit value michael@0: // ecx:ebx in address. michael@0: // We thus put contents of address in edx:eax to force ecx:ebx michael@0: // in address michael@0: " mov 8(%ebp), %esi;" // esi contains target address michael@0: " mov 12(%ebp), %ebx;" michael@0: " mov 16(%ebp), %ecx;" // ecx:ebx now contains value to put in target address michael@0: " mov (%esi), %eax;" michael@0: " mov 4(%esi), %edx;" // edx:eax now contains value currently contained in target address michael@0: " lock; cmpxchg8b (%esi);" // atomic move. michael@0: michael@0: // restore registers michael@0: " popl %edx;" michael@0: " popl %eax;" michael@0: " popl %ecx;" michael@0: " popl %ebx;" michael@0: " popl %esi;" michael@0: " popl %ebp;" michael@0: " ret" michael@0: ); michael@0: #elif defined(__x86_64__) michael@0: void atomic_mov64( michael@0: uint64_t *targetAddress, michael@0: uint64_t value ) michael@0: { michael@0: *targetAddress = value; michael@0: } michael@0: #endif michael@0: #endif