diff options
author | Mikkel Krautz <mikkel@krautz.dk> | 2010-02-16 00:17:51 +0300 |
---|---|---|
committer | Mikkel Krautz <mikkel@krautz.dk> | 2010-02-16 00:24:05 +0300 |
commit | 2dbba848bae6a051947701ed8a3261a9538dce63 (patch) | |
tree | e45e6df29a14d754346e49f2c6cd00312b5e650a /macx | |
parent | ffe8fe2872b7a9cca734ffd3037ee42a68383f41 (diff) |
Move OSX overlay and overlay launcher into macx subdir.
Diffstat (limited to 'macx')
-rw-r--r-- | macx/common.pri | 6 | ||||
-rw-r--r-- | macx/launcher/launcher.m | 170 | ||||
-rw-r--r-- | macx/launcher/launcher.plist | 20 | ||||
-rw-r--r-- | macx/launcher/launcher.pro | 23 | ||||
-rw-r--r-- | macx/macx.pro | 4 | ||||
-rw-r--r-- | macx/overlay/mach_override.c | 711 | ||||
-rw-r--r-- | macx/overlay/mach_override.h | 157 | ||||
-rw-r--r-- | macx/overlay/overlay.m | 587 | ||||
-rw-r--r-- | macx/overlay/overlay.plist | 13 | ||||
-rw-r--r-- | macx/overlay/overlay.pro | 28 |
10 files changed, 1719 insertions, 0 deletions
diff --git a/macx/common.pri b/macx/common.pri new file mode 100644 index 000000000..f8add5c73 --- /dev/null +++ b/macx/common.pri @@ -0,0 +1,6 @@ +# Common OSX overlay settings. + +VERSION = 1.2.3 + +DEFINES *= VERSION=\\\"$$VERSION\\\" + diff --git a/macx/launcher/launcher.m b/macx/launcher/launcher.m new file mode 100644 index 000000000..7475a064f --- /dev/null +++ b/macx/launcher/launcher.m @@ -0,0 +1,170 @@ +/* + Copyright (C) 2009, Mikkel Krautz <mikkel@krautz.dk> + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * Mumble overlay app-launcher for OS X. + */ + +#include <unistd.h> +#include <ApplicationServices/ApplicationServices.h> + +#import <Cocoa/Cocoa.h> + +static void usage(void) { + printf("Usage: mumble-overlay [opts] <app> [app-args ...]\n"); + printf("\n"); + printf(" Launch an application (given by the `app' argument) with\n"); + printf(" the Mumble overlay enabled.\n"); + printf("\n"); + printf(" -h -? Show usage information.\n"); + printf(" -d Run overlay in debug mode.\n"); + printf(" -v Show version information.\n"); + printf("\n"); + exit(0); +} + +static void version(void) { + printf("%s\n", VERSION); + exit(0); +} + +int main(int argc, char *argv[]) { + NSAutoreleasePool *pool; + char *appPath = NULL; + int c, i, debug = 0; + + pool = [[NSAutoreleasePool alloc] init]; + + while ((c = getopt(argc, argv, "h?vd")) != -1) { + switch (c) { + case 'h': + case '?': + usage(); + break; + case 'v': + version(); + break; + case 'd': + debug = 1; + break; + } + } + argc -= optind; + argv += optind; + + if (argc) { + appPath = argv[0]; + --argc; + ++argv; + } else + usage(); + + FSRef ref; + OSStatus err; + + /* Get FSRef of our app path. */ + err = FSPathMakeRef((const UInt8 *) appPath, &ref, NULL); + if (err != noErr) { + fprintf(stderr, "mumble-overlay: Unable to get reference to application path. Make sure you specify a valid path.\n"); + return -1; + } + + /* Get a hold of our Mumble process, to find our overlay library. */ + NSArray *apps = [[NSWorkspace sharedWorkspace] launchedApplications]; + NSString *mumblePath = nil; + + for (NSDictionary *dict in apps) { + if ([[dict objectForKey:@"NSApplicationBundleIdentifier"] isEqualToString:@"net.sourceforge.mumble.Mumble"]) { + mumblePath = [dict objectForKey:@"NSApplicationPath"]; + break; + } + if ([[dict objectForKey:@"NSApplicationBundleIdentifier"] isEqualToString:@"net.sourceforge.mumble.Mumble11x"]) { + mumblePath = [dict objectForKey:@"NSApplicationPath"]; + break; + } + } + + if (! mumblePath) { + fprintf(stderr, "mumble-overlay: Cannot find open Mumble instance. Bailing.\n"); + return -1; + } + + NSBundle *appBundle = [NSBundle bundleWithPath: mumblePath]; + if (! appBundle) { + fprintf(stderr, "mumble-overlay: Cannot open Mumble app bundle.\n"); + return -1; + } + + NSString *overlayInBundle = [appBundle objectForInfoDictionaryKey:@"MumbleOverlayLibrary"]; + if (! overlayInBundle) { + fprintf(stderr, "mumble-overlay: No key 'MumbleOverlayLibrary' specified in Mumble's property list. Cannot find overlay library.\n"); + return -1; + } + + NSString *overlayPath = [NSString stringWithFormat:@"%@/%@", mumblePath, overlayInBundle]; + + if (! [[NSFileManager defaultManager] fileExistsAtPath:overlayPath]) { + fprintf(stderr, "mumble-overlay: Overlay library non-existant at MumbleOverlayLibrary-specified path.\n"); + return -1; + } + + /* Create environment dict. */ + NSMutableDictionary *environment = [NSMutableDictionary dictionaryWithCapacity: 2]; + [environment setObject:overlayPath forKey:@"DYLD_INSERT_LIBRARIES"]; + if (debug) { + [environment setObject:@"1" forKey:@"MUMBLE_OVERLAY_DEBUG"]; + } + + /* Create argv array. */ + NSMutableArray *arguments = [NSMutableArray arrayWithCapacity:(NSUInteger)argc]; + for (i = 0; i < argc; i++) { + [arguments insertObject: [NSString stringWithCString:argv[i] encoding:NSUTF8StringEncoding] atIndex:i]; + } + + LSApplicationParameters parm = { + .version = 0, + .flags = kLSLaunchDefaults, + .application = &ref, + .asyncLaunchRefCon = NULL, + .environment = environment, + .argv = arguments, + .initialEvent = NULL + }; + + err = LSOpenApplication(&parm, NULL); + + if (err != noErr) { + fprintf(stderr, "mumble-overlay: LSOpenApplication() failed: %i\n", (int)err); + return -1; + } + + return 0; +} diff --git a/macx/launcher/launcher.plist b/macx/launcher/launcher.plist new file mode 100644 index 000000000..9b62681ea --- /dev/null +++ b/macx/launcher/launcher.plist @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleExecutable</key> + <string>mumble-overlay</string> + <key>CFBundleGetInfoString</key> + <string>Mumble helper utility for launching games with the in-game overlay enabled.</string> + <key>CFBundleIdentifier</key> + <string>net.sourceforge.mumble.OverlayLauncher</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleSignature</key> + <string>LAUN</string> + <key>CFBundleVersion</key> + <string>1.2.3</string> + <key>NSHumanReadableCopyright</key> + <string>Copyright (c) 2009 Mikkel Krautz <mikkel@krautz.dk></string> +</dict> +</plist> diff --git a/macx/launcher/launcher.pro b/macx/launcher/launcher.pro new file mode 100644 index 000000000..43d9fa927 --- /dev/null +++ b/macx/launcher/launcher.pro @@ -0,0 +1,23 @@ +# Overlay app-launcher for Mac OS X + +include(../../compiler.pri) +include(../common.pri) + +TEMPLATE = app +CONFIG -= qt app_bundle +CONFIG *= debug_and_release + +TARGET = mumble-overlay +SOURCES = launcher.m +QMAKE_LFLAGS += -framework ApplicationServices -framework Cocoa -sectcreate __TEXT __info_plist launcher.plist +DIST = launcher.plist + +CONFIG(debug, debug|release) { + DESTDIR = ../../debug +} + +CONFIG(release, debug|release) { + DESTDIR = ../../release +} + +include(../../symbols.pri) diff --git a/macx/macx.pro b/macx/macx.pro new file mode 100644 index 000000000..138a9fadb --- /dev/null +++ b/macx/macx.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs + +CONFIG += debug_and_release +SUBDIRS = overlay launcher diff --git a/macx/overlay/mach_override.c b/macx/overlay/mach_override.c new file mode 100644 index 000000000..3a1b3e22d --- /dev/null +++ b/macx/overlay/mach_override.c @@ -0,0 +1,711 @@ +/******************************************************************************* + mach_override.c + Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com> + Some rights reserved: <http://opensource.org/licenses/mit-license.php> + + ***************************************************************************/ + +#include "mach_override.h" + +#include <mach-o/dyld.h> +#include <mach/mach_host.h> +#include <mach/mach_init.h> +#include <mach/vm_map.h> +#include <sys/mman.h> + +#include <CoreServices/CoreServices.h> + +/************************** +* +* Constants +* +**************************/ +#pragma mark - +#pragma mark (Constants) + +#if defined(__ppc__) || defined(__POWERPC__) + +long kIslandTemplate[] = { + 0x9001FFFC, // stw r0,-4(SP) + 0x3C00DEAD, // lis r0,0xDEAD + 0x6000BEEF, // ori r0,r0,0xBEEF + 0x7C0903A6, // mtctr r0 + 0x8001FFFC, // lwz r0,-4(SP) + 0x60000000, // nop ; optionally replaced + 0x4E800420 // bctr +}; + +#define kAddressHi 3 +#define kAddressLo 5 +#define kInstructionHi 10 +#define kInstructionLo 11 + +#elif defined(__i386__) + +#define kOriginalInstructionsSize 16 + +char kIslandTemplate[] = { + // kOriginalInstructionsSize nop instructions so that we + // should have enough space to host original instructions + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + // Now the real jump instruction + 0xE9, 0xEF, 0xBE, 0xAD, 0xDE +}; + +#define kInstructions 0 +#define kJumpAddress kInstructions + kOriginalInstructionsSize + 1 +#elif defined(__x86_64__) + +#define kOriginalInstructionsSize 32 + +#define kJumpAddress kOriginalInstructionsSize + 6 + +char kIslandTemplate[] = { + // kOriginalInstructionsSize nop instructions so that we + // should have enough space to host original instructions + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + // Now the real jump instruction + 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +#endif + +#define kAllocateHigh 1 +#define kAllocateNormal 0 + +/************************** +* +* Data Types +* +**************************/ +#pragma mark - +#pragma mark (Data Types) + +typedef struct { + char instructions[sizeof(kIslandTemplate)]; + int allocatedHigh; +} BranchIsland; + +/************************** +* +* Funky Protos +* +**************************/ +#pragma mark - +#pragma mark (Funky Protos) + + mach_error_t +allocateBranchIsland( + BranchIsland **island, + int allocateHigh, + void *originalFunctionAddress); + + mach_error_t +freeBranchIsland( + BranchIsland *island ); + +#if defined(__ppc__) || defined(__POWERPC__) + mach_error_t +setBranchIslandTarget( + BranchIsland *island, + const void *branchTo, + long instruction ); +#endif + +#if defined(__i386__) || defined(__x86_64__) +mach_error_t +setBranchIslandTarget_i386( + BranchIsland *island, + const void *branchTo, + char* instructions ); +void +atomic_mov64( + uint64_t *targetAddress, + uint64_t value ); + + static Boolean +eatKnownInstructions( + unsigned char *code, + uint64_t *newInstruction, + int *howManyEaten, + char *originalInstructions ); +#endif + +/******************************************************************************* +* +* Interface +* +*******************************************************************************/ +#pragma mark - +#pragma mark (Interface) + + mach_error_t +mach_override( + char *originalFunctionSymbolName, + const char *originalFunctionLibraryNameHint, + const void *overrideFunctionAddress, + void **originalFunctionReentryIsland ) +{ + assert( originalFunctionSymbolName ); + assert( strlen( originalFunctionSymbolName ) ); + assert( overrideFunctionAddress ); + + // Lookup the original function's code pointer. + long *originalFunctionPtr; + if( originalFunctionLibraryNameHint ) + _dyld_lookup_and_bind_with_hint( + originalFunctionSymbolName, + originalFunctionLibraryNameHint, + (void*) &originalFunctionPtr, + NULL ); + else + _dyld_lookup_and_bind( + originalFunctionSymbolName, + (void*) &originalFunctionPtr, + NULL ); + + //printf ("In mach_override\n"); + return mach_override_ptr( originalFunctionPtr, overrideFunctionAddress, + originalFunctionReentryIsland ); +} + +#if defined(__x86_64__) +mach_error_t makeIslandExecutable(void *address) { + mach_error_t err = err_none; + vm_size_t pageSize; + host_page_size( mach_host_self(), &pageSize ); + uint64_t page = (uint64_t)address & ~(uint64_t)(pageSize-1); + int e = err_none; + e |= mprotect((void *)page, pageSize, PROT_EXEC | PROT_READ | PROT_WRITE); + e |= msync((void *)page, pageSize, MS_INVALIDATE ); + if (e) { + err = err_cannot_override; + } + return err; +} +#endif + + mach_error_t +mach_override_ptr( + void *originalFunctionAddress, + const void *overrideFunctionAddress, + void **originalFunctionReentryIsland ) +{ + assert( originalFunctionAddress ); + assert( overrideFunctionAddress ); + + long *originalFunctionPtr = (long*) originalFunctionAddress; + mach_error_t err = err_none; + +#if defined(__ppc__) || defined(__POWERPC__) + // Ensure first instruction isn't 'mfctr'. + #define kMFCTRMask 0xfc1fffff + #define kMFCTRInstruction 0x7c0903a6 + + long originalInstruction = *originalFunctionPtr; + if( !err && ((originalInstruction & kMFCTRMask) == kMFCTRInstruction) ) + err = err_cannot_override; +#elif defined(__i386__) || defined(__x86_64__) + int eatenCount = 0; + char originalInstructions[kOriginalInstructionsSize]; + uint64_t jumpRelativeInstruction = 0; // JMP + + Boolean overridePossible = eatKnownInstructions ((unsigned char *)originalFunctionPtr, + &jumpRelativeInstruction, &eatenCount, originalInstructions); + if (eatenCount > kOriginalInstructionsSize) { + //printf ("Too many instructions eaten\n"); + overridePossible = false; + } + if (!overridePossible) err = err_cannot_override; + if (err) printf("err = %x %d\n", err, __LINE__); +#endif + + // Make the original function implementation writable. + if( !err ) { + err = vm_protect( mach_task_self(), + (vm_address_t) originalFunctionPtr, + sizeof(long), false, (VM_PROT_ALL | VM_PROT_COPY) ); + if( err ) + err = vm_protect( mach_task_self(), + (vm_address_t) originalFunctionPtr, sizeof(long), false, + (VM_PROT_DEFAULT | VM_PROT_COPY) ); + } + if (err) printf("err = %x %d\n", err, __LINE__); + + // Allocate and target the escape island to the overriding function. + BranchIsland *escapeIsland = NULL; + if( !err ) + err = allocateBranchIsland( &escapeIsland, kAllocateHigh, originalFunctionAddress ); + if (err) printf("err = %x %d\n", err, __LINE__); + + +#if defined(__ppc__) || defined(__POWERPC__) + if( !err ) + err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 ); + + // Build the branch absolute instruction to the escape island. + long branchAbsoluteInstruction = 0; // Set to 0 just to silence warning. + if( !err ) { + long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF; + branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress; + } +#elif defined(__i386__) || defined(__x86_64__) + if (err) printf("err = %x %d\n", err, __LINE__); + + if( !err ) + err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 ); + + if (err) printf("err = %x %d\n", err, __LINE__); + // Build the jump relative instruction to the escape island +#endif + + +#if defined(__i386__) || defined(__x86_64__) + if (!err) { + uint32_t addressOffset = ((void*)escapeIsland - (void*)originalFunctionPtr - 5); + addressOffset = OSSwapInt32(addressOffset); + + jumpRelativeInstruction |= 0xE900000000000000LL; + jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24; + jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction); + } +#endif + + // Optionally allocate & return the reentry island. + BranchIsland *reentryIsland = NULL; + if( !err && originalFunctionReentryIsland ) { + err = allocateBranchIsland( &reentryIsland, kAllocateNormal, NULL); + if( !err ) + *originalFunctionReentryIsland = reentryIsland; + } + +#if defined(__ppc__) || defined(__POWERPC__) + // Atomically: + // o If the reentry island was allocated: + // o Insert the original instruction into the reentry island. + // o Target the reentry island at the 2nd instruction of the + // original function. + // o Replace the original instruction with the branch absolute. + if( !err ) { + int escapeIslandEngaged = false; + do { + if( reentryIsland ) + err = setBranchIslandTarget( reentryIsland, + (void*) (originalFunctionPtr+1), originalInstruction ); + if( !err ) { + escapeIslandEngaged = CompareAndSwap( originalInstruction, + branchAbsoluteInstruction, + (UInt32*)originalFunctionPtr ); + if( !escapeIslandEngaged ) { + // Someone replaced the instruction out from under us, + // re-read the instruction, make sure it's still not + // 'mfctr' and try again. + originalInstruction = *originalFunctionPtr; + if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction) + err = err_cannot_override; + } + } + } while( !err && !escapeIslandEngaged ); + } +#elif defined(__i386__) || defined(__x86_64__) + // Atomically: + // o If the reentry island was allocated: + // o Insert the original instructions into the reentry island. + // o Target the reentry island at the first non-replaced + // instruction of the original function. + // o Replace the original first instructions with the jump relative. + // + // Note that on i386, we do not support someone else changing the code under our feet + if ( !err ) { + if( reentryIsland ) + err = setBranchIslandTarget_i386( reentryIsland, + (void*) ((char *)originalFunctionPtr+eatenCount), originalInstructions ); + if ( !err ) + atomic_mov64((uint64_t *)originalFunctionPtr, jumpRelativeInstruction); + } +#endif + + // Clean up on error. + if( err ) { + if( reentryIsland ) + freeBranchIsland( reentryIsland ); + if( escapeIsland ) + freeBranchIsland( escapeIsland ); + } + +#if defined(__x86_64__) + err = makeIslandExecutable(escapeIsland); + err = makeIslandExecutable(reentryIsland); +#endif + + return err; +} + +/******************************************************************************* +* +* Implementation +* +*******************************************************************************/ +#pragma mark - +#pragma mark (Implementation) + +/***************************************************************************//** + Implementation: Allocates memory for a branch island. + + @param island <- The allocated island. + @param allocateHigh -> Whether to allocate the island at the end of the + address space (for use with the branch absolute + instruction). + @result <- mach_error_t + + ***************************************************************************/ + + mach_error_t +allocateBranchIsland( + BranchIsland **island, + int allocateHigh, + void *originalFunctionAddress) +{ + assert( island ); + + mach_error_t err = err_none; + + if( allocateHigh ) { + vm_size_t pageSize; + err = host_page_size( mach_host_self(), &pageSize ); + if( !err ) { + assert( sizeof( BranchIsland ) <= pageSize ); +#if defined(__x86_64__) + vm_address_t first = (uint64_t)originalFunctionAddress & ~(uint64_t)(((uint64_t)1 << 31) - 1) | ((uint64_t)1 << 31); // start in the middle of the page? + vm_address_t last = 0x0; +#else + vm_address_t first = 0xfeffffff; + vm_address_t last = 0xfe000000 + pageSize; +#endif + + vm_address_t page = first; + int allocated = 0; + vm_map_t task_self = mach_task_self(); + + while( !err && !allocated && page != last ) { + + err = vm_allocate( task_self, &page, pageSize, 0 ); + if( err == err_none ) + allocated = 1; + else if( err == KERN_NO_SPACE ) { +#if defined(__x86_64__) + page -= pageSize; +#else + page += pageSize; +#endif + err = err_none; + } + } + if( allocated ) + *island = (void*) page; + else if( !allocated && !err ) + err = KERN_NO_SPACE; + } + } else { + void *block = malloc( sizeof( BranchIsland ) ); + if( block ) + *island = block; + else + err = KERN_NO_SPACE; + } + if( !err ) + (**island).allocatedHigh = allocateHigh; + + return err; +} + +/***************************************************************************//** + Implementation: Deallocates memory for a branch island. + + @param island -> The island to deallocate. + @result <- mach_error_t + + ***************************************************************************/ + + mach_error_t +freeBranchIsland( + BranchIsland *island ) +{ + assert( island ); + assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] ); + assert( island->allocatedHigh ); + + mach_error_t err = err_none; + + if( island->allocatedHigh ) { + vm_size_t pageSize; + err = host_page_size( mach_host_self(), &pageSize ); + if( !err ) { + assert( sizeof( BranchIsland ) <= pageSize ); + err = vm_deallocate( + mach_task_self(), + (vm_address_t) island, pageSize ); + } + } else { + free( island ); + } + + return err; +} + +/***************************************************************************//** + Implementation: Sets the branch island's target, with an optional + instruction. + + @param island -> The branch island to insert target into. + @param branchTo -> The address of the target. + @param instruction -> Optional instruction to execute prior to branch. Set + to zero for nop. + @result <- mach_error_t + + ***************************************************************************/ +#if defined(__ppc__) || defined(__POWERPC__) + mach_error_t +setBranchIslandTarget( + BranchIsland *island, + const void *branchTo, + long instruction ) +{ + // Copy over the template code. + bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); + + // Fill in the address. + ((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF; + ((short*)island->instructions)[kAddressHi] + = (((long) branchTo) >> 16) & 0x0000FFFF; + + // Fill in the (optional) instuction. + if( instruction != 0 ) { + ((short*)island->instructions)[kInstructionLo] + = instruction & 0x0000FFFF; + ((short*)island->instructions)[kInstructionHi] + = (instruction >> 16) & 0x0000FFFF; + } + + //MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) ); + msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); + + return err_none; +} +#endif + +#if defined(__i386__) + mach_error_t +setBranchIslandTarget_i386( + BranchIsland *island, + const void *branchTo, + char* instructions ) +{ + + // Copy over the template code. + bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); + + // copy original instructions + if (instructions) { + bcopy (instructions, island->instructions + kInstructions, kOriginalInstructionsSize); + } + + // Fill in the address. + int32_t addressOffset = (char *)branchTo - (island->instructions + kJumpAddress + 4); + *((int32_t *)(island->instructions + kJumpAddress)) = addressOffset; + + msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); + return err_none; +} + +#elif defined(__x86_64__) +mach_error_t +setBranchIslandTarget_i386( + BranchIsland *island, + const void *branchTo, + char* instructions ) +{ + // Copy over the template code. + bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); + + // Copy original instructions. + if (instructions) { + bcopy (instructions, island->instructions, kOriginalInstructionsSize); + } + + // Fill in the address. + *((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo; + msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); + + return err_none; +} +#endif + + +#if defined(__i386__) || defined(__x86_64__) +// simplistic instruction matching +typedef struct { + unsigned int length; // max 15 + unsigned char mask[15]; // sequence of bytes in memory order + unsigned char constraint[15]; // sequence of bytes in memory order +} AsmInstructionMatch; + +#if defined(__i386__) +static AsmInstructionMatch possibleInstructions[] = { + { 0x1, {0xFF}, {0x90} }, // nop + { 0x1, {0xFF}, {0x55} }, // push %esp + { 0x2, {0xFF, 0xFF}, {0x89, 0xE5} }, // mov %esp,%ebp + { 0x1, {0xFF}, {0x53} }, // push %ebx + { 0x3, {0xFF, 0xFF, 0x00}, {0x83, 0xEC, 0x00} }, // sub 0x??, %esp + { 0x1, {0xFF}, {0x57} }, // push %edi + { 0x1, {0xFF}, {0x56} }, // push %esi + { 0x0 } +}; +#elif defined(__x86_64__) +static AsmInstructionMatch possibleInstructions[] = { + { 0x1, {0xFF}, {0x90} }, // nop + { 0x1, {0xF8}, {0x50} }, // push %rX + { 0x3, {0xFF, 0xFF, 0xFF}, {0x48, 0x89, 0xE5} }, // mov %rsp,%rbp + { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xEC, 0x00} }, // sub 0x??, %rsp + { 0x4, {0xFB, 0xFF, 0x00, 0x00}, {0x48, 0x89, 0x00, 0x00} }, // move onto rbp + { 0x2, {0xFF, 0x00}, {0x41, 0x00} }, // push %rXX + { 0x2, {0xFF, 0x00}, {0x85, 0x00} }, // test %rX,%rX + { 0x0 } +}; +#endif + +static Boolean codeMatchesInstruction(unsigned char *code, AsmInstructionMatch* instruction) +{ + Boolean match = true; + + int i; + for (i=0; i<instruction->length; i++) { + unsigned char mask = instruction->mask[i]; + unsigned char constraint = instruction->constraint[i]; + unsigned char codeValue = code[i]; + + match = ((codeValue & mask) == constraint); + if (!match) break; + } + + return match; +} + +#if defined(__i386__) || defined(__x86_64__) + static Boolean +eatKnownInstructions( + unsigned char *code, + uint64_t* newInstruction, + int* howManyEaten, + char* originalInstructions ) +{ + Boolean allInstructionsKnown = true; + int totalEaten = 0; + unsigned char* ptr = code; + int remainsToEat = 5; // a JMP instruction takes 5 bytes + + if (howManyEaten) *howManyEaten = 0; + while (remainsToEat > 0) { + Boolean curInstructionKnown = false; + + // See if instruction matches one we know + AsmInstructionMatch* curInstr = possibleInstructions; + do { + if (curInstructionKnown = codeMatchesInstruction(ptr, curInstr)) break; + curInstr++; + } while (curInstr->length > 0); + + // if all instruction matches failed, we don't know current instruction then, stop here + if (!curInstructionKnown) { + allInstructionsKnown = false; + break; + } + + // At this point, we've matched curInstr + int eaten = curInstr->length; + ptr += eaten; + remainsToEat -= eaten; + totalEaten += eaten; + } + + + if (howManyEaten) *howManyEaten = totalEaten; + + if (originalInstructions) { + Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize); + + if (enoughSpaceForOriginalInstructions) { + memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP + bcopy(code, originalInstructions, totalEaten); + } else { + // printf ("Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n"); + return false; + } + } + + if (allInstructionsKnown) { + // save last 3 bytes of first 64bits of codre we'll replace + uint64_t currentFirst64BitsOfCode = *((uint64_t *)code); + currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation + currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL; + + // keep only last 3 instructions bytes, first 5 will be replaced by JMP instr + *newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes + *newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes + } + + return allInstructionsKnown; +} +#endif + +#if defined(__i386__) +asm( + ".text;" + ".align 2, 0x90;" + ".globl _atomic_mov64;" + "_atomic_mov64:;" + " pushl %ebp;" + " movl %esp, %ebp;" + " pushl %esi;" + " pushl %ebx;" + " pushl %ecx;" + " pushl %eax;" + " pushl %edx;" + + // atomic push of value to an address + // we use cmpxchg8b, which compares content of an address with + // edx:eax. If they are equal, it atomically puts 64bit value + // ecx:ebx in address. + // We thus put contents of address in edx:eax to force ecx:ebx + // in address + " mov 8(%ebp), %esi;" // esi contains target address + " mov 12(%ebp), %ebx;" + " mov 16(%ebp), %ecx;" // ecx:ebx now contains value to put in target address + " mov (%esi), %eax;" + " mov 4(%esi), %edx;" // edx:eax now contains value currently contained in target address + " lock; cmpxchg8b (%esi);" // atomic move. + + // restore registers + " popl %edx;" + " popl %eax;" + " popl %ecx;" + " popl %ebx;" + " popl %esi;" + " popl %ebp;" + " ret" +); +#elif defined(__x86_64__) +void atomic_mov64( + uint64_t *targetAddress, + uint64_t value ) +{ + *targetAddress = value; +} +#endif +#endif
\ No newline at end of file diff --git a/macx/overlay/mach_override.h b/macx/overlay/mach_override.h new file mode 100644 index 000000000..685f1deb1 --- /dev/null +++ b/macx/overlay/mach_override.h @@ -0,0 +1,157 @@ +/******************************************************************************* + mach_override.h + Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com> + Some rights reserved: <http://opensource.org/licenses/mit-license.php> + + ***************************************************************************/ + +/***************************************************************************//** + @mainpage mach_override + @author Jonathan 'Wolf' Rentzsch: <http://rentzsch.com> + + This package, coded in C to the Mach API, allows you to override ("patch") + program- and system-supplied functions at runtime. You can fully replace + functions with your implementations, or merely head- or tail-patch the + original implementations. + + Use it by #include'ing mach_override.h from your .c, .m or .mm file(s). + + @todo Discontinue use of Carbon's MakeDataExecutable() and + CompareAndSwap() calls and start using the Mach equivalents, if they + exist. If they don't, write them and roll them in. That way, this + code will be pure Mach, which will make it easier to use everywhere. + Update: MakeDataExecutable() has been replaced by + msync(MS_INVALIDATE). There is an OSCompareAndSwap in libkern, but + I'm currently unsure if I can link against it. May have to roll in + my own version... + @todo Stop using an entire 4K high-allocated VM page per 28-byte escape + branch island. Done right, this will dramatically speed up escape + island allocations when they number over 250. Then again, if you're + overriding more than 250 functions, maybe speed isn't your main + concern... + @todo Add detection of: b, bl, bla, bc, bcl, bcla, bcctrl, bclrl + first-instructions. Initially, we should refuse to override + functions beginning with these instructions. Eventually, we should + dynamically rewrite them to make them position-independent. + @todo Write mach_unoverride(), which would remove an override placed on a + function. Must be multiple-override aware, which means an almost + complete rewrite under the covers, because the target address can't + be spread across two load instructions like it is now since it will + need to be atomically updatable. + @todo Add non-rentry variants of overrides to test_mach_override. + + ***************************************************************************/ + +#ifndef _mach_override_ +#define _mach_override_ + +#include <sys/types.h> +#include <mach/error.h> + +#ifdef __cplusplus +extern "C" { +#endif + + /** + Returned if the function to be overrided begins with a 'mfctr' instruction. + */ +#define err_cannot_override (err_local|1) + + /***************************************************************************//** + Dynamically overrides the function implementation referenced by + originalFunctionSymbolName with the implentation pointed to by + overrideFunctionAddress. Optionally returns a pointer to a "reentry island" + which, if jumped to, will resume the original implementation. + + @param originalFunctionSymbolName -> Required symbol name of the + function to override (with + overrideFunctionAddress). + Remember, C function name + symbols are prepended with an + underscore. + @param originalFunctionLibraryNameHint -> Optional name of the library + which contains + originalFunctionSymbolName. Can + be NULL, but this may result in + the wrong function being + overridden and/or a crash. + @param overrideFunctionAddress -> Required address to the + overriding function. + @param originalFunctionReentryIsland <- Optional pointer to pointer to + the reentry island. Can be NULL. + @result <- err_cannot_override if the + original function's + implementation begins with the + 'mfctr' instruction. + + ***************************************************************************/ + + mach_error_t + mach_override( + char *originalFunctionSymbolName, + const char *originalFunctionLibraryNameHint, + const void *overrideFunctionAddress, + void **originalFunctionReentryIsland); + + /************************************************************************************//** + Dynamically overrides the function implementation referenced by + originalFunctionAddress with the implentation pointed to by overrideFunctionAddress. + Optionally returns a pointer to a "reentry island" which, if jumped to, will resume + the original implementation. + + @param originalFunctionAddress -> Required address of the function to + override (with overrideFunctionAddress). + @param overrideFunctionAddress -> Required address to the overriding + function. + @param originalFunctionReentryIsland <- Optional pointer to pointer to the + reentry island. Can be NULL. + @result <- err_cannot_override if the original + function's implementation begins with + the 'mfctr' instruction. + + ************************************************************************************/ + + mach_error_t + mach_override_ptr( + void *originalFunctionAddress, + const void *overrideFunctionAddress, + void **originalFunctionReentryIsland); + + /************************************************************************************//** + + + ************************************************************************************/ + +#ifdef __cplusplus + +#define MACH_OVERRIDE( ORIGINAL_FUNCTION_RETURN_TYPE, ORIGINAL_FUNCTION_NAME, ORIGINAL_FUNCTION_ARGS, ERR ) \ + { \ + static ORIGINAL_FUNCTION_RETURN_TYPE (*ORIGINAL_FUNCTION_NAME##_reenter)ORIGINAL_FUNCTION_ARGS; \ + static bool ORIGINAL_FUNCTION_NAME##_overriden = false; \ + class mach_override_class__##ORIGINAL_FUNCTION_NAME { \ + public: \ + static kern_return_t override(void *originalFunctionPtr) { \ + kern_return_t result = err_none; \ + if (!ORIGINAL_FUNCTION_NAME##_overriden) { \ + ORIGINAL_FUNCTION_NAME##_overriden = true; \ + result = mach_override_ptr( (void*)originalFunctionPtr, \ + (void*)mach_override_class__##ORIGINAL_FUNCTION_NAME::replacement, \ + (void**)&ORIGINAL_FUNCTION_NAME##_reenter ); \ + } \ + return result; \ + } \ + static ORIGINAL_FUNCTION_RETURN_TYPE replacement ORIGINAL_FUNCTION_ARGS { + +#define END_MACH_OVERRIDE( ORIGINAL_FUNCTION_NAME ) \ + } \ + }; \ + \ + err = mach_override_class__##ORIGINAL_FUNCTION_NAME::override((void*)ORIGINAL_FUNCTION_NAME); \ + } + +#endif + +#ifdef __cplusplus +} +#endif +#endif // _mach_override_
\ No newline at end of file diff --git a/macx/overlay/overlay.m b/macx/overlay/overlay.m new file mode 100644 index 000000000..216979def --- /dev/null +++ b/macx/overlay/overlay.m @@ -0,0 +1,587 @@ +/* Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com> + Copyright (C) 2008-2009, Mikkel Krautz <mikkel@krautz.dk> + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <wchar.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/ipc.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/stat.h> +#include <time.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <mach/mach_init.h> +#include <mach/task.h> +#include <dlfcn.h> +#include <pwd.h> + +#include <OpenGL/OpenGL.h> +#include <Carbon/Carbon.h> +#include <Cocoa/Cocoa.h> +#include <AGL/AGL.h> + +#include "mach_override.h" +#include <objc/objc-runtime.h> + +#include "../../overlay/overlay.h" + +static bool bDebug = false; +static bool vendorIntel = false; + +typedef struct _Context { + struct _Context *next; + CGLContextObj cglctx; + GLuint uiProgram; + + unsigned int uiWidth, uiHeight; + unsigned int uiLeft, uiRight, uiTop, uiBottom; + + struct sockaddr_un saName; + int iSocket; + struct OverlayMsg omMsg; + GLuint texture; + + unsigned char *a_ucTexture; + unsigned int uiMappedLength; +} Context; + +static const char vshader[] = "" + "void main() {" + "gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;" + "gl_TexCoord[0] = gl_MultiTexCoord0;" + "}"; + +static const char fshader[] = "" + "uniform sampler2D tex;" + "void main() {" + "gl_FragColor = texture2D(tex, gl_TexCoord[0].st);" + "}"; + +const GLfloat fBorder[] = {0.125f, 0.250f, 0.5f, 0.75f}; + +static Context *contexts = NULL; + +#define FDEF(name) static __typeof__(&name) o##name = NULL +FDEF(CGLFlushDrawable); + +__attribute__((format(printf, 1, 2))) +static void ods(const char *format, ...) { + if (!bDebug) { + return; + } + + fprintf(stderr, "MumbleOverlay: "); + + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); + fflush(stderr); +} + +static void newContext(Context * ctx) { + ctx->iSocket = -1; + ctx->omMsg.omh.iLength = -1; + ctx->texture = ~0; + + char *home = getenv("HOME"); + if (home == NULL) { + struct passwd *pwent= getpwuid(getuid()); + if (pwent && pwent->pw_dir && pwent->pw_dir[0]) + home = pwent->pw_dir; + } + + if (home) { + ctx->saName.sun_family = PF_UNIX; + strcpy(ctx->saName.sun_path, home); + strcat(ctx->saName.sun_path, "/.MumbleOverlayPipe"); + } + + ods("OpenGL Version %s, Vendor %s, Renderer %s, Shader %s", glGetString(GL_VERSION), glGetString(GL_VENDOR), glGetString(GL_RENDERER), glGetString(GL_SHADING_LANGUAGE_VERSION)); + + if (!strcmp(glGetString(GL_VENDOR), "Intel Inc.")) { + ods("Enabling Intel specific hacks"); + vendorIntel = true; + } + + const char *vsource = vshader; + const char *fsource = fshader; + char buffer[8192]; + GLint l; + GLuint vs = glCreateShader(GL_VERTEX_SHADER); + GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(vs, 1, &vsource, NULL); + glShaderSource(fs, 1, &fsource, NULL); + glCompileShader(vs); + glCompileShader(fs); + glGetShaderInfoLog(vs, 8192, &l, buffer); + ods("VERTEX: %s", buffer); + glGetShaderInfoLog(fs, 8192, &l, buffer); + ods("FRAGMENT: %s", buffer); + ctx->uiProgram = glCreateProgram(); + glAttachShader(ctx->uiProgram, vs); + glAttachShader(ctx->uiProgram, fs); + glLinkProgram(ctx->uiProgram); +} + +static void releaseMem(Context *ctx) { + if (ctx->a_ucTexture) { + munmap(ctx->a_ucTexture, ctx->uiMappedLength); + ctx->a_ucTexture = NULL; + ctx->uiMappedLength = 0; + } + if (ctx->texture != ~0) { + glDeleteTextures(1, &ctx->texture); + ctx->texture = ~0; + } + ctx->uiLeft = ctx->uiTop = ctx->uiRight = ctx->uiBottom = 0; +} + +static void disconnect(Context *ctx) { + releaseMem(ctx); + ctx->uiWidth = ctx->uiHeight = 0; + if (ctx->iSocket != -1) { + close(ctx->iSocket); + ctx->iSocket = -1; + } + ods("Disconnected"); +} + +static bool sendMessage(Context *ctx, struct OverlayMsg *om) { + if (ctx->iSocket != -1) { + ssize_t wantsend = sizeof(struct OverlayMsgHeader) + om->omh.iLength; + ssize_t sent = send(ctx->iSocket, om, wantsend, MSG_DONTWAIT); + if (wantsend == sent) + return true; + ods("Short write"); + } + disconnect(ctx); + return false; +} + +static void regenTexture(Context *ctx) { + if (ctx->texture != ~0) + glDeleteTextures(1, & ctx->texture); + glGenTextures(1, &ctx->texture); + + glBindTexture(GL_TEXTURE_2D, ctx->texture); + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, fBorder); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ctx->uiWidth, ctx->uiHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, ctx->a_ucTexture); +} + +static void drawOverlay(Context *ctx, unsigned int width, unsigned int height) { + if (ctx->iSocket == -1) { + releaseMem(ctx); + if (! ctx->saName.sun_path[0]) + return; + ctx->iSocket = socket(AF_UNIX, SOCK_STREAM, 0); + if (ctx->iSocket == -1) { + ods("socket() failure"); + return; + } + fcntl(ctx->iSocket, F_SETFL, O_NONBLOCK, 1); + if (connect(ctx->iSocket, (struct sockaddr *)(& ctx->saName), sizeof(ctx->saName)) != 0) { + close(ctx->iSocket); + ctx->iSocket = -1; + ods("connect() failure %s", ctx->saName.sun_path); + return; + } + ods("Connected"); + } + + if ((ctx->uiWidth != width) || (ctx->uiHeight != height)) { + ods("Sent init"); + releaseMem(ctx); + + ctx->uiWidth = width; + ctx->uiHeight = height; + + struct OverlayMsg om; + om.omh.uiMagic = OVERLAY_MAGIC_NUMBER; + om.omh.uiType = OVERLAY_MSGTYPE_INIT; + om.omh.iLength = sizeof(struct OverlayMsgInit); + om.omi.uiWidth = ctx->uiWidth; + om.omi.uiHeight = ctx->uiHeight; + + if (! sendMessage(ctx, &om)) + return; + } + + while (1) { + if (ctx->omMsg.omh.iLength == -1) { + ssize_t length = recv(ctx->iSocket, ctx->omMsg.headerbuffer, sizeof(struct OverlayMsgHeader), 0); + if (length < 0) { + if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) + break; + disconnect(ctx); + return; + } else if (length != sizeof(struct OverlayMsgHeader)) { + ods("Short header read"); + disconnect(ctx); + return; + } + } else { + ssize_t length = recv(ctx->iSocket, ctx->omMsg.msgbuffer, ctx->omMsg.omh.iLength, 0); + if (length < 0) { + if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) + break; + disconnect(ctx); + return; + } else if (length != ctx->omMsg.omh.iLength) { + ods("Short message read %x %d/%d", ctx->omMsg.omh.uiType, length, ctx->omMsg.omh.iLength); + disconnect(ctx); + return; + } + ctx->omMsg.omh.iLength = -1; + + switch (ctx->omMsg.omh.uiType) { + case OVERLAY_MSGTYPE_SHMEM: { + struct OverlayMsgShmem *oms = & ctx->omMsg.omi; + ods("SHMEM %s", oms->a_cName); + releaseMem(ctx); + int fd = shm_open(oms->a_cName, O_RDONLY, 0600); + if (fd != -1) { + struct stat buf; + fstat(fd, &buf); + if (buf.st_size >= ctx->uiWidth * ctx->uiHeight * 4) { + ctx->uiMappedLength = buf.st_size; + ctx->a_ucTexture = mmap(NULL, buf.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (ctx->a_ucTexture != (unsigned char *) -1) { + regenTexture(ctx); + continue; + } + ctx->a_ucTexture = NULL; + } + ctx->uiMappedLength = 0; + close(fd); + } + ods("Failed to map memory"); + } + break; + case OVERLAY_MSGTYPE_BLIT: { + struct OverlayMsgBlit *omb = & ctx->omMsg.omb; + ods("BLIT %d %d %d %d", omb->x, omb->y, omb->w, omb->h); + if ((ctx->a_ucTexture != NULL) && (ctx->texture != ~0)) { + glBindTexture(GL_TEXTURE_2D, ctx->texture); + + if ((omb->x == 0) && (omb->y == 0) && (omb->w == ctx->uiWidth) && (omb->h == ctx->uiHeight)) { + ods("Optimzied fullscreen blit"); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ctx->uiWidth, ctx->uiHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, ctx->a_ucTexture); + } else { + unsigned int x = omb->x; + unsigned int y = omb->y; + unsigned int w = omb->w; + unsigned int h = omb->h; + unsigned char *ptr = (unsigned char *) malloc(w*h*4); + int r; + memset(ptr, 0, w * h * 4); + + for (r = 0; r < h; ++r) { + const unsigned char *sptr = ctx->a_ucTexture + 4 * ((y+r) * ctx->uiWidth + x); + unsigned char *dptr = ptr + 4 * w * r; + memcpy(dptr, sptr, w * 4); + } + + glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_BGRA, GL_UNSIGNED_BYTE, ptr); + free(ptr); + } + } + } + break; + case OVERLAY_MSGTYPE_ACTIVE: { + struct OverlayMsgActive *oma = & ctx->omMsg.oma; + ods("ACTIVE %d %d %d %d", oma->x, oma->y, oma->w, oma->h); + ctx->uiLeft = oma->x; + ctx->uiTop = oma->y; + ctx->uiRight = oma->x + oma->w; + ctx->uiBottom = oma->y + oma->h; + } + break; + default: + break; + } + } + } + + if ((ctx->a_ucTexture == NULL) || (ctx->texture == ~0)) + return; + + if (! glIsTexture(ctx->texture)) { + ctx->texture = ~0; + ods("Lost texture"); + regenTexture(ctx); + } else { + glBindTexture(GL_TEXTURE_2D, ctx->texture); + GLfloat bordercolor[4]; + glGetTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bordercolor); + if (bordercolor[0] != fBorder[0] || bordercolor[1] != fBorder[1] || bordercolor[2] != fBorder[2] || bordercolor[3] != fBorder[3]) { + ods("Texture hijacked"); + regenTexture(ctx); + } + } + + glBindTexture(GL_TEXTURE_2D, ctx->texture); + glPushMatrix(); + + float w = (float)(ctx->uiWidth); + float h = (float)(ctx->uiHeight); + + float left = (float)(ctx->uiLeft); + float top = (float)(ctx->uiTop); + float right = (float)(ctx->uiRight); + float bottom = (float)(ctx->uiBottom); + + float xm = (left) / w; + float ym = (top) / h; + float xmx = (right) / w; + float ymx = (bottom) / h; + + GLfloat vertex[] = {left, bottom, + left, top, + right, top, + right, bottom}; + GLfloat tex[] = {xm, ymx, xm, ym, xmx, ym, xmx, ymx}; + + if (! vendorIntel) { + glVertexPointer(2, GL_FLOAT, 0, vertex); + glTexCoordPointer(2, GL_FLOAT, 0, tex); + glDrawArrays(GL_QUADS, 0, 4); + } else { + glBegin(GL_QUADS); + glTexCoord2f(tex[0], tex[1]); + glVertex2f(vertex[0], vertex[1]); + glTexCoord2f(tex[2], tex[3]); + glVertex2f(vertex[2], vertex[3]); + glTexCoord2f(tex[4], tex[5]); + glVertex2f(vertex[4], vertex[5]); + glTexCoord2f(tex[6], tex[7]); + glVertex2f(vertex[6], vertex[7]); + glEnd(); + } + + glPopMatrix(); +} + +static void drawContext(Context * ctx, int width, int height) { + GLint program; + GLint viewport[4]; + int i; + + glPushAttrib(GL_ALL_ATTRIB_BITS); + glPushClientAttrib(GL_ALL_ATTRIB_BITS); + glGetIntegerv(GL_VIEWPORT, viewport); + glGetIntegerv(GL_CURRENT_PROGRAM, &program); + + glViewport(0, 0, width, height); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0, width, height, 0, -100.0, 100.0); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + glLoadIdentity(); + + + glDisable(GL_ALPHA_TEST); + glDisable(GL_AUTO_NORMAL); + // Skip clip planes, there are thousands of them. + glDisable(GL_COLOR_LOGIC_OP); + glDisable(GL_COLOR_TABLE); + glDisable(GL_CONVOLUTION_1D); + glDisable(GL_CONVOLUTION_2D); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_DITHER); + glDisable(GL_FOG); + glDisable(GL_HISTOGRAM); + glDisable(GL_INDEX_LOGIC_OP); + glDisable(GL_LIGHTING); + glDisable(GL_NORMALIZE); + // Skip line smmooth + // Skip map + glDisable(GL_MINMAX); + // Skip polygon offset + glDisable(GL_SEPARABLE_2D); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_STENCIL_TEST); + glDisable(GL_TEXTURE_GEN_Q); + glDisable(GL_TEXTURE_GEN_R); + glDisable(GL_TEXTURE_GEN_S); + glDisable(GL_TEXTURE_GEN_T); + + glRenderMode(GL_RENDER); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_INDEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_EDGE_FLAG_ARRAY); + + glPixelStorei(GL_UNPACK_SWAP_BYTES, 0); + glPixelStorei(GL_UNPACK_LSB_FIRST, 0); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + GLint texunits = 1; + + glGetIntegerv(GL_MAX_TEXTURE_UNITS, &texunits); + + for (i=texunits-1;i>=0;--i) { + glActiveTexture(GL_TEXTURE0 + i); + glDisable(GL_TEXTURE_1D); + glDisable(GL_TEXTURE_2D); + glDisable(GL_TEXTURE_3D); + } + + glDisable(GL_TEXTURE_CUBE_MAP); + glDisable(GL_VERTEX_PROGRAM_ARB); + glDisable(GL_FRAGMENT_PROGRAM_ARB); + + glUseProgram(ctx->uiProgram); + + glEnable(GL_COLOR_MATERIAL); + glEnable(GL_TEXTURE_2D); + glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glMatrixMode(GL_MODELVIEW); + + GLint uni = glGetUniformLocation(ctx->uiProgram, "tex"); + glUniform1i(uni, 0); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + int bound = 0; + glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &bound); + + if (bound != 0) + glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0); + + drawOverlay(ctx, width, height); + + if (bound != 0) + glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, bound); + + glMatrixMode(GL_TEXTURE); + glPopMatrix(); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + glPopClientAttrib(); + glPopAttrib(); + glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); + glUseProgram(program); + + while (glGetError() != GL_NO_ERROR); +} + +void CGLFlushDrawableOverride(CGLContextObj ctx) { + ods("CGLFlushDrawable()"); + + Context *c = contexts; + + while (c) { + if (c->cglctx == ctx) + break; + c = c->next; + } + + if (!c) { + ods("Current context is: %p", ctx); + + c = malloc(sizeof(Context)); + if (!c) { + ods("malloc failure"); + return; + } + memset(c, 0, sizeof(Context)); + c->next = contexts; + c->cglctx = ctx; + contexts = c; + newContext(c); + } + + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + ods("%i, %i", viewport[0], viewport[1]); + int width = viewport[2]; + int height = viewport[3]; + drawContext(c, width, height); + + oCGLFlushDrawable(ctx); +} + + +__attribute__((constructor)) +static void entryPoint() { + if (getenv("MUMBLE_OVERLAY_DEBUG")) + bDebug = true; + else + bDebug = false; + + void *cglAvailable = dlsym(RTLD_DEFAULT, "CGLFlushDrawable"); + if (cglAvailable) { + ods("Attempting to hook CGL"); + if (mach_override("_CGLFlushDrawable", NULL, CGLFlushDrawableOverride, (void **) &oCGLFlushDrawable) != 0) { + ods("CGLFlushDrawable override failed."); + } + } else { + ods("Unable to hook CGL"); + return; + } + + ods("Up running."); +} diff --git a/macx/overlay/overlay.plist b/macx/overlay/overlay.plist new file mode 100644 index 000000000..fd75e9336 --- /dev/null +++ b/macx/overlay/overlay.plist @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd"> +<plist version="0.9"> +<dict> + <key>CFBundlePackageType</key> + <string>FMWK</string> + <key>CFBundleSignature</key> + <string>OLAY</string> + <key>CFBundleExecutable</key> + <string>MumbleOverlay</string> +</dict> +</plist> + diff --git a/macx/overlay/overlay.pro b/macx/overlay/overlay.pro new file mode 100644 index 000000000..594813692 --- /dev/null +++ b/macx/overlay/overlay.pro @@ -0,0 +1,28 @@ +# Overlay payload for Mac OS X. + +include(../../compiler.pri) + +CONFIG += x86_64 i386 ppc + +TEMPLATE = lib +CONFIG += plugin +CONFIG -= gui qt + +TARGET = mumbleoverlay + +QMAKE_LFLAGS_PLUGIN += -undefined dynamic_lookup -dynamic +QMAKE_LFLAGS = -framework CoreFoundation + +SOURCES = mach_override.c overlay.m +HEADERS = mach_override.h +DIST = overlay.plist + +CONFIG(debug, debug|release) { + DESTDIR = ../../debug +} + +CONFIG(release, debug|release) { + DESTDIR = ../../release +} + +include(../../symbols.pri) |