|
1 /* |
|
2 File: CFMLateImport.h |
|
3 |
|
4 Contains: Interface to CFM late import library. |
|
5 |
|
6 Written by: Quinn |
|
7 |
|
8 Copyright: Copyright © 1999 by Apple Computer, Inc., all rights reserved. |
|
9 |
|
10 You may incorporate this Apple sample source code into your program(s) without |
|
11 restriction. This Apple sample source code has been provided "AS IS" and the |
|
12 responsibility for its operation is yours. You are not permitted to redistribute |
|
13 this Apple sample source code as "Apple sample source code" after having made |
|
14 changes. If you're going to re-distribute the source, we require that you make |
|
15 it clear in the source that the code was descended from Apple sample source |
|
16 code, but that you've made changes. |
|
17 |
|
18 Change History (most recent first): |
|
19 |
|
20 <6> 21/9/01 Quinn Changes for CWPro7 Mach-O build. |
|
21 <5> 19/9/01 Quinn Change comments to reflect the fact that an unpacked data |
|
22 section is no longer required. |
|
23 <4> 19/9/01 Quinn Simplified API and implementation after a suggestion by Eric |
|
24 Grant. You no longer have to CFM export a dummy function; you |
|
25 can just pass in the address of your fragment's init routine. |
|
26 <3> 16/11/00 Quinn Allow symbol finding via a callback and use that to implement |
|
27 CFBundle support. |
|
28 <2> 18/10/99 Quinn Renamed CFMLateImport to CFMLateImportLibrary to allow for |
|
29 possible future API expansion. |
|
30 <1> 15/6/99 Quinn First checked in. |
|
31 */ |
|
32 |
|
33 #pragma once |
|
34 |
|
35 ///////////////////////////////////////////////////////////////// |
|
36 |
|
37 // MoreIsBetter Setup |
|
38 |
|
39 //#include "MoreSetup.h" |
|
40 |
|
41 // Mac OS Interfaces |
|
42 |
|
43 #if ! MORE_FRAMEWORK_INCLUDES |
|
44 #include <MacTypes.h> |
|
45 #include <CodeFragments.h> |
|
46 #include <Devices.h> |
|
47 #include <CFBundle.h> |
|
48 #endif |
|
49 |
|
50 ///////////////////////////////////////////////////////////////// |
|
51 |
|
52 #ifdef __cplusplus |
|
53 extern "C" { |
|
54 #endif |
|
55 |
|
56 /* FAQ |
|
57 --- |
|
58 |
|
59 Q: What does this library do? |
|
60 A: It allows you to resolve a weak linked library at runtime, |
|
61 by supply a CFM connection to the library that should substitute |
|
62 for the weak linked one. |
|
63 |
|
64 Q: Does the substituted library have to have the same name as the |
|
65 weak linked library. |
|
66 A: No. |
|
67 |
|
68 Q: What's this useful for? |
|
69 A: The most obvious example of where this is useful is when |
|
70 you rely on shared libraries that the user might delete |
|
71 or move. To can find the shared library (possibly even |
|
72 using CatSearch), call GetDiskFragment to open a connection |
|
73 to it, late import it using this library, and then the |
|
74 rest of your code can continue to use the shared library |
|
75 as if nothing had happened. No more defining thousands |
|
76 of stub routines which call through routine pointers. |
|
77 |
|
78 There are, however, numerous less obvious uses. You can |
|
79 use this code to make a 'self repairing' application. If |
|
80 the user removes your shared library from the Extensions |
|
81 folder, the startup code for your application can offer |
|
82 tor re-install it. If the user agrees, you can then |
|
83 re-install your shared library, late import it, and then |
|
84 continue running your application if nothing happened. |
|
85 |
|
86 You can even use this code to free yourself from the |
|
87 Extensions folder entirely. Say you have a suite of |
|
88 applications that currently installs a dozen shared |
|
89 libraries in the Extensions folder. You can move those |
|
90 libraries to another folder entirely and each application's |
|
91 startup code can track down the library (using an alias |
|
92 in the Preferences file) and late import it. |
|
93 |
|
94 An even cooler use is to provide easy abstraction layers. |
|
95 Say you have a network code for both the MacTCP |
|
96 API and the Open Transport API. Typically, you would be |
|
97 force to do this by having an abstraction layer where every |
|
98 routine contains a switch between MacTCP and OT. Your |
|
99 OpenSocket routine might look like: |
|
100 |
|
101 static int OpenSocket(void) |
|
102 { |
|
103 if (gOTAvailable) { |
|
104 return OpenSocketOT(); |
|
105 } else { |
|
106 return OpenSocketMacTCP(); |
|
107 } |
|
108 } |
|
109 |
|
110 With this code, you can avoid that entirely. Simply |
|
111 weak link to a shared library that you know is never |
|
112 going to be implemented ("crea;MySocketsDummy") and then, |
|
113 at runtime, decide whether the system has MacTCP or OT |
|
114 and late import the relevant real implementation |
|
115 ("crea;MySocketsMacTCP" or "crea;MySocketsOT"). |
|
116 One benefit of this approach is that only the MacTCP or |
|
117 the OT code is resident in memory on any given system. |
|
118 */ |
|
119 |
|
120 typedef pascal OSStatus (*CFMLateImportLookupProc)(ConstStr255Param symName, CFragSymbolClass symClass, |
|
121 void **symAddr, void *refCon); |
|
122 // CFMLateImportLookupProc defines a callback for CFMLateImportCore. |
|
123 // The routine is expected to look up the address of the symbol named |
|
124 // symName and return it in *symAddr. The symbol should be of class |
|
125 // symClass, although the callback decides whether a class mismatch is |
|
126 // an error. refCon is an application defined value that was originally |
|
127 // passed in to CFMLateImportCore. |
|
128 // |
|
129 // If this routine returns an error, a symbol address of 0 is assumed. |
|
130 // If the symbol is marked as a weak import, the CFMLateImportCore will |
|
131 // continue, otherwise the CFMLateImportCore routine will fail with the |
|
132 // error. |
|
133 |
|
134 extern pascal OSStatus CFMLateImportCore(const CFragSystem7DiskFlatLocator *fragToFixLocator, |
|
135 CFragConnectionID fragToFixConnID, |
|
136 CFragInitFunction fragToFixInitRoutine, |
|
137 ConstStr255Param weakLinkedLibraryName, |
|
138 CFMLateImportLookupProc lookup, |
|
139 void *refCon); |
|
140 // This routine will link you, at runtime, to some library |
|
141 // that you were weak linked to and wasn't present when your |
|
142 // fragment was prepared. As well as the obvious functionality |
|
143 // of being able to resolve weak links after prepare time, |
|
144 // this functionality can be put to a number of less obvious uses, |
|
145 // some of which are discussed at the top of this header file. |
|
146 // |
|
147 // To call this routine, you need a number of pieces of information: |
|
148 // |
|
149 // 1. fragToFixLocator, fragToFixConnID: The location of your own |
|
150 // code fragment on disk and the CFM connection ID to your own |
|
151 // code fragment. Typically you get this information from your |
|
152 // fragment's CFM init routine. You must ensure that |
|
153 // fragToFixLocator->fileSpec points to an FSSpec of the |
|
154 // file which holds your code fragment. |
|
155 // |
|
156 // IMPORTANT: |
|
157 // The fact that you pass in a CFragSystem7DiskFlatLocator as the |
|
158 // fragToFixLocator implies that the fragment to be fixed up must |
|
159 // be in the data fork of a file. The code could be modified |
|
160 // to remove this requirement, but on disk code fragments are the most |
|
161 // common case. |
|
162 // |
|
163 // IMPORTANT: |
|
164 // The fragment to fix may have a packed data section. Packing the |
|
165 // data section will reduce the size of your fragment on disk, but it |
|
166 // will significantly increase the memory needed by this routine |
|
167 // (it increases memory usage by the sum of the sizes of the packed |
|
168 // and unpacked data section). See below for instructions on how to |
|
169 // create an unpacked data section. |
|
170 // |
|
171 // 2. fragToFixInitRoutine: A pointer to your own code fragment's |
|
172 // fragment initialiser routine. You necessarily have one of these |
|
173 // because you need it to get values for the fragToFixLocator and |
|
174 // fragToFixConnID parameters. Just pass its address in as a parameter |
|
175 // as well. |
|
176 // |
|
177 // 3. weakLinkedLibraryName: The name of the weak linked library which |
|
178 // failed to link. You must have weak linked to this library. |
|
179 // It is oxymoric for you to pass a strong linked library here, |
|
180 // because your code would not have prepared if a strong linked |
|
181 // library failed to prepare, and so you couldn't supply a valid |
|
182 /// fragToFix. |
|
183 // |
|
184 // 4. lookup, refCon: A pointer to a callback function that the |
|
185 // routine calls to look up the address of a symbol, and a refCon |
|
186 // for that callback routine. |
|
187 // |
|
188 // Note: |
|
189 // The fragToFixLocator and fragToFixInitRoutine parameters |
|
190 // are artifacts of the way in which this functionality is implemented. |
|
191 // In an ideal world, where CFM exported decent introspection APIs |
|
192 // to third party developers, these parameters would not be necessary. |
|
193 // If you're using this code inside Apple, you probably should investigate |
|
194 // using the CFM private APIs for getting at the information these |
|
195 // parameters are needed for. See the comments inside the implementation |
|
196 // for more details. |
|
197 // |
|
198 // Note: |
|
199 // The extra memory taken when you use a packed data section is also an |
|
200 // artifact of my workaround for the lack of CFM introspection APIs. In |
|
201 // my opinion it's better to use an unpacked data section and consume more |
|
202 // space on disk while saving memory. In CodeWarrior you can switch to an |
|
203 // unpacked data section by checking the "Expand Uninitialized Data" |
|
204 // checkbox in the "PPC PEF" settings panel. In MPW, specified the |
|
205 // "-packdata off" option to PPCLink. |
|
206 // |
|
207 // When the routine returns, any symbols that you imported from the |
|
208 // library named weakLinkedLibraryName will be resolved to the address |
|
209 // of the symbol provided by the "lookup" callback routine. |
|
210 // |
|
211 // It is possible for an unresolved import to remain unresolved after |
|
212 // this routine returns. If the symbol import is marked as weak (as |
|
213 // opposed to the library, which *must* be marked as weak) and the symbol |
|
214 // is not found by the "lookup" callback, the routine will simple skip |
|
215 // that symbol. If the symbol isn't marked as weak, the routine will fail |
|
216 // in that case. |
|
217 // |
|
218 // Most of the possible error results are co-opted CFM errors. These |
|
219 // include: |
|
220 // |
|
221 // cfragFragmentFormatErr -- The fragment to fix is is an unknown format. |
|
222 // cfragNoSectionErr -- Could not find the loader section in the fragment to fix. |
|
223 // cfragNoLibraryErr -- The fragment to fix is not weak linked to weakLinkedLibraryName. |
|
224 // cfragFragmentUsageErr -- The fragment to fix doesn't have a data section. |
|
225 // -- The fragment to fix is strong linked to weakLinkedLibraryName. |
|
226 // -- The fragment doesn't have an init routine. |
|
227 // cfragFragmentCorruptErr -- Encountered an undefined relocation opcode. |
|
228 // unimpErr -- Encountered an unimplement relocation opcode. The |
|
229 // relocation engine only implements a subset of the CFM |
|
230 // relocation opcodes, the subset most commonly used by |
|
231 // MPW and CodeWarrior PEF containers. If you encounter |
|
232 // this error, you'll probably have to add the weird |
|
233 // relocation opcode to the engine, which shouldn't be |
|
234 // be too hard. |
|
235 // memFullErr -- It's likely that this error is triggered by the memory |
|
236 // needed to unpack your data section. Either make your |
|
237 // data section smaller, or unpack it (see above). |
|
238 // errors returned by FindSymbol |
|
239 // errors returned by Memory Manager |
|
240 // |
|
241 // The routine needs enough memory to hold the loader section of the fragment |
|
242 // to fix in memory. It allocates that memory using NewPtr and dispsoses of |
|
243 // it before it returns. You may want to change the memory allocator, which |
|
244 // is very simple. |
|
245 |
|
246 extern pascal OSStatus CFMLateImportLibrary(const CFragSystem7DiskFlatLocator *fragToFixLocator, |
|
247 CFragConnectionID fragToFixConnID, |
|
248 CFragInitFunction fragToFixInitRoutine, |
|
249 ConstStr255Param weakLinkedLibraryName, |
|
250 CFragConnectionID connIDToImport); |
|
251 // A wrapper around CFMLateImportCore that looks up symbols by calling |
|
252 // FindSymbol on a connection to a CFM library (connIDToImport). |
|
253 // You can get this connection ID through any standard CFM API, for example |
|
254 // GetSharedLibrary, GetDiskFragment, or GetMemFragment. |
|
255 // |
|
256 // IMPORTANT: |
|
257 // The fragment name for connIDToImport *does not* have to match |
|
258 // weakLinkedLibraryName. This is part of the power of this library. |
|
259 |
|
260 extern pascal OSStatus CFMLateImportBundle(const CFragSystem7DiskFlatLocator *fragToFixLocator, |
|
261 CFragConnectionID fragToFixConnID, |
|
262 CFragInitFunction fragToFixInitRoutine, |
|
263 ConstStr255Param weakLinkedLibraryName, |
|
264 CFBundleRef bundleToImport); |
|
265 // A wrapper around CFMLateImportCore that looks up symbols by calling |
|
266 // CFBundleGetFunctionPointerForName on a reference to a Core Foundation |
|
267 // bundle (bundleToImport). You can get this reference through any |
|
268 // Core Foundation bundle API, for example CFBundleCreate. |
|
269 |
|
270 #ifdef __cplusplus |
|
271 } |
|
272 #endif |