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
97static 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
126struct 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};
138typedef 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
153static 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
244static 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
266static 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
287static 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
422leaveNow:
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
489struct TVector {
490    void *codePtr;
491    void *tocPtr;
492};
493typedef struct TVector TVector;
494
495static 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
623static 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
653static 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
699static 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
773struct 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};
782typedef 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
788static 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
872static UInt8 kPEFRelocBasicOpcodes[kPEFRelocBasicOpcodeRange] = { PEFMaskedBasicOpcodes };
873
874static 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 practice 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
1158leaveNow:
1159    return err;
1160}
1161
1162extern 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
1253static 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
1284extern 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
1296static 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
1350extern 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}
1361