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