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

Last change on this file since 408 was 408, checked in by Nicholas Riley, 12 years ago

More debug logging until we have a GUI error channel.

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