1 | /*
|
---|
2 | File: CFMLateImport.c
|
---|
3 |
|
---|
4 | Contains: Implementation of 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 | <13> 24/9/01 Quinn Fixes to compile with C++ activated.
|
---|
21 | <12> 21/9/01 Quinn [2710489] Fix typo in the comments for FragmentLookup.
|
---|
22 | <11> 21/9/01 Quinn Changes for CWPro7 Mach-O build.
|
---|
23 | <10> 19/9/01 Quinn Corrected implementation of kPEFRelocSmBySection. Added
|
---|
24 | implementations of kPEFRelocSetPosition and kPEFRelocLgByImport
|
---|
25 | (from code contributed by Eric Grant, Ned Holbrook, and Steve
|
---|
26 | Kalkwarf), although I can't test them yet.
|
---|
27 | <9> 19/9/01 Quinn We now handle unpacked data sections, courtesy of some code from
|
---|
28 | Ned Holbrook.
|
---|
29 | <8> 19/9/01 Quinn Minor fixes for the previous checkin. Updated some comments and
|
---|
30 | killed some dead code.
|
---|
31 | <7> 19/9/01 Quinn Simplified API and implementation after a suggestion by Eric
|
---|
32 | Grant. You no longer have to CFM export a dummy function; you
|
---|
33 | can just pass in the address of your fragment's init routine.
|
---|
34 | <6> 15/2/01 Quinn Modify compile-time warnings to complain if you try to build
|
---|
35 | this module into a Mach-O binary.
|
---|
36 | <5> 5/2/01 Quinn Removed redundant assignment in CFMLateImportCore.
|
---|
37 | <4> 30/11/00 Quinn Added comment about future of data symbols in CF.
|
---|
38 | <3> 16/11/00 Quinn Allow symbol finding via a callback and use that to implement
|
---|
39 | CFBundle support.
|
---|
40 | <2> 18/10/99 Quinn Renamed CFMLateImport to CFMLateImportLibrary to allow for
|
---|
41 | possible future API expansion.
|
---|
42 | <1> 15/6/99 Quinn First checked in.
|
---|
43 | */
|
---|
44 |
|
---|
45 | // To Do List:
|
---|
46 | //
|
---|
47 | // o get rid of dependence on ANSI "string.h", but how?
|
---|
48 | //
|
---|
49 | // Done:
|
---|
50 | //
|
---|
51 | // Ã investigate alternative APIs, like an external lookup routine
|
---|
52 | // renamed CFMLateImport to CFMLateImportLibrary to allow for
|
---|
53 | // future expansion of the APIs for things like CFMLateImportSymbol
|
---|
54 | // Ã test with non-zero fragment offset in the file
|
---|
55 | // Ã test more with MPW fragments
|
---|
56 | // Ã test data imports
|
---|
57 |
|
---|
58 | /////////////////////////////////////////////////////////////////
|
---|
59 |
|
---|
60 | // MoreIsBetter Setup
|
---|
61 |
|
---|
62 | //#include "MoreSetup.h"
|
---|
63 | #define MoreAssert(x) (true)
|
---|
64 | #define MoreAssertQ(x)
|
---|
65 |
|
---|
66 | // Mac OS Interfaces
|
---|
67 |
|
---|
68 | #if ! MORE_FRAMEWORK_INCLUDES
|
---|
69 | #include <CodeFragments.h>
|
---|
70 | #include <PEFBinaryFormat.h>
|
---|
71 | #endif
|
---|
72 |
|
---|
73 | // Standard C Interfaces
|
---|
74 |
|
---|
75 | #include <string.h>
|
---|
76 |
|
---|
77 | // MIB Prototypes
|
---|
78 |
|
---|
79 | //#include "MoreInterfaceLib.h"
|
---|
80 | #define MoreBlockZero BlockZero
|
---|
81 |
|
---|
82 | // Our Prototypes
|
---|
83 |
|
---|
84 | #include "CFMLateImport.h"
|
---|
85 |
|
---|
86 | /////////////////////////////////////////////////////////////////
|
---|
87 |
|
---|
88 | #if TARGET_RT_MAC_MACHO
|
---|
89 | #error CFMLateImport is not suitable for use in a Mach-O project.
|
---|
90 | #elif !TARGET_RT_MAC_CFM || !TARGET_CPU_PPC
|
---|
91 | #error CFMLateImport has not been qualified for 68K or CFM-68K use.
|
---|
92 | #endif
|
---|
93 |
|
---|
94 | /////////////////////////////////////////////////////////////////
|
---|
95 | #pragma mark ----- Utility Routines -----
|
---|
96 |
|
---|
97 | static OSStatus FSReadAtOffset(SInt16 refNum, SInt32 offset, SInt32 count, void *buffer)
|
---|
98 | // A convenient wrapper around PBRead which has two advantages
|
---|
99 | // over FSRead. First, it takes count as a value parameter.
|
---|
100 | // Second, it reads from an arbitrary offset into the file,
|
---|
101 | // which avoids a bunch of SetFPos calls.
|
---|
102 | //
|
---|
103 | // I guess this should go into "MoreFiles.h", but I'm not sure
|
---|
104 | // how we're going to integrate such a concept into MIB yet.
|
---|
105 | {
|
---|
106 | ParamBlockRec pb;
|
---|
107 |
|
---|
108 | pb.ioParam.ioRefNum = refNum;
|
---|
109 | pb.ioParam.ioBuffer = (Ptr) buffer;
|
---|
110 | pb.ioParam.ioReqCount = count;
|
---|
111 | pb.ioParam.ioPosMode = fsFromStart;
|
---|
112 | pb.ioParam.ioPosOffset = offset;
|
---|
113 |
|
---|
114 | return PBReadSync(&pb);
|
---|
115 | }
|
---|
116 |
|
---|
117 | /////////////////////////////////////////////////////////////////
|
---|
118 | #pragma mark ----- Late Import Engine -----
|
---|
119 |
|
---|
120 | // This structure represents the core data structure of the late import
|
---|
121 | // engine. It basically holds information about the fragment we're going
|
---|
122 | // to fix up. It starts off with the first three fields, which are
|
---|
123 | // provided by the client. Then, as we procede through the operation,
|
---|
124 | // we fill out more fields.
|
---|
125 |
|
---|
126 | struct FragToFixInfo {
|
---|
127 | CFragSystem7DiskFlatLocator locator; // How to find the fragment's container.
|
---|
128 | CFragConnectionID connID; // CFM connection to the fragment.
|
---|
129 | CFragInitFunction initRoutine; // The CFM init routine for the fragment.
|
---|
130 | PEFContainerHeader containerHeader; // The CFM header, read in from the container.
|
---|
131 | PEFSectionHeader *sectionHeaders; // The CFM section headers. A pointer block containing an array of containerHeader.sectionCount elements.
|
---|
132 | PEFLoaderInfoHeader *loaderSection; // The entire CFM loader section in a pointer block.
|
---|
133 | SInt16 fileRef; // A read-only path to the CFM container. We keep this here because one that one routine needs to read from the container.
|
---|
134 | void *section0Base; // The base address of section 0, which we go through hoops to calculate.
|
---|
135 | void *section1Base; // The base address of section 1, which we go through hoops to calculate.
|
---|
136 | Boolean disposeSectionPointers; // See below.
|
---|
137 | };
|
---|
138 | typedef struct FragToFixInfo FragToFixInfo;
|
---|
139 |
|
---|
140 | // The disposeSectionPointers Boolean is designed for future cool VM
|
---|
141 | // support. If VM is on, the entire code fragment is file mapped into
|
---|
142 | // high memory, including the data we're forced to allocate the
|
---|
143 | // sectionHeaders and loaderSection memory blocks to maintain. If
|
---|
144 | // we could find the address of the entire file mapped container,
|
---|
145 | // we could access the information directly from there and thus
|
---|
146 | // we wouldn't need to allocate (or dispose of) the memory blocks
|
---|
147 | // for sectionHeaders and loaderSection.
|
---|
148 | //
|
---|
149 | // I haven't implemented this yet because a) I'm not sure how to do
|
---|
150 | // it with documented APIs, and b) I couldn't be bothered, but
|
---|
151 | // disposeSectionPointers remains as vestigial support for the concept.
|
---|
152 |
|
---|
153 | static OSStatus ReadContainerBasics(FragToFixInfo *fragToFix)
|
---|
154 | // Reads some basic information from the container of the
|
---|
155 | // fragment to fix and stores it in various fields of
|
---|
156 | // fragToFix. This includes:
|
---|
157 | //
|
---|
158 | // o containerHeader -- The contain header itself.
|
---|
159 | // o sectionHeaders -- The array of section headers (in a newly allocated pointer block).
|
---|
160 | // o loaderSection -- The entire loader section (in a newly allocated pointer block).
|
---|
161 | //
|
---|
162 | // Also sets disposeSectionPointers to indicate whether
|
---|
163 | // the last two pointers should be disposed of.
|
---|
164 | //
|
---|
165 | // Finally, it leaves the container file open for later
|
---|
166 | // folks who want to read data from it.
|
---|
167 | {
|
---|
168 | OSStatus err;
|
---|
169 | UInt16 sectionIndex;
|
---|
170 | Boolean found;
|
---|
171 |
|
---|
172 | MoreAssertQ(fragToFix != nil);
|
---|
173 | MoreAssertQ(fragToFix->locator.fileSpec != nil);
|
---|
174 | MoreAssertQ(fragToFix->connID != nil);
|
---|
175 | MoreAssertQ(fragToFix->loaderSection == nil);
|
---|
176 | MoreAssertQ(fragToFix->sectionHeaders == nil);
|
---|
177 | MoreAssertQ(fragToFix->fileRef == 0);
|
---|
178 |
|
---|
179 | fragToFix->disposeSectionPointers = true;
|
---|
180 |
|
---|
181 | // Open up the file, read the container head, then read in
|
---|
182 | // all the section headers, then go looking through the
|
---|
183 | // section headers for the loader section (PEF defines
|
---|
184 | // that there can be only one).
|
---|
185 |
|
---|
186 | err = FSpOpenDF(fragToFix->locator.fileSpec, fsRdPerm, &fragToFix->fileRef);
|
---|
187 | if (err == noErr) {
|
---|
188 | err = FSReadAtOffset(fragToFix->fileRef,
|
---|
189 | fragToFix->locator.offset,
|
---|
190 | sizeof(fragToFix->containerHeader),
|
---|
191 | &fragToFix->containerHeader);
|
---|
192 | if (err == noErr) {
|
---|
193 | if ( fragToFix->containerHeader.tag1 != kPEFTag1
|
---|
194 | || fragToFix->containerHeader.tag2 != kPEFTag2
|
---|
195 | || fragToFix->containerHeader.architecture != kCompiledCFragArch
|
---|
196 | || fragToFix->containerHeader.formatVersion != kPEFVersion) {
|
---|
197 | err = cfragFragmentFormatErr;
|
---|
198 | }
|
---|
199 | }
|
---|
200 | if (err == noErr) {
|
---|
201 | fragToFix->sectionHeaders = (PEFSectionHeader *) NewPtr(fragToFix->containerHeader.sectionCount * sizeof(PEFSectionHeader));
|
---|
202 | err = MemError();
|
---|
203 | }
|
---|
204 | if (err == noErr) {
|
---|
205 | err = FSReadAtOffset(fragToFix->fileRef,
|
---|
206 | fragToFix->locator.offset + sizeof(fragToFix->containerHeader),
|
---|
207 | fragToFix->containerHeader.sectionCount * sizeof(PEFSectionHeader),
|
---|
208 | fragToFix->sectionHeaders);
|
---|
209 | }
|
---|
210 | if (err == noErr) {
|
---|
211 | sectionIndex = 0;
|
---|
212 | found = false;
|
---|
213 | while ( sectionIndex < fragToFix->containerHeader.sectionCount && ! found ) {
|
---|
214 | found = (fragToFix->sectionHeaders[sectionIndex].sectionKind == kPEFLoaderSection);
|
---|
215 | if ( ! found ) {
|
---|
216 | sectionIndex += 1;
|
---|
217 | }
|
---|
218 | }
|
---|
219 | }
|
---|
220 | if (err == noErr && ! found) {
|
---|
221 | err = cfragNoSectionErr;
|
---|
222 | }
|
---|
223 |
|
---|
224 | // Now read allocate a pointer block and read the loader section into it.
|
---|
225 |
|
---|
226 | if (err == noErr) {
|
---|
227 | fragToFix->loaderSection = (PEFLoaderInfoHeader *) NewPtr(fragToFix->sectionHeaders[sectionIndex].containerLength);
|
---|
228 | err = MemError();
|
---|
229 | }
|
---|
230 | if (err == noErr) {
|
---|
231 | err = FSReadAtOffset(fragToFix->fileRef,
|
---|
232 | fragToFix->locator.offset + fragToFix->sectionHeaders[sectionIndex].containerOffset,
|
---|
233 | fragToFix->sectionHeaders[sectionIndex].containerLength,
|
---|
234 | fragToFix->loaderSection);
|
---|
235 | }
|
---|
236 | }
|
---|
237 |
|
---|
238 | // No clean up. The client must init fragToFix to zeros and then
|
---|
239 | // clean up regardless of whether we return an error.
|
---|
240 |
|
---|
241 | return err;
|
---|
242 | }
|
---|
243 |
|
---|
244 | static UInt32 DecodeVCountValue(const UInt8 *start, UInt32 *outCount)
|
---|
245 | // Given a pointer to the start of a variable length PEF value,
|
---|
246 | // work out the value (in *outCount). Returns the number of bytes
|
---|
247 | // consumed by the value.
|
---|
248 | {
|
---|
249 | UInt8 * bytePtr;
|
---|
250 | UInt8 byte;
|
---|
251 | UInt32 count;
|
---|
252 |
|
---|
253 | bytePtr = (UInt8 *)start;
|
---|
254 |
|
---|
255 | // Code taken from "PEFBinaryFormat.h".
|
---|
256 | count = 0;
|
---|
257 | do {
|
---|
258 | byte = *bytePtr++;
|
---|
259 | count = (count << kPEFPkDataVCountShift) | (byte & kPEFPkDataVCountMask);
|
---|
260 | } while ((byte & kPEFPkDataVCountEndMask) != 0);
|
---|
261 |
|
---|
262 | *outCount = count;
|
---|
263 | return bytePtr - start;
|
---|
264 | }
|
---|
265 |
|
---|
266 | static UInt32 DecodeInstrCountValue(const UInt8 *inOpStart, UInt32 *outCount)
|
---|
267 | // Given a pointer to the start of an opcode (inOpStart), work out the
|
---|
268 | // count argument for that opcode (*outCount). Returns the number of
|
---|
269 | // bytes consumed by the opcode and count combination.
|
---|
270 | {
|
---|
271 | MoreAssertQ(inOpStart != nil);
|
---|
272 | MoreAssertQ(outCount != nil);
|
---|
273 |
|
---|
274 | if (PEFPkDataCount5(*inOpStart) != 0)
|
---|
275 | {
|
---|
276 | // Simple case, count encoded in opcode.
|
---|
277 | *outCount = PEFPkDataCount5(*inOpStart);
|
---|
278 | return 1;
|
---|
279 | }
|
---|
280 | else
|
---|
281 | {
|
---|
282 | // Variable-length case.
|
---|
283 | return 1 + DecodeVCountValue(inOpStart + 1, outCount);
|
---|
284 | }
|
---|
285 | }
|
---|
286 |
|
---|
287 | static OSStatus UnpackPEFDataSection(const UInt8 * const packedData, UInt32 packedSize,
|
---|
288 | UInt8 * const unpackedData, UInt32 unpackedSize)
|
---|
289 | {
|
---|
290 | OSErr err;
|
---|
291 | UInt32 offset;
|
---|
292 | UInt8 opCode;
|
---|
293 | UInt8 * unpackCursor;
|
---|
294 |
|
---|
295 | MoreAssertQ(packedData != nil);
|
---|
296 | MoreAssertQ(unpackedData != nil);
|
---|
297 | MoreAssertQ(unpackedSize >= packedSize);
|
---|
298 |
|
---|
299 | // The following asserts assume that the client allocated the memory with NewPtr,
|
---|
300 | // which may not always be true. However, the asserts' value in preventing accidental
|
---|
301 | // memory block overruns outweighs the possible maintenance effort.
|
---|
302 |
|
---|
303 | MoreAssertQ( packedSize == GetPtrSize( (Ptr) packedData ) );
|
---|
304 | MoreAssertQ( unpackedSize == GetPtrSize( (Ptr) unpackedData) );
|
---|
305 |
|
---|
306 | err = noErr;
|
---|
307 | offset = 0;
|
---|
308 | unpackCursor = unpackedData;
|
---|
309 | while (offset < packedSize) {
|
---|
310 | MoreAssertQ(unpackCursor < &unpackedData[unpackedSize]);
|
---|
311 |
|
---|
312 | opCode = packedData[offset];
|
---|
313 |
|
---|
314 | switch (PEFPkDataOpcode(opCode)) {
|
---|
315 | case kPEFPkDataZero:
|
---|
316 | {
|
---|
317 | UInt32 count;
|
---|
318 |
|
---|
319 | offset += DecodeInstrCountValue(&packedData[offset], &count);
|
---|
320 |
|
---|
321 | MoreBlockZero(unpackCursor, count);
|
---|
322 | unpackCursor += count;
|
---|
323 | }
|
---|
324 | break;
|
---|
325 |
|
---|
326 | case kPEFPkDataBlock:
|
---|
327 | {
|
---|
328 | UInt32 blockSize;
|
---|
329 |
|
---|
330 | offset += DecodeInstrCountValue(&packedData[offset], &blockSize);
|
---|
331 |
|
---|
332 | BlockMoveData(&packedData[offset], unpackCursor, blockSize);
|
---|
333 | unpackCursor += blockSize;
|
---|
334 | offset += blockSize;
|
---|
335 | }
|
---|
336 | break;
|
---|
337 |
|
---|
338 | case kPEFPkDataRepeat:
|
---|
339 | {
|
---|
340 | UInt32 blockSize;
|
---|
341 | UInt32 repeatCount;
|
---|
342 | UInt32 loopCounter;
|
---|
343 |
|
---|
344 | offset += DecodeInstrCountValue(&packedData[offset], &blockSize);
|
---|
345 | offset += DecodeVCountValue(&packedData[offset], &repeatCount);
|
---|
346 | repeatCount += 1; // stored value is (repeatCount - 1)
|
---|
347 |
|
---|
348 | for (loopCounter = 0; loopCounter < repeatCount; loopCounter++) {
|
---|
349 | BlockMoveData(&packedData[offset], unpackCursor, blockSize);
|
---|
350 | unpackCursor += blockSize;
|
---|
351 | }
|
---|
352 | offset += blockSize;
|
---|
353 | }
|
---|
354 | break;
|
---|
355 |
|
---|
356 | case kPEFPkDataRepeatBlock:
|
---|
357 | {
|
---|
358 | UInt32 commonSize;
|
---|
359 | UInt32 customSize;
|
---|
360 | UInt32 repeatCount;
|
---|
361 | const UInt8 *commonData;
|
---|
362 | const UInt8 *customData;
|
---|
363 | UInt32 loopCounter;
|
---|
364 |
|
---|
365 | offset += DecodeInstrCountValue(&packedData[offset], &commonSize);
|
---|
366 | offset += DecodeVCountValue(&packedData[offset], &customSize);
|
---|
367 | offset += DecodeVCountValue(&packedData[offset], &repeatCount);
|
---|
368 |
|
---|
369 | commonData = &packedData[offset];
|
---|
370 | customData = &packedData[offset + commonSize];
|
---|
371 |
|
---|
372 | for (loopCounter = 0; loopCounter < repeatCount; loopCounter++) {
|
---|
373 | BlockMoveData(commonData, unpackCursor, commonSize);
|
---|
374 | unpackCursor += commonSize;
|
---|
375 | BlockMoveData(customData, unpackCursor, customSize);
|
---|
376 | unpackCursor += customSize;
|
---|
377 | customData += customSize;
|
---|
378 | }
|
---|
379 | BlockMoveData(commonData, unpackCursor, commonSize);
|
---|
380 | unpackCursor += commonSize;
|
---|
381 | offset += (repeatCount * (commonSize + customSize)) + commonSize;
|
---|
382 | }
|
---|
383 | break;
|
---|
384 |
|
---|
385 | case kPEFPkDataRepeatZero:
|
---|
386 | {
|
---|
387 | UInt32 commonSize;
|
---|
388 | UInt32 customSize;
|
---|
389 | UInt32 repeatCount;
|
---|
390 | const UInt8 *customData;
|
---|
391 | UInt32 loopCounter;
|
---|
392 |
|
---|
393 | offset += DecodeInstrCountValue(&packedData[offset], &commonSize);
|
---|
394 | offset += DecodeVCountValue(&packedData[offset], &customSize);
|
---|
395 | offset += DecodeVCountValue(&packedData[offset], &repeatCount);
|
---|
396 |
|
---|
397 | customData = &packedData[offset];
|
---|
398 |
|
---|
399 | for (loopCounter = 0; loopCounter < repeatCount; loopCounter++) {
|
---|
400 | MoreBlockZero(unpackCursor, commonSize);
|
---|
401 | unpackCursor += commonSize;
|
---|
402 | BlockMoveData(customData, unpackCursor, customSize);
|
---|
403 | unpackCursor += customSize;
|
---|
404 | customData += customSize;
|
---|
405 | }
|
---|
406 | MoreBlockZero(unpackCursor, commonSize);
|
---|
407 | unpackCursor += commonSize;
|
---|
408 | offset += repeatCount * customSize;
|
---|
409 | }
|
---|
410 | break;
|
---|
411 |
|
---|
412 | default:
|
---|
413 | #if MORE_DEBUG
|
---|
414 | DebugStr("\pUnpackPEFDataSection: Unexpected data opcode");
|
---|
415 | #endif
|
---|
416 | err = cfragFragmentCorruptErr;
|
---|
417 | goto leaveNow;
|
---|
418 | break;
|
---|
419 | }
|
---|
420 | }
|
---|
421 |
|
---|
422 | leaveNow:
|
---|
423 | return err;
|
---|
424 | }
|
---|
425 |
|
---|
426 | /* SetupSectionBaseAddresses Rationale
|
---|
427 | -----------------------------------
|
---|
428 |
|
---|
429 | OK, here's where things get weird. In order to run the relocation
|
---|
430 | engine, I need to be able to find the base address of an instantiated
|
---|
431 | section of the fragment we're fixing up given only its section number.
|
---|
432 | This isn't hard for CFM to do because it's the one that instantiated the
|
---|
433 | sections in the first place. It's surprisingly difficult to do if
|
---|
434 | you're not CFM. [And you don't have access to the private CFM APis for
|
---|
435 | doing it.]
|
---|
436 |
|
---|
437 | [Alan Lillich is going to kill me when he reads this! I should point out
|
---|
438 | that TVector's don't have to contain two words, they can be longer,
|
---|
439 | and that the second word isn't necessarily a TOC pointer, it's
|
---|
440 | just that the calling conventions require that it be put in the
|
---|
441 | TOC register when the code is called.
|
---|
442 |
|
---|
443 | Furthermore, the code section isn't always section 0, and the data
|
---|
444 | section isn't always section 1, and there can be zero to many sections
|
---|
445 | of each type.
|
---|
446 |
|
---|
447 | But these niceties are besides the point: I'm doing something tricky
|
---|
448 | because I don't have a nice API for getting section base addresses.
|
---|
449 | If I had a nice API for doing that, none of this code would exist.
|
---|
450 | ]
|
---|
451 |
|
---|
452 | The technique is very sneaky (thanks to Eric Grant). The fragment to
|
---|
453 | fix necessarily has a CFM init routine (because it needs that routine
|
---|
454 | in order to capture the fragment location and connection ID). Thus the
|
---|
455 | fragment to fix must have a TVector in its data section. TVectors are
|
---|
456 | interesting because they're made up of two words. The first is a pointer
|
---|
457 | to the code that implements the routine; the second is a pointer to the TOC
|
---|
458 | for the fragment that's exporting the TVector. How TVectors are
|
---|
459 | created is interesting too. On disk, a TVector consists of two words,
|
---|
460 | the first being the offset from the start of the code section to the
|
---|
461 | routine, the second being the offset from the start of the data section
|
---|
462 | to the TOC base. When CFM prepares a TVector, it applies the following
|
---|
463 | transform:
|
---|
464 |
|
---|
465 | tvector.codePtr = tvector.codeOffset + base of code section
|
---|
466 | tvector.tocPtr = tvector.tocOffset + base of data section
|
---|
467 |
|
---|
468 | Now, you can reverse these questions to make them:
|
---|
469 |
|
---|
470 | base of code section = tvector.codePtr - tvector.codeOffset
|
---|
471 | base of data section = tvector.dataPtr - tvector.dataOffset
|
---|
472 |
|
---|
473 | So if you can find the relocated contents of the TVector and
|
---|
474 | find the original offsets that made up the TVector, you can then
|
---|
475 | calculate the base address of both the code and data sections.
|
---|
476 |
|
---|
477 | Finding the relocated contents of the TVector is easy; I simply
|
---|
478 | require the client to pass in a pointer to its init routine.
|
---|
479 | A routine pointer is a TVector pointer, so you can just cast it
|
---|
480 | and extract the pair of words.
|
---|
481 |
|
---|
482 | Finding the original offsets is a trickier. My technique is to
|
---|
483 | look up the init routine in the fragment's loader info header. This
|
---|
484 | yields the section number and offset where the init routine's unrelocated
|
---|
485 | TVector exists. Once I have that, I can just read the unrelocated TVector
|
---|
486 | out of the file and extract the offsets.
|
---|
487 | */
|
---|
488 |
|
---|
489 | struct TVector {
|
---|
490 | void *codePtr;
|
---|
491 | void *tocPtr;
|
---|
492 | };
|
---|
493 | typedef struct TVector TVector;
|
---|
494 |
|
---|
495 | static OSStatus SetupSectionBaseAddresses(FragToFixInfo *fragToFix)
|
---|
496 | // This routine initialises the section0Base and section1Base
|
---|
497 | // base fields of fragToFix to the base addresses of the
|
---|
498 | // instantiated fragment represented by the other fields
|
---|
499 | // of fragToFix. The process works in three states:
|
---|
500 | //
|
---|
501 | // 1. Find the contents of the relocated TVector of the
|
---|
502 | // fragment's initialisation routine, provided to us by
|
---|
503 | // the caller.
|
---|
504 | //
|
---|
505 | // 2. Find the contents of the non-relocated TVector by
|
---|
506 | // looking it up in the PEF loader info header and then
|
---|
507 | // using that to read the TVector contents from disk.
|
---|
508 | // This yields the offsets from the section bases for
|
---|
509 | // the init routine.
|
---|
510 | //
|
---|
511 | // 3. Subtract 2 from 3.
|
---|
512 | {
|
---|
513 | OSStatus err;
|
---|
514 | TVector * relocatedExport;
|
---|
515 | SInt32 initSection;
|
---|
516 | UInt32 initOffset;
|
---|
517 | PEFSectionHeader * initSectionHeader;
|
---|
518 | Ptr packedDataSection;
|
---|
519 | Ptr unpackedDataSection;
|
---|
520 | TVector originalOffsets;
|
---|
521 |
|
---|
522 | packedDataSection = nil;
|
---|
523 | unpackedDataSection = nil;
|
---|
524 |
|
---|
525 | // Step 1.
|
---|
526 |
|
---|
527 | // First find the init routine's TVector, which gives us the relocated
|
---|
528 | // offsets of the init routine into the data and code sections.
|
---|
529 |
|
---|
530 | relocatedExport = (TVector *) fragToFix->initRoutine;
|
---|
531 |
|
---|
532 | // Step 2.
|
---|
533 |
|
---|
534 | // Now find the init routine's TVector's offsets in the data section on
|
---|
535 | // disk. This gives us the raw offsets from the data and code section
|
---|
536 | // of the beginning of the init routine.
|
---|
537 |
|
---|
538 | err = noErr;
|
---|
539 | initSection = fragToFix->loaderSection->initSection;
|
---|
540 | initOffset = fragToFix->loaderSection->initOffset;
|
---|
541 | if (initSection == -1) {
|
---|
542 | err = cfragFragmentUsageErr;
|
---|
543 | }
|
---|
544 | if (err == noErr) {
|
---|
545 | MoreAssertQ( initSection >= 0 ); // Negative indexes are pseudo-sections which are just not allowed!
|
---|
546 | MoreAssertQ( initSection < fragToFix->containerHeader.sectionCount );
|
---|
547 |
|
---|
548 | initSectionHeader = &fragToFix->sectionHeaders[initSection];
|
---|
549 |
|
---|
550 | // If the data section is packed, unpack it to a temporary buffer and then get the
|
---|
551 | // original offsets from that buffer. If the data section is unpacked, just read
|
---|
552 | // the original offsets directly off the disk.
|
---|
553 |
|
---|
554 | if ( initSectionHeader->sectionKind == kPEFPackedDataSection ) {
|
---|
555 |
|
---|
556 | // Allocate space for packed and unpacked copies of the section.
|
---|
557 |
|
---|
558 | packedDataSection = NewPtr(initSectionHeader->containerLength);
|
---|
559 | err = MemError();
|
---|
560 |
|
---|
561 | if (err == noErr) {
|
---|
562 | unpackedDataSection = NewPtr(initSectionHeader->unpackedLength);
|
---|
563 | err = MemError();
|
---|
564 | }
|
---|
565 |
|
---|
566 | // Read the contents of the packed section.
|
---|
567 |
|
---|
568 | if (err == noErr) {
|
---|
569 | err = FSReadAtOffset( fragToFix->fileRef,
|
---|
570 | fragToFix->locator.offset
|
---|
571 | + initSectionHeader->containerOffset,
|
---|
572 | initSectionHeader->containerLength,
|
---|
573 | packedDataSection);
|
---|
574 | }
|
---|
575 |
|
---|
576 | // Unpack the data into the unpacked section.
|
---|
577 |
|
---|
578 | if (err == noErr) {
|
---|
579 | err = UnpackPEFDataSection( (UInt8 *) packedDataSection, initSectionHeader->containerLength,
|
---|
580 | (UInt8 *) unpackedDataSection, initSectionHeader->unpackedLength);
|
---|
581 | }
|
---|
582 |
|
---|
583 | // Extract the init routine's TVector from the unpacked section.
|
---|
584 |
|
---|
585 | if (err == noErr) {
|
---|
586 | BlockMoveData(unpackedDataSection + initOffset, &originalOffsets, sizeof(TVector));
|
---|
587 | }
|
---|
588 |
|
---|
589 | } else {
|
---|
590 | MoreAssertQ(fragToFix->sectionHeaders[initSection].sectionKind == kPEFUnpackedDataSection);
|
---|
591 | err = FSReadAtOffset(fragToFix->fileRef,
|
---|
592 | fragToFix->locator.offset
|
---|
593 | + fragToFix->sectionHeaders[initSection].containerOffset
|
---|
594 | + initOffset,
|
---|
595 | sizeof(TVector),
|
---|
596 | &originalOffsets);
|
---|
597 | }
|
---|
598 | }
|
---|
599 |
|
---|
600 | // Step 3.
|
---|
601 |
|
---|
602 | // Do the maths to subtract the unrelocated offsets from the current address
|
---|
603 | // to get the base address.
|
---|
604 |
|
---|
605 | if (err == noErr) {
|
---|
606 | fragToFix->section0Base = ((char *) relocatedExport->codePtr) - (UInt32) originalOffsets.codePtr;
|
---|
607 | fragToFix->section1Base = ((char *) relocatedExport->tocPtr) - (UInt32) originalOffsets.tocPtr;
|
---|
608 | }
|
---|
609 |
|
---|
610 | // Clean up.
|
---|
611 |
|
---|
612 | if (packedDataSection != nil) {
|
---|
613 | DisposePtr(packedDataSection);
|
---|
614 | MoreAssertQ( MemError() == noErr );
|
---|
615 | }
|
---|
616 | if (unpackedDataSection != nil) {
|
---|
617 | DisposePtr(unpackedDataSection);
|
---|
618 | MoreAssertQ( MemError() == noErr );
|
---|
619 | }
|
---|
620 | return err;
|
---|
621 | }
|
---|
622 |
|
---|
623 | static void *GetSectionBaseAddress(const FragToFixInfo *fragToFix, UInt16 sectionIndex)
|
---|
624 | // This routine returns the base of the instantiated section
|
---|
625 | // whose index is sectionIndex. This routine is the evil twin
|
---|
626 | // of SetupSectionBaseAddresses. It simply returns the values
|
---|
627 | // for section 0 and 1 that we derived in SetupSectionBaseAddresses.
|
---|
628 | // In a real implementation, this routine would call CFM API
|
---|
629 | // to get this information, and SetupSectionBaseAddresses would
|
---|
630 | // not exist, but CFM does not export the necessary APIs to
|
---|
631 | // third parties.
|
---|
632 | {
|
---|
633 | void *result;
|
---|
634 |
|
---|
635 | MoreAssertQ(fragToFix != nil);
|
---|
636 | MoreAssertQ(fragToFix->containerHeader.tag1 == kPEFTag1);
|
---|
637 |
|
---|
638 | switch (sectionIndex) {
|
---|
639 | case 0:
|
---|
640 | result = fragToFix->section0Base;
|
---|
641 | break;
|
---|
642 | case 1:
|
---|
643 | result = fragToFix->section1Base;
|
---|
644 | break;
|
---|
645 | default:
|
---|
646 | result = nil;
|
---|
647 | break;
|
---|
648 | }
|
---|
649 | return result;
|
---|
650 | }
|
---|
651 |
|
---|
652 |
|
---|
653 | static OSStatus FindImportLibrary(PEFLoaderInfoHeader *loaderSection, const char *libraryName, PEFImportedLibrary **importLibrary)
|
---|
654 | // This routine finds the import library description (PEFImportedLibrary)
|
---|
655 | // for the import library libraryName in the PEF loader section.
|
---|
656 | // It sets *importLibrary to the address of the description.
|
---|
657 | {
|
---|
658 | OSStatus err;
|
---|
659 | UInt32 librariesRemaining;
|
---|
660 | PEFImportedLibrary *thisImportLibrary;
|
---|
661 | Boolean found;
|
---|
662 |
|
---|
663 | MoreAssertQ(loaderSection != nil);
|
---|
664 | MoreAssertQ(libraryName != nil);
|
---|
665 | MoreAssertQ(importLibrary != nil);
|
---|
666 |
|
---|
667 | // Loop through each import library looking for a matching name.
|
---|
668 |
|
---|
669 | // Initialise thisImportLibrary to point to the byte after the
|
---|
670 | // end of the loader section's header.
|
---|
671 |
|
---|
672 | thisImportLibrary = (PEFImportedLibrary *) (loaderSection + 1);
|
---|
673 | librariesRemaining = loaderSection->importedLibraryCount;
|
---|
674 | found = false;
|
---|
675 | while ( librariesRemaining > 0 && ! found ) {
|
---|
676 | // PEF defines that import library names will have
|
---|
677 | // a null terminator, so we can just use strcmp.
|
---|
678 | found = (strcmp( libraryName,
|
---|
679 | ((char *)loaderSection)
|
---|
680 | + loaderSection->loaderStringsOffset
|
---|
681 | + thisImportLibrary->nameOffset) == 0);
|
---|
682 | // *** Remove ANSI strcmp eventually.
|
---|
683 | if ( ! found ) {
|
---|
684 | thisImportLibrary += 1;
|
---|
685 | librariesRemaining -= 1;
|
---|
686 | }
|
---|
687 | }
|
---|
688 |
|
---|
689 | if (found) {
|
---|
690 | *importLibrary = thisImportLibrary;
|
---|
691 | err = noErr;
|
---|
692 | } else {
|
---|
693 | *importLibrary = nil;
|
---|
694 | err = cfragNoLibraryErr;
|
---|
695 | }
|
---|
696 | return err;
|
---|
697 | }
|
---|
698 |
|
---|
699 | static OSStatus LookupSymbol(CFMLateImportLookupProc lookup, void *refCon,
|
---|
700 | PEFLoaderInfoHeader *loaderSection,
|
---|
701 | UInt32 symbolIndex,
|
---|
702 | UInt32 *symbolValue)
|
---|
703 | // This routine is used to look up a symbol during relocation.
|
---|
704 | // "lookup" is a client callback and refCon is its argument.
|
---|
705 | // Typically refCon is the CFM connection to the library that is
|
---|
706 | // substituting for the weak linked library. loaderSection
|
---|
707 | // is a pointer to the loader section of the fragment to fix up.
|
---|
708 | // symbolIndex is the index of the imported symbol in the loader section.
|
---|
709 | // The routine sets the word pointed to by symbolValue to the
|
---|
710 | // value of the symbol.
|
---|
711 | //
|
---|
712 | // The routine works by using symbolIndex to index into the imported
|
---|
713 | // symbol table to find the offset of the symbol's name in the string
|
---|
714 | // table. It then looks up the symbol by calling the client's "lookup"
|
---|
715 | // function and passes the resulting symbol address back in symbolValue.
|
---|
716 | {
|
---|
717 | OSStatus err;
|
---|
718 | UInt32 *importSymbolTable;
|
---|
719 | UInt32 symbolStringOffset;
|
---|
720 | Boolean symbolIsWeak;
|
---|
721 | CFragSymbolClass symbolClass;
|
---|
722 | char *symbolStringAddress;
|
---|
723 | Str255 symbolString;
|
---|
724 |
|
---|
725 | MoreAssertQ(lookup != nil);
|
---|
726 | MoreAssertQ(loaderSection != nil);
|
---|
727 | MoreAssertQ(symbolIndex < loaderSection->totalImportedSymbolCount);
|
---|
728 | MoreAssertQ(symbolValue != nil);
|
---|
729 |
|
---|
730 | // Find the base of the imported symbol table.
|
---|
731 |
|
---|
732 | importSymbolTable = (UInt32 *)(((char *)(loaderSection + 1)) + (loaderSection->importedLibraryCount * sizeof(PEFImportedLibrary)));
|
---|
733 |
|
---|
734 | // Grab the appropriate entry out of the table and
|
---|
735 | // extract the information from that entry.
|
---|
736 |
|
---|
737 | symbolStringOffset = importSymbolTable[symbolIndex];
|
---|
738 | symbolClass = PEFImportedSymbolClass(symbolStringOffset);
|
---|
739 | symbolIsWeak = ((symbolClass & kPEFWeakImportSymMask) != 0);
|
---|
740 | symbolClass = symbolClass & ~kPEFWeakImportSymMask;
|
---|
741 | symbolStringOffset = PEFImportedSymbolNameOffset(symbolStringOffset);
|
---|
742 |
|
---|
743 | // Find the string for the symbol in the strings table and
|
---|
744 | // extract it from the table into a Pascal string on the stack.
|
---|
745 |
|
---|
746 | symbolStringAddress = ((char *)loaderSection) + loaderSection->loaderStringsOffset + symbolStringOffset;
|
---|
747 | symbolString[0] = strlen(symbolStringAddress); // *** remove ANSI strlen
|
---|
748 | BlockMoveData(symbolStringAddress, &symbolString[1], symbolString[0]);
|
---|
749 |
|
---|
750 | // Look up the symbol in substitute library. If it fails, return
|
---|
751 | // a 0 value and check whether the error is fatal (a strong linked
|
---|
752 | // symbol) or benign (a weak linked symbol).
|
---|
753 |
|
---|
754 | err = lookup(symbolString, symbolClass, (void **) symbolValue, refCon);
|
---|
755 | if (err != noErr) {
|
---|
756 | *symbolValue = 0;
|
---|
757 | if (symbolIsWeak) {
|
---|
758 | err = noErr;
|
---|
759 | }
|
---|
760 | }
|
---|
761 | return err;
|
---|
762 | }
|
---|
763 |
|
---|
764 | // The EngineState structure encapsulates all of the persistent state
|
---|
765 | // of the CFM relocation engine virtual machine. I originally defined
|
---|
766 | // this structure so I could pass the state around between routines
|
---|
767 | // that implement various virtual opcodes, however I later worked
|
---|
768 | // out that the relocation was sufficiently simple that I could put it
|
---|
769 | // in in one routine. Still, I left the state in this structure in
|
---|
770 | // case I ever need to reverse that decision. It's also a convenient
|
---|
771 | // instructional design.
|
---|
772 |
|
---|
773 | struct EngineState {
|
---|
774 | UInt32 currentReloc; // Index of current relocation opcodes
|
---|
775 | UInt32 terminatingReloc; // Index of relocation opcodes which terminates relocation
|
---|
776 | UInt32 *sectionBase; // Start of the section
|
---|
777 | UInt32 *relocAddress; // Address within the section where the relocations are to be performed
|
---|
778 | UInt32 importIndex; // Symbol index, which is used to access an imported symbol's address
|
---|
779 | void *sectionC; // Memory address of an instantiated section within the PEF container; this variable is used by relocation opcodes that relocate section addresses
|
---|
780 | void *sectionD; // Memory address of an instantiated section within the PEF container; this variable is used by relocation opcodes that relocate section addresses
|
---|
781 | };
|
---|
782 | typedef struct EngineState EngineState;
|
---|
783 |
|
---|
784 | // Note:
|
---|
785 | // If I ever have to support the repeat opcodes, I'll probably
|
---|
786 | // have to add a repeat counter to EngineState.
|
---|
787 |
|
---|
788 | static OSStatus InitEngineState(const FragToFixInfo *fragToFix,
|
---|
789 | UInt16 relocHeaderIndex,
|
---|
790 | EngineState *state)
|
---|
791 | // This routine initialises the engine state suitably for
|
---|
792 | // running the relocation opcodes for the section whose
|
---|
793 | // index is relocHeaderIndex. relocHeaderIndex is not a
|
---|
794 | // a section number. See the comment where it's used below
|
---|
795 | // for details. The routine basically fills out all the fields
|
---|
796 | // in the EngineState structure as described by
|
---|
797 | // "Mac OS Runtime Architectures".
|
---|
798 | {
|
---|
799 | OSStatus err;
|
---|
800 | PEFLoaderRelocationHeader *relocHeader;
|
---|
801 |
|
---|
802 | MoreAssertQ(fragToFix != nil);
|
---|
803 | MoreAssertQ(state != nil);
|
---|
804 |
|
---|
805 | // This bit is tricky. relocHeaderIndex is an index into the relocation
|
---|
806 | // header table, starting at relocSectionCount (which is in the loader
|
---|
807 | // section header) for the first relocated section and decrementing
|
---|
808 | // down to 1 for the last relocated section. I find the relocation
|
---|
809 | // header by using relocHeaderIndex as a index backwards from the
|
---|
810 | // start of the relocation opcodes (ie relocInstrOffset). If you
|
---|
811 | // look at the diagram of the layout of the container in
|
---|
812 | // "PEFBinaryFormat.h", you'll see that the relocation opcodes
|
---|
813 | // immediately follow the relocation headers.
|
---|
814 | //
|
---|
815 | // I did this because the alternative (starting at the loader
|
---|
816 | // header and stepping past the import library table and the
|
---|
817 | // import symbol table) was a pain.
|
---|
818 |
|
---|
819 | relocHeader = (PEFLoaderRelocationHeader *) (((char *) fragToFix->loaderSection) + fragToFix->loaderSection->relocInstrOffset - relocHeaderIndex * sizeof(PEFLoaderRelocationHeader));
|
---|
820 |
|
---|
821 | MoreAssertQ(relocHeader->reservedA == 0); // PEF spec says it must be; we check to try to catch bugs in calculation of relocHeader
|
---|
822 |
|
---|
823 | state->currentReloc = relocHeader->firstRelocOffset;
|
---|
824 | state->terminatingReloc = relocHeader->firstRelocOffset + relocHeader->relocCount;
|
---|
825 | state->sectionBase = (UInt32 *) GetSectionBaseAddress(fragToFix, relocHeader->sectionIndex);
|
---|
826 | state->relocAddress = state->sectionBase;
|
---|
827 | state->importIndex = 0;
|
---|
828 |
|
---|
829 | // From "Mac OS Runtime Architectures":
|
---|
830 | //
|
---|
831 | // The sectionC and sectionD variables actually contain the
|
---|
832 | // memory address of an instantiated section minus the
|
---|
833 | // default address for that section. The default address for a
|
---|
834 | // section is contained in the defaultAddress field of the
|
---|
835 | // section header. However, in almost all cases the default
|
---|
836 | // address should be 0, so the simplified definition suffices.
|
---|
837 | //
|
---|
838 | // In the debug version, we drop into MacsBug if this weird case
|
---|
839 | // ever executes because it's more likely we made a mistake than
|
---|
840 | // we encountered a section with a default address.
|
---|
841 |
|
---|
842 | state->sectionC = GetSectionBaseAddress(fragToFix, 0);
|
---|
843 | if (state->sectionC != nil) {
|
---|
844 | #if MORE_DEBUG
|
---|
845 | if (fragToFix->sectionHeaders[0].defaultAddress != 0) {
|
---|
846 | DebugStr("\pInitEngineState: Executing weird case.");
|
---|
847 | }
|
---|
848 | #endif
|
---|
849 | (char *) state->sectionC -= fragToFix->sectionHeaders[0].defaultAddress;
|
---|
850 | }
|
---|
851 | state->sectionD = GetSectionBaseAddress(fragToFix, 1);
|
---|
852 | if (state->sectionD != nil) {
|
---|
853 | #if MORE_DEBUG
|
---|
854 | if (fragToFix->sectionHeaders[1].defaultAddress != 0) {
|
---|
855 | DebugStr("\pInitEngineState: Executing weird case.");
|
---|
856 | }
|
---|
857 | #endif
|
---|
858 | (char *) state->sectionD -= fragToFix->sectionHeaders[1].defaultAddress;
|
---|
859 | }
|
---|
860 |
|
---|
861 | err = noErr;
|
---|
862 | if (state->relocAddress == nil) {
|
---|
863 | err = cfragFragmentUsageErr;
|
---|
864 | }
|
---|
865 | return err;
|
---|
866 | }
|
---|
867 |
|
---|
868 | // kPEFRelocBasicOpcodes is a table that maps the top 7 bits of the opcode
|
---|
869 | // to a fundamental action. It's contents are defined for me in "PEFBinaryFormat.h",
|
---|
870 | // which is really convenient.
|
---|
871 |
|
---|
872 | static UInt8 kPEFRelocBasicOpcodes[kPEFRelocBasicOpcodeRange] = { PEFMaskedBasicOpcodes };
|
---|
873 |
|
---|
874 | static OSStatus RunRelocationEngine(const FragToFixInfo *fragToFix,
|
---|
875 | PEFImportedLibrary *importLibrary,
|
---|
876 | CFMLateImportLookupProc lookup, void *refCon)
|
---|
877 | // This is where the rubber really hits the. Given a fully
|
---|
878 | // populated fragToFix structure, the import library description
|
---|
879 | // of the weak imported library we're resolving, and a connection
|
---|
880 | // to the library we're going to substitute it, re-execute the
|
---|
881 | // relocation instructions (CFM has already executed them once)
|
---|
882 | // but only *do* instructions (ie store the change to the data section)
|
---|
883 | // that CFM skipped because the weak symbols were missing.
|
---|
884 | {
|
---|
885 | OSStatus err;
|
---|
886 | EngineState state;
|
---|
887 | UInt16 sectionsLeftToRelocate;
|
---|
888 | UInt32 totalRelocs;
|
---|
889 | UInt16 *relocInstrTable;
|
---|
890 | UInt16 opCode;
|
---|
891 |
|
---|
892 | MoreAssertQ(fragToFix != nil);
|
---|
893 | MoreAssertQ(fragToFix->containerHeader.tag1 == kPEFTag1);
|
---|
894 | MoreAssertQ(fragToFix->sectionHeaders != nil);
|
---|
895 | MoreAssertQ(fragToFix->loaderSection != nil);
|
---|
896 | MoreAssertQ(fragToFix->section0Base != nil); // Technically, having a nil for these two is not a problem, ...
|
---|
897 | MoreAssertQ(fragToFix->section1Base != nil); // but in practise it a wildly deviant case and we should know about it.
|
---|
898 | MoreAssertQ(importLibrary != nil);
|
---|
899 | MoreAssertQ(lookup != nil);
|
---|
900 |
|
---|
901 | // Before entering the loop, work out some information in advance.
|
---|
902 |
|
---|
903 | // totalRelocs is only used for debugging, to make sure our
|
---|
904 | // relocation PC (state.currentReloc) doesn't run wild.
|
---|
905 |
|
---|
906 | totalRelocs = (fragToFix->loaderSection->loaderStringsOffset - fragToFix->loaderSection->relocInstrOffset) / sizeof(UInt16);
|
---|
907 |
|
---|
908 | // relocInstrTable is the base address of the table of relocation
|
---|
909 | // instructions in the fragment to fix.
|
---|
910 |
|
---|
911 | relocInstrTable = (UInt16 *)((char *) fragToFix->loaderSection + fragToFix->loaderSection->relocInstrOffset);
|
---|
912 |
|
---|
913 | // sectionsLeftToRelocate is the loop counter for the outer loop.
|
---|
914 |
|
---|
915 | MoreAssertQ(fragToFix->loaderSection->relocSectionCount <= 0x0FFFF);
|
---|
916 | sectionsLeftToRelocate = fragToFix->loaderSection->relocSectionCount;
|
---|
917 |
|
---|
918 | // Now let's run the relocation engine. We run it once per
|
---|
919 | // section in the table. Each time around, we init the engine
|
---|
920 | // and then loop again, this time executing individual opcodes.
|
---|
921 | // The opcode loop terminates when the relocation PC
|
---|
922 | // (state.currentReloc) hits the final opcode (state.terminatingReloc).
|
---|
923 |
|
---|
924 | // Note:
|
---|
925 | // One design decision I made was to totally re-init the engine state
|
---|
926 | // for each section. The CFM spec is unclear as to whether you're supposed
|
---|
927 | // to totally re-init the engine state, or just re-init the section-specific
|
---|
928 | // state (ie currentReloc, terminatingReloc, and relocAddress). I hope this
|
---|
929 | // is correct, but it's hard to test without having a fragment with multiple
|
---|
930 | // relocated sections, which is difficult to create.
|
---|
931 |
|
---|
932 | // How do I decide which opcodes should be effective (ie make changes to
|
---|
933 | // the section being relocated) and which opcodes should just be executed
|
---|
934 | // for their side effects (ie updated state.relocAddress or state.importIndex)?
|
---|
935 | // The answer is both simple and subtle. Opcodes whose actions are dependent
|
---|
936 | // on a symbol that was in the weak linked library are effective, those that
|
---|
937 | // an independent of those symbols are not. The only opcodes that use
|
---|
938 | // symbolic values are kPEFRelocImportRun and kPEFRelocSmByImport, and
|
---|
939 | // these are only if the symbol is in the weak linked library.
|
---|
940 | // All other cases are executed for their side effects only.
|
---|
941 | //
|
---|
942 | // How do I determine if a symbol is in the weak linked library?
|
---|
943 | // Well I know the symbol's index and I know the lower bound and count
|
---|
944 | // of the symbols in the weak linked library, so I just do a simple
|
---|
945 | // bounds test, ie
|
---|
946 | //
|
---|
947 | // firstImportedSymbol <= importIndex < firstImportedSymbol + importedSymbolCount
|
---|
948 |
|
---|
949 | // From this code, it's relatively easy to see which relocation opcodes
|
---|
950 | // aren't implemented. If you ever encounter one, you'll find yourself
|
---|
951 | // in MacsBug with a message telling you which opcode was found. The
|
---|
952 | // two big groups of opcodes I skipped were the large format opcodes
|
---|
953 | // and the repeating opcodes. I skipped them because:
|
---|
954 | //
|
---|
955 | // a) I haven't got a way to generate them in a PEF container that I can
|
---|
956 | // test against. Without that, there's no way I could be assured that
|
---|
957 | // the code worked.
|
---|
958 | //
|
---|
959 | // b) I'm lazy.
|
---|
960 |
|
---|
961 | err = noErr;
|
---|
962 | while ( sectionsLeftToRelocate > 0 ) {
|
---|
963 | err = InitEngineState(fragToFix, sectionsLeftToRelocate, &state);
|
---|
964 | if (err != noErr) {
|
---|
965 | goto leaveNow;
|
---|
966 | }
|
---|
967 |
|
---|
968 | while ( state.currentReloc != state.terminatingReloc ) {
|
---|
969 |
|
---|
970 | MoreAssertQ( state.currentReloc < totalRelocs );
|
---|
971 |
|
---|
972 | opCode = relocInstrTable[state.currentReloc];
|
---|
973 | switch ( PEFRelocBasicOpcode(opCode) ) {
|
---|
974 | case kPEFRelocBySectDWithSkip:
|
---|
975 | {
|
---|
976 | UInt16 skipCount;
|
---|
977 | UInt16 relocCount;
|
---|
978 |
|
---|
979 | skipCount = ((opCode >> 6) & 0x00FF);
|
---|
980 | relocCount = (opCode & 0x003F);
|
---|
981 | state.relocAddress += skipCount;
|
---|
982 | state.relocAddress += relocCount;
|
---|
983 | }
|
---|
984 | break;
|
---|
985 | case kPEFRelocBySectC:
|
---|
986 | case kPEFRelocBySectD:
|
---|
987 | {
|
---|
988 | UInt16 runLength;
|
---|
989 |
|
---|
990 | runLength = (opCode & 0x01FF) + 1;
|
---|
991 | state.relocAddress += runLength;
|
---|
992 | }
|
---|
993 | break;
|
---|
994 | case kPEFRelocTVector12:
|
---|
995 | {
|
---|
996 | UInt16 runLength;
|
---|
997 |
|
---|
998 | runLength = (opCode & 0x01FF) + 1;
|
---|
999 | state.relocAddress += (runLength * 3);
|
---|
1000 | }
|
---|
1001 | break;
|
---|
1002 | case kPEFRelocTVector8:
|
---|
1003 | case kPEFRelocVTable8:
|
---|
1004 | {
|
---|
1005 | UInt16 runLength;
|
---|
1006 |
|
---|
1007 | runLength = (opCode & 0x01FF) + 1;
|
---|
1008 | state.relocAddress += (runLength * 2);
|
---|
1009 | }
|
---|
1010 | break;
|
---|
1011 | case kPEFRelocImportRun:
|
---|
1012 | {
|
---|
1013 | UInt32 symbolValue;
|
---|
1014 | UInt16 runLength;
|
---|
1015 |
|
---|
1016 | runLength = (opCode & 0x01FF) + 1;
|
---|
1017 | while (runLength > 0) {
|
---|
1018 | if ( state.importIndex >= importLibrary->firstImportedSymbol && state.importIndex < (importLibrary->firstImportedSymbol + importLibrary->importedSymbolCount) ) {
|
---|
1019 | err = LookupSymbol(lookup, refCon, fragToFix->loaderSection, state.importIndex, &symbolValue);
|
---|
1020 | if (err != noErr) {
|
---|
1021 | goto leaveNow;
|
---|
1022 | }
|
---|
1023 | *(state.relocAddress) += symbolValue;
|
---|
1024 | }
|
---|
1025 | state.importIndex += 1;
|
---|
1026 | state.relocAddress += 1;
|
---|
1027 | runLength -= 1;
|
---|
1028 | }
|
---|
1029 | }
|
---|
1030 | break;
|
---|
1031 | case kPEFRelocSmByImport:
|
---|
1032 | {
|
---|
1033 | UInt32 symbolValue;
|
---|
1034 | UInt32 index;
|
---|
1035 |
|
---|
1036 | index = (opCode & 0x01FF);
|
---|
1037 | if ( index >= importLibrary->firstImportedSymbol && index < (importLibrary->firstImportedSymbol + importLibrary->importedSymbolCount) ) {
|
---|
1038 | err = LookupSymbol(lookup, refCon, fragToFix->loaderSection, index, &symbolValue);
|
---|
1039 | if (err != noErr) {
|
---|
1040 | goto leaveNow;
|
---|
1041 | }
|
---|
1042 | *(state.relocAddress) += symbolValue;
|
---|
1043 | }
|
---|
1044 | state.importIndex = index + 1;
|
---|
1045 | state.relocAddress += 1;
|
---|
1046 | }
|
---|
1047 | break;
|
---|
1048 | case kPEFRelocSmSetSectC:
|
---|
1049 | {
|
---|
1050 | UInt32 index;
|
---|
1051 |
|
---|
1052 | index = (opCode & 0x01FF);
|
---|
1053 | state.sectionC = GetSectionBaseAddress(fragToFix, index);
|
---|
1054 | MoreAssertQ(state.sectionC != nil);
|
---|
1055 | }
|
---|
1056 | break;
|
---|
1057 | case kPEFRelocSmSetSectD:
|
---|
1058 | {
|
---|
1059 | UInt32 index;
|
---|
1060 |
|
---|
1061 | index = (opCode & 0x01FF);
|
---|
1062 | state.sectionD = GetSectionBaseAddress(fragToFix, index);
|
---|
1063 | MoreAssertQ(state.sectionD != nil);
|
---|
1064 | }
|
---|
1065 | break;
|
---|
1066 | case kPEFRelocSmBySection:
|
---|
1067 | state.relocAddress += 1;
|
---|
1068 | break;
|
---|
1069 | case kPEFRelocIncrPosition:
|
---|
1070 | {
|
---|
1071 | UInt16 offset;
|
---|
1072 |
|
---|
1073 | offset = (opCode & 0x0FFF) + 1;
|
---|
1074 | ((char *) state.relocAddress) += offset;
|
---|
1075 | }
|
---|
1076 | break;
|
---|
1077 | case kPEFRelocSmRepeat:
|
---|
1078 | #if MORE_DEBUG
|
---|
1079 | DebugStr("\pRunRelocationEngine: kPEFRelocSmRepeat not yet implemented");
|
---|
1080 | #endif
|
---|
1081 | err = unimpErr;
|
---|
1082 | goto leaveNow;
|
---|
1083 | break;
|
---|
1084 | case kPEFRelocSetPosition:
|
---|
1085 | {
|
---|
1086 | UInt32 offset;
|
---|
1087 |
|
---|
1088 | // Lot's of folks have tried various interpretations of the description of
|
---|
1089 | // this opCode in "Mac OS Runtime Architectures" (which states "This instruction
|
---|
1090 | // sets relocAddress to the address of the section offset offset." *smile*).
|
---|
1091 | // I eventually dug into the CFM source code to find my interpretation, which
|
---|
1092 | // I believe is correct. The key point is tht the offset is relative to
|
---|
1093 | // the start of the section for which these relocations are being performed.
|
---|
1094 |
|
---|
1095 | // Skip to next reloc word, which is the second chunk of the offset.
|
---|
1096 |
|
---|
1097 | state.currentReloc += 1;
|
---|
1098 |
|
---|
1099 | // Extract offset based on the most significant 10 bits in opCode and
|
---|
1100 | // the next significant 16 bits in the next reloc word.
|
---|
1101 |
|
---|
1102 | offset = PEFRelocSetPosFullOffset(opCode, relocInstrTable[state.currentReloc]);
|
---|
1103 |
|
---|
1104 | state.relocAddress = (UInt32 *) ( ((char *) state.sectionBase) + offset);
|
---|
1105 | }
|
---|
1106 | break;
|
---|
1107 | case kPEFRelocLgByImport:
|
---|
1108 | {
|
---|
1109 | UInt32 symbolValue;
|
---|
1110 | UInt32 index;
|
---|
1111 |
|
---|
1112 | // Get the 26 bit symbol index from the current and next reloc words.
|
---|
1113 |
|
---|
1114 | state.currentReloc += 1;
|
---|
1115 | index = PEFRelocLgByImportFullIndex(opCode, relocInstrTable[state.currentReloc]);
|
---|
1116 |
|
---|
1117 | if ( index >= importLibrary->firstImportedSymbol && index < (importLibrary->firstImportedSymbol + importLibrary->importedSymbolCount) ) {
|
---|
1118 | err = LookupSymbol(lookup, refCon, fragToFix->loaderSection, index, &symbolValue);
|
---|
1119 | if (err != noErr) {
|
---|
1120 | goto leaveNow;
|
---|
1121 | }
|
---|
1122 | *(state.relocAddress) += symbolValue;
|
---|
1123 | }
|
---|
1124 | state.importIndex = index + 1;
|
---|
1125 | state.relocAddress += 1;
|
---|
1126 | }
|
---|
1127 | break;
|
---|
1128 | case kPEFRelocLgRepeat:
|
---|
1129 | #if MORE_DEBUG
|
---|
1130 | DebugStr("\pRunRelocationEngine: kPEFRelocLgRepeat not yet implemented");
|
---|
1131 | #endif
|
---|
1132 | err = unimpErr;
|
---|
1133 | goto leaveNow;
|
---|
1134 | break;
|
---|
1135 | case kPEFRelocLgSetOrBySection:
|
---|
1136 | #if MORE_DEBUG
|
---|
1137 | DebugStr("\pRunRelocationEngine: kPEFRelocLgSetOrBySection not yet implemented");
|
---|
1138 | #endif
|
---|
1139 | err = unimpErr;
|
---|
1140 | goto leaveNow;
|
---|
1141 | break;
|
---|
1142 | case kPEFRelocUndefinedOpcode:
|
---|
1143 | err = cfragFragmentCorruptErr;
|
---|
1144 | goto leaveNow;
|
---|
1145 | break;
|
---|
1146 | default:
|
---|
1147 | MoreAssertQ(false);
|
---|
1148 | err = cfragFragmentCorruptErr;
|
---|
1149 | goto leaveNow;
|
---|
1150 | break;
|
---|
1151 | }
|
---|
1152 | state.currentReloc += 1;
|
---|
1153 | }
|
---|
1154 |
|
---|
1155 | sectionsLeftToRelocate -= 1;
|
---|
1156 | }
|
---|
1157 |
|
---|
1158 | leaveNow:
|
---|
1159 | return err;
|
---|
1160 | }
|
---|
1161 |
|
---|
1162 | extern pascal OSStatus CFMLateImportCore(const CFragSystem7DiskFlatLocator *fragToFixLocator,
|
---|
1163 | CFragConnectionID fragToFixConnID,
|
---|
1164 | CFragInitFunction fragToFixInitRoutine,
|
---|
1165 | ConstStr255Param weakLinkedLibraryName,
|
---|
1166 | CFMLateImportLookupProc lookup,
|
---|
1167 | void *refCon)
|
---|
1168 | // See comments in interface part.
|
---|
1169 | {
|
---|
1170 | OSStatus err;
|
---|
1171 | OSStatus junk;
|
---|
1172 | FragToFixInfo fragToFix;
|
---|
1173 | PEFImportedLibrary *importLibrary;
|
---|
1174 | char weakLinkedLibraryNameCString[256];
|
---|
1175 |
|
---|
1176 | MoreAssertQ(fragToFixLocator != nil);
|
---|
1177 | MoreAssertQ(fragToFixConnID != nil);
|
---|
1178 | MoreAssertQ(fragToFixInitRoutine != nil);
|
---|
1179 | MoreAssertQ(weakLinkedLibraryName != nil);
|
---|
1180 | MoreAssertQ(lookup != nil);
|
---|
1181 |
|
---|
1182 | // Fill out the bits of fragToFix which are passed in
|
---|
1183 | // by the client.
|
---|
1184 |
|
---|
1185 | MoreBlockZero(&fragToFix, sizeof(fragToFix));
|
---|
1186 | fragToFix.locator = *fragToFixLocator;
|
---|
1187 | fragToFix.connID = fragToFixConnID;
|
---|
1188 | fragToFix.initRoutine = fragToFixInitRoutine;
|
---|
1189 |
|
---|
1190 | // Make a C string from weakLinkedLibraryName.
|
---|
1191 |
|
---|
1192 | BlockMoveData(weakLinkedLibraryName + 1, weakLinkedLibraryNameCString, weakLinkedLibraryName[0]);
|
---|
1193 | weakLinkedLibraryNameCString[weakLinkedLibraryName[0]] = 0;
|
---|
1194 |
|
---|
1195 | // Get the basic information from the fragment.
|
---|
1196 | // Fills out the containerHeader, sectionHeaders, loaderSection and fileRef fields
|
---|
1197 | // of fragToFix.
|
---|
1198 |
|
---|
1199 | err = ReadContainerBasics(&fragToFix);
|
---|
1200 |
|
---|
1201 | // Set up the base address fields in fragToFix (ie section0Base and section1Base)
|
---|
1202 | // by looking up our init routine (fragToFix.initRoutine) and subtracting
|
---|
1203 | // away the section offsets (which we get from the disk copy of the section)
|
---|
1204 | // to derive the bases of the sections themselves.
|
---|
1205 |
|
---|
1206 | if (err == noErr) {
|
---|
1207 | err = SetupSectionBaseAddresses(&fragToFix);
|
---|
1208 | }
|
---|
1209 |
|
---|
1210 | // Look inside the loader section for the import library description
|
---|
1211 | // of weakLinkedLibraryName. We need this to know the range of symbol
|
---|
1212 | // indexes we're going to fix up.
|
---|
1213 |
|
---|
1214 | if (err == noErr) {
|
---|
1215 | err = FindImportLibrary(fragToFix.loaderSection, weakLinkedLibraryNameCString, &importLibrary);
|
---|
1216 | }
|
---|
1217 |
|
---|
1218 | // Do a quick check to ensure that the library was actually imported weak.
|
---|
1219 | // If it wasn't, it doesn't make much sense to resolve its weak imports
|
---|
1220 | // later on. Resolving them again is likely to be bad.
|
---|
1221 |
|
---|
1222 | if (err == noErr) {
|
---|
1223 | if ((importLibrary->options & kPEFWeakImportLibMask) == 0) {
|
---|
1224 | err = cfragFragmentUsageErr;
|
---|
1225 | }
|
---|
1226 | }
|
---|
1227 |
|
---|
1228 | // Now run the main relocation engine.
|
---|
1229 |
|
---|
1230 | if (err == noErr) {
|
---|
1231 | err = RunRelocationEngine(&fragToFix, importLibrary, lookup, refCon);
|
---|
1232 | }
|
---|
1233 |
|
---|
1234 | // Clean up.
|
---|
1235 |
|
---|
1236 | if (fragToFix.disposeSectionPointers) {
|
---|
1237 | if (fragToFix.fileRef != 0) {
|
---|
1238 | junk = FSClose(fragToFix.fileRef);
|
---|
1239 | MoreAssertQ(junk == noErr);
|
---|
1240 | }
|
---|
1241 | if (fragToFix.loaderSection != nil) {
|
---|
1242 | DisposePtr( (Ptr) fragToFix.loaderSection);
|
---|
1243 | MoreAssertQ(MemError() == noErr);
|
---|
1244 | }
|
---|
1245 | if (fragToFix.sectionHeaders != nil) {
|
---|
1246 | DisposePtr( (Ptr) fragToFix.sectionHeaders);
|
---|
1247 | MoreAssertQ(MemError() == noErr);
|
---|
1248 | }
|
---|
1249 | }
|
---|
1250 | return err;
|
---|
1251 | }
|
---|
1252 |
|
---|
1253 | static pascal OSStatus FragmentLookup(ConstStr255Param symName, CFragSymbolClass symClass,
|
---|
1254 | void **symAddr, void *refCon)
|
---|
1255 | // This is the CFMLateImportLookupProc callback used when
|
---|
1256 | // late importing from a CFM shared library.
|
---|
1257 | {
|
---|
1258 | OSStatus err;
|
---|
1259 | CFragConnectionID connIDToImport;
|
---|
1260 | CFragSymbolClass foundSymClass;
|
---|
1261 |
|
---|
1262 | MoreAssertQ(symName != nil);
|
---|
1263 | MoreAssertQ(symAddr != nil);
|
---|
1264 | MoreAssertQ(refCon != nil);
|
---|
1265 |
|
---|
1266 | connIDToImport = (CFragConnectionID) refCon;
|
---|
1267 |
|
---|
1268 | // Shame there's no way to validate that connIDToImport is valid.
|
---|
1269 |
|
---|
1270 | err = FindSymbol(connIDToImport, symName, (Ptr *) symAddr, &foundSymClass);
|
---|
1271 | if (err == noErr) {
|
---|
1272 | // If the symbol isn't of the right class, we act like we didn't
|
---|
1273 | // find it, but also assert in the debug build because weird things
|
---|
1274 | // are afoot.
|
---|
1275 | if (foundSymClass != symClass) {
|
---|
1276 | MoreAssertQ(false);
|
---|
1277 | *symAddr = nil;
|
---|
1278 | err = cfragNoSymbolErr;
|
---|
1279 | }
|
---|
1280 | }
|
---|
1281 | return err;
|
---|
1282 | }
|
---|
1283 |
|
---|
1284 | extern pascal OSStatus CFMLateImportLibrary(const CFragSystem7DiskFlatLocator *fragToFixLocator,
|
---|
1285 | CFragConnectionID fragToFixConnID,
|
---|
1286 | CFragInitFunction fragToFixInitRoutine,
|
---|
1287 | ConstStr255Param weakLinkedLibraryName,
|
---|
1288 | CFragConnectionID connIDToImport)
|
---|
1289 | // See comments in interface part.
|
---|
1290 | {
|
---|
1291 | MoreAssertQ(connIDToImport != nil);
|
---|
1292 | return CFMLateImportCore(fragToFixLocator, fragToFixConnID, fragToFixInitRoutine,
|
---|
1293 | weakLinkedLibraryName, FragmentLookup, connIDToImport);
|
---|
1294 | }
|
---|
1295 |
|
---|
1296 | static pascal OSStatus BundleLookup(ConstStr255Param symName, CFragSymbolClass symClass,
|
---|
1297 | void **symAddr, void *refCon)
|
---|
1298 | // This is the CFMLateImportLookupProc callback used when
|
---|
1299 | // late importing from a CFBundle.
|
---|
1300 | {
|
---|
1301 | OSStatus err;
|
---|
1302 | CFBundleRef bundleToImport;
|
---|
1303 | CFStringRef symNameStr;
|
---|
1304 |
|
---|
1305 | MoreAssertQ(symName != nil);
|
---|
1306 | MoreAssertQ(symAddr != nil);
|
---|
1307 | MoreAssertQ(refCon != nil);
|
---|
1308 |
|
---|
1309 | symNameStr = nil;
|
---|
1310 |
|
---|
1311 | bundleToImport = (CFBundleRef) refCon;
|
---|
1312 |
|
---|
1313 | // Shame there's no way to validate that bundleToImport is really a bundle.
|
---|
1314 |
|
---|
1315 | // We can only find function pointers because CFBundleGetFunctionPointerForName
|
---|
1316 | // only works for function pointers. So if the client is asking for something
|
---|
1317 | // other than a function pointer (ie TVector symbol) then we don't even true.
|
---|
1318 | // Also assert in the debug build because this shows a certain lack of
|
---|
1319 | // understanding on the part of the client.
|
---|
1320 | //
|
---|
1321 | // CF is being revise to support accessing data symbols using a new API
|
---|
1322 | // (currently this is available to Apple internal developers as
|
---|
1323 | // CFBundleGetDataPointerForName). When the new API is available in a
|
---|
1324 | // public header file I should revise this code to lift this restriction.
|
---|
1325 |
|
---|
1326 | err = noErr;
|
---|
1327 | if (symClass != kTVectorCFragSymbol) {
|
---|
1328 | MoreAssertQ(false);
|
---|
1329 | err = cfragNoSymbolErr;
|
---|
1330 | }
|
---|
1331 | if (err == noErr) {
|
---|
1332 | symNameStr = CFStringCreateWithPascalString(kCFAllocatorSystemDefault,
|
---|
1333 | symName, kCFStringEncodingMacRoman);
|
---|
1334 | if (symNameStr == nil) {
|
---|
1335 | err = coreFoundationUnknownErr;
|
---|
1336 | }
|
---|
1337 | }
|
---|
1338 | if (err == noErr) {
|
---|
1339 | *symAddr = CFBundleGetFunctionPointerForName(bundleToImport, symNameStr);
|
---|
1340 | if (*symAddr == nil) {
|
---|
1341 | err = cfragNoSymbolErr;
|
---|
1342 | }
|
---|
1343 | }
|
---|
1344 | if (symNameStr != nil) {
|
---|
1345 | CFRelease(symNameStr);
|
---|
1346 | }
|
---|
1347 | return err;
|
---|
1348 | }
|
---|
1349 |
|
---|
1350 | extern pascal OSStatus CFMLateImportBundle(const CFragSystem7DiskFlatLocator *fragToFixLocator,
|
---|
1351 | CFragConnectionID fragToFixConnID,
|
---|
1352 | CFragInitFunction fragToFixInitRoutine,
|
---|
1353 | ConstStr255Param weakLinkedLibraryName,
|
---|
1354 | CFBundleRef bundleToImport)
|
---|
1355 | // See comments in interface part.
|
---|
1356 | {
|
---|
1357 | MoreAssertQ(bundleToImport != nil);
|
---|
1358 | return CFMLateImportCore(fragToFixLocator, fragToFixConnID, fragToFixInitRoutine,
|
---|
1359 | weakLinkedLibraryName, BundleLookup, bundleToImport);
|
---|
1360 | }
|
---|