source: trunk/Cocoa/F-Script Anywhere/Source/SCPatch/SCPatchController/SCPatchLoader.c @ 342

Last change on this file since 342 was 342, checked in by rchin, 13 years ago

Leopard compatibility changes:

  • Removed some icon caching code that was causing crashes (not sure we really needed those optimizations anyway -- doesn't appear to affect performance)
  • Added code to automatically try to add certificate to keychain, for new code signing behavior (replaces previous procmod nonesense).
  • Note that the enclosed public certificate is mine, and so it will need to be signed by me. In the case that someone else wants to distribute this binary, please replace Certficiate.cer with your own public certificate, and then make sure o code sign the binary after it is built.
File size: 8.2 KB
Line 
1#include <syslog.h>
2#include <pthread.h>
3#include <mach/thread_act.h>
4#include <mach-o/dyld.h>
5#include "mach_inject.h"
6#include "SCPatchPrivate.h"
7#include "SCPatchCommon.h"
8#include "SCPatchMessages.h"
9
10#undef USE_CFBUNDLE
11#undef USE_CFRUNLOOP
12#undef CHECK_SYMBOL_REFERENCES
13#undef SCPL_DEBUG_LOG
14
15#ifdef SCPL_DEBUG_LOG
16        #define SCPLLog(...) syslog(LOG_ALERT, "SCPL: "__VA_ARGS__);
17#else
18        #define SCPLLog(...) ;
19#endif
20
21//-------------------------------------------------------------------------------------------------------------
22static void SendError(HFSUniStr255 *uniBundleID, HFSUniStr255 *uniControllerBundleID, mach_error_t err)
23{
24        SInt32                          result = kCFMessagePortTransportError;
25        CFStringRef                     clientBundleID, controllerBundleID;
26        OSType                          sig = 'SCPM';
27        long                            descSize;
28        Ptr                                     descData;
29        CFDataRef                       dataRef;
30        CFMessagePortRef        portRef;
31        AEDesc                          desc;
32
33       
34        if((clientBundleID = CFStringCreateWithCharacters(NULL, uniBundleID->unicode, uniBundleID->length)) != NULL &&
35           (controllerBundleID = CFStringCreateWithCharacters(NULL, uniControllerBundleID->unicode, uniControllerBundleID->length)) != NULL &&
36           AEBuildAppleEvent(kSCMessageClass, kSCLoadResult, typeApplSignature, &sig, sizeof(sig),
37                                                 kAutoGenerateReturnID, kAnyTransactionID, &desc, NULL, "bndl:TEXT(@), err :long(@)", 
38                                                 CFStringGetCStringPtr(clientBundleID, kCFStringEncodingMacRoman), err) == noErr &&
39           (descSize = AEGetDescDataSize(&desc)) > 0 && (descData = NewPtr(descSize)) != NULL &&
40       AEGetDescData(&desc, descData, descSize) == noErr &&
41           (dataRef = CFDataCreate(NULL, (unsigned char*)descData, descSize)) != NULL)
42        {
43           if((portRef = CFMessagePortCreateRemote(NULL, controllerBundleID)) != NULL)
44                {
45                        result = CFMessagePortSendRequest(portRef, kSCMessageClass, dataRef, 5, 0, NULL, NULL);
46                        CFRelease(portRef);
47                }
48                CFRelease(dataRef);
49                CFRelease(clientBundleID);
50                CFRelease(controllerBundleID);
51                DisposePtr(descData);
52        }
53        else
54        {
55                syslog(LOG_ALERT, "SCPL: unable to send notification of patch loading error %d\n", err);
56        }
57}
58
59//-------------------------------------------------------------------------------------------------------------
60// Yes, this is ugly, but it avoids runtime dyld errors caused by loading Cocoa code into a Carbon-only app,
61// and other such messes.
62static Boolean CanLoadImage(NSObjectFileImage image)
63{
64#ifdef CHECK_SYMBOL_REFERENCES
65        UInt32  ii, symbolCount = NSSymbolReferenceCountInObjectFileImage(image);
66       
67        for(ii = 0; ii < symbolCount; ii++)
68        {
69                if(!NSIsSymbolNameDefined(NSSymbolReferenceNameInObjectFileImage(image, ii, NULL)))
70                {
71                        SCPLLog("failed to find %s\n", NSSymbolReferenceNameInObjectFileImage(image, ii, NULL));
72                        return false;
73                }
74        }
75#endif
76        return true;
77}
78
79//-------------------------------------------------------------------------------------------------------------
80#ifdef USE_CFRUNLOOP
81        void SCPatchObserver(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *paramBlock)
82#else
83        pascal void SCPatchTimerHandler(EventLoopTimerRef inTimer, void *paramBlock)
84#endif
85{
86        SCPatchLoaderParams             *params = (SCPatchLoaderParams *)paramBlock;
87        mach_error_t                    err = err_none;
88        short                                   ii;
89       
90        SCPLLog("loading patches in thread %x\n", pthread_self());
91
92#ifdef USE_CFRUNLOOP
93        CFRunLoopObserverInvalidate(observer);
94#endif
95       
96        for(ii = 0; ii < params->patchCount; ii++)
97        {
98                SCPatchParams                   patchParams;
99                SCPatchEntry                    patchCode = NULL;
100                HFSUniStr255                    *bundleID = SCPatchGetHFSUniStrPointer(params, ii);
101                char                                    *urlData = SCPatchGetStringPointer(params, ii);
102
103#ifdef USE_CFBUNDLE
104                CFURLRef                                url = NULL;
105                CFBundleRef                             bundle = NULL;
106                CFStringRef                             urlString = NULL;
107               
108                if((urlString = CFStringCreateWithCString(NULL, urlData, kCFStringEncodingUTF8)) != NULL &&
109                   (url = CFURLCreateWithString(NULL, urlString, NULL)) != NULL &&
110                   (bundle = CFBundleCreate(NULL, url)) != NULL &&
111                   CFBundleLoadExecutable(bundle))
112                {
113                        if((patchCode = (SCPatchEntry)CFBundleGetFunctionPointerForName(bundle, CFSTR("SCPatchInit"))) == NULL)
114                                patchCode = (SCPatchEntry)CFBundleGetFunctionPointerForName(bundle, CFSTR("_Z11SCPatchInitP13SCPatchParams"));
115                        if(patchCode)
116                                err = err_none;
117                        else
118                                err = err_couldnt_find_injectedThread_symbol;
119                }
120                else
121                {
122                        err = err_couldnt_load_injection_bundle;
123                }
124#else
125                NSObjectFileImage               image;
126                NSModule                                module;
127                NSSymbol                                symbol;
128
129                if(NSCreateObjectFileImageFromFile(urlData, &image) == NSObjectFileImageSuccess && CanLoadImage(image) &&
130                   (module = NSLinkModule(image, urlData, NSLINKMODULE_OPTION_BINDNOW | NSLINKMODULE_OPTION_PRIVATE | NSLINKMODULE_OPTION_RETURN_ON_ERROR)) != NULL)
131                {
132                        if((symbol = NSLookupSymbolInModule(module, "_SCPatchInit")) == NULL)
133                                symbol = NSLookupSymbolInModule(module, "__Z11SCPatchInitP13SCPatchParams");
134                        if(symbol)
135                                patchCode = (SCPatchEntry)NSAddressOfSymbol(symbol);
136                        if(patchCode)
137                                err = err_none;
138                        else
139                                err = err_couldnt_find_injectedThread_symbol;
140                }
141                else
142                {
143                        err = err_couldnt_load_injection_bundle;
144                }
145#endif
146               
147                // Run the startup code
148                if(!err)
149                {
150                        // Make copies of the params for the patch code
151                        patchParams.version = 0;
152                        patchParams.parent = params->parent;
153                        patchParams.parentBundleID = params->parentBundleID;
154                        patchParams.patchBundleID = *bundleID;
155                        // CFURLGetFSRef(url, &patchParams.patchRef);
156                        SCPLLog("calling patch init code\n");
157                        err = patchCode(&patchParams);
158                }
159                else
160                {
161                        char    *name = rindex(urlData, '/');
162                        SCPLLog("error 0x%x loading patch \"%s\"\n", err, name ? name + 1 : urlData);
163                }
164#ifdef USE_CFBUNDLE
165                if(url)
166                        CFRelease(url);
167                if(urlString);
168                        CFRelease(urlString);
169#endif
170                SendError(bundleID, &params->parentBundleID, err);
171        }
172}
173
174//-------------------------------------------------------------------------------------------------------------
175void *SCPatchThreadEntry( void *paramBlock )
176{
177        // Now that we've got a pthread, we have to hop onto the main event loop to do our code loading so that
178        // we don't screw up dyld or Cocoa apps that don't have multithreading turned on.
179#ifdef USE_CFRUNLOOP
180        CFRunLoopObserverContext        context = { 0 };
181        CFRunLoopObserverRef            observer;
182        CFRunLoopRef                            runLoop;
183       
184        if((runLoop = (CFRunLoopRef)GetCFRunLoopFromEventLoop(GetMainEventLoop())) != NULL)
185        {
186                context.info = paramBlock;
187                observer = CFRunLoopObserverCreate (kCFAllocatorDefault, kCFRunLoopAllActivities, false, 0, 
188                                            SCPatchObserver + ((SCPatchLoaderParams *)paramBlock)->codeOffset,
189                                            &context);
190                CFRunLoopAddObserver(runLoop, observer, kCFRunLoopCommonModes);
191        }
192#else
193        InstallEventLoopTimer(GetMainEventLoop(), 0, 0, 
194                          NewEventLoopTimerUPP(SCPatchTimerHandler + ((SCPatchLoaderParams *)paramBlock)->codeOffset), 
195                          paramBlock, NULL);
196#endif
197        return 0;
198}
199
200//-------------------------------------------------------------------------------------------------------------
201mach_error_t INJECT_ENTRY( ptrdiff_t codeOffset, void *paramBlock, size_t paramSize, char *dummy_pthread_struct  )
202{
203        pthread_t                       thread;
204        pthread_attr_t          attr;
205        struct sched_param      sched;
206        int                                     policy;
207#if defined (__i386__)
208        // On intel, per-pthread data is a zone of data that must be allocated.
209        // if not, all function trying to access per-pthread data (all mig functions for instance)
210        // will crash.
211        extern void __pthread_set_self(char*);
212        __pthread_set_self(dummy_pthread_struct);
213#endif
214    // We need to fix up function addresses in gcc code, but not for code generated by CW.
215        // It turns out that this is not needed for gcc 4.0
216//#ifdef __MWERKS__
217    codeOffset = 0;
218//#endif
219    // Stash the code offset where we can get at it later.
220    ((SCPatchLoaderParams *)paramBlock)->codeOffset = codeOffset;
221   
222        // OK, first we need a pthread so that CoreFoundation will work correctly
223        // (it uses pthread tokens to make itself thread-safe).
224        pthread_attr_init(&attr); 
225        pthread_attr_getschedpolicy(&attr, &policy);
226        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
227        pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
228        sched.sched_priority = sched_get_priority_max(policy);
229        pthread_attr_setschedparam(&attr, &sched);
230
231        pthread_create(&thread, &attr, SCPatchThreadEntry + codeOffset, (void *)paramBlock);
232
233        pthread_attr_destroy(&attr);
234
235        thread_terminate(mach_thread_self());
236   
237        return err_none;
238}
Note: See TracBrowser for help on using the repository browser.