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, 16 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.