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

Last change on this file since 241 was 217, checked in by rchin, 19 years ago
  • Updated to new mach_inject with support for x86
  • Updated nib files to 10.2+ format so that future updates to the f-script framework won't break fsa
  • Adjusted some code to work with the new x86 mach_inject (sends an additional argument)
  • Updates code to work with newer defines in cctools
File size: 8.2 KB
RevLine 
[153]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 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//-------------------------------------------------------------------------------------------------------------
[217]201mach_error_t INJECT_ENTRY( ptrdiff_t codeOffset, void *paramBlock, size_t paramSize, char *dummy_pthread_struct )
[153]202{
203 pthread_t thread;
204 pthread_attr_t attr;
205 struct sched_param sched;
206 int policy;
[217]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
[153]214 // We need to fix up function addresses in gcc code, but not for code generated by CW.
[217]215 // It turns out that this is not needed for gcc 4.0
216//#ifdef __MWERKS__
[153]217 codeOffset = 0;
[217]218//#endif
[153]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.