aboutsummaryrefslogtreecommitdiff
blob: 955acfb85bd7fcb1a6bc0d5442f29ad149f0a55a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
/*
    File:               CFMLateImport.c

    Contains:           Implementation of CFM late import library.

    Written by:         Quinn

    Copyright:          Copyright � 1999 by Apple Computer, Inc., all rights reserved.

                            You may incorporate this Apple sample source code into your program(s) without
                            restriction. This Apple sample source code has been provided "AS IS" and the
                            responsibility for its operation is yours. You are not permitted to redistribute
                            this Apple sample source code as "Apple sample source code" after having made
                            changes. If you're going to re-distribute the source, we require that you make
                            it clear in the source that the code was descended from Apple sample source
                            code, but that you've made changes.

    Change History (most recent first):

    <13>     24/9/01    Quinn   Fixes to compile with C++ activated.
    <12>     21/9/01    Quinn   [2710489] Fix typo in the comments for FragmentLookup.
    <11>     21/9/01    Quinn   Changes for CWPro7 Mach-O build.
    <10>     19/9/01    Quinn   Corrected implementation of kPEFRelocSmBySection. Added
                                implementations of kPEFRelocSetPosition and kPEFRelocLgByImport
                                (from code contributed by Eric Grant, Ned Holbrook, and Steve
                                Kalkwarf), although I can't test them yet.
     <9>     19/9/01    Quinn   We now handle unpacked data sections, courtesy of some code from
                                Ned Holbrook.
     <8>     19/9/01    Quinn   Minor fixes for the previous checkin. Updated some comments and
                                killed some dead code.
     <7>     19/9/01    Quinn   Simplified API and implementation after a suggestion by Eric
                                Grant. You no longer have to CFM export a dummy function; you
                                can just pass in the address of your fragment's init routine.
     <6>     15/2/01    Quinn   Modify compile-time warnings to complain if you try to build
                                this module into a Mach-O binary.
     <5>      5/2/01    Quinn   Removed redundant assignment in CFMLateImportCore.
     <4>    30/11/00    Quinn   Added comment about future of data symbols in CF.
     <3>    16/11/00    Quinn   Allow symbol finding via a callback and use that to implement
                                CFBundle support.
     <2>    18/10/99    Quinn   Renamed CFMLateImport to CFMLateImportLibrary to allow for
                                possible future API expansion.
     <1>     15/6/99    Quinn   First checked in.
*/

// To Do List:
//
// o get rid of dependence on ANSI "string.h", but how?
//
// Done:
//
// � investigate alternative APIs, like an external lookup routine
//   renamed CFMLateImport to CFMLateImportLibrary to allow for
//   future expansion of the APIs for things like CFMLateImportSymbol
// � test with non-zero fragment offset in the file
// � test more with MPW fragments
// � test data imports

/////////////////////////////////////////////////////////////////

// MoreIsBetter Setup

//#include "MoreSetup.h"
#define MoreAssert(x) (true)
#define MoreAssertQ(x)

// Mac OS Interfaces

#if ! MORE_FRAMEWORK_INCLUDES
    #include <CodeFragments.h>
    #include <PEFBinaryFormat.h>
#endif

// Standard C Interfaces

#include <string.h>

// MIB Prototypes

//#include "MoreInterfaceLib.h"
#define MoreBlockZero BlockZero

// Our Prototypes

#include "CFMLateImport.h"

/////////////////////////////////////////////////////////////////

#if TARGET_RT_MAC_MACHO
    #error CFMLateImport is not suitable for use in a Mach-O project.
#elif !TARGET_RT_MAC_CFM || !TARGET_CPU_PPC
    #error CFMLateImport has not been qualified for 68K or CFM-68K use.
#endif

/////////////////////////////////////////////////////////////////
#pragma mark ----- Utility Routines -----

static OSStatus FSReadAtOffset(SInt16 refNum, SInt32 offset, SInt32 count, void *buffer)
    // A convenient wrapper around PBRead which has two advantages
    // over FSRead.  First, it takes count as a value parameter.
    // Second, it reads from an arbitrary offset into the file,
    // which avoids a bunch of SetFPos calls.
    //
    // I guess this should go into "MoreFiles.h", but I'm not sure
    // how we're going to integrate such a concept into MIB yet.
{
    ParamBlockRec pb;

    pb.ioParam.ioRefNum     = refNum;
    pb.ioParam.ioBuffer     = (Ptr) buffer;
    pb.ioParam.ioReqCount   = count;
    pb.ioParam.ioPosMode    = fsFromStart;
    pb.ioParam.ioPosOffset  = offset;

    return PBReadSync(&pb);
}

/////////////////////////////////////////////////////////////////
#pragma mark ----- Late Import Engine -----

// This structure represents the core data structure of the late import
// engine.  It basically holds information about the fragment we're going
// to fix up.  It starts off with the first three fields, which are
// provided by the client.  Then, as we procede through the operation,
// we fill out more fields.

struct FragToFixInfo {
    CFragSystem7DiskFlatLocator         locator;                                // How to find the fragment's container.
    CFragConnectionID                           connID;                                 // CFM connection to the fragment.
    CFragInitFunction                           initRoutine;                    // The CFM init routine for the fragment.
    PEFContainerHeader                          containerHeader;                // The CFM header, read in from the container.
    PEFSectionHeader                            *sectionHeaders;                // The CFM section headers.  A pointer block containing an array of containerHeader.sectionCount elements.
    PEFLoaderInfoHeader                         *loaderSection;                 // The entire CFM loader section in a pointer block.
    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.
    void                                                *section0Base;                  // The base address of section 0, which we go through hoops to calculate.
    void                                                *section1Base;                  // The base address of section 1, which we go through hoops to calculate.
    Boolean                                             disposeSectionPointers; // See below.
};
typedef struct FragToFixInfo FragToFixInfo;

// The disposeSectionPointers Boolean is designed for future cool VM
// support.  If VM is on, the entire code fragment is file mapped into
// high memory, including the data we're forced to allocate the
// sectionHeaders and loaderSection memory blocks to maintain.  If
// we could find the address of the entire file mapped container,
// we could access the information directly from there and thus
// we wouldn't need to allocate (or dispose of) the memory blocks
// for sectionHeaders and loaderSection.
//
// I haven't implemented this yet because a) I'm not sure how to do
// it with documented APIs, and b) I couldn't be bothered, but
// disposeSectionPointers remains as vestigial support for the concept.

static OSStatus ReadContainerBasics(FragToFixInfo *fragToFix)
    // Reads some basic information from the container of the
    // fragment to fix and stores it in various fields of
    // fragToFix.  This includes:
    //
    // o containerHeader -- The contain header itself.
    // o sectionHeaders  -- The array of section headers (in a newly allocated pointer block).
    // o loaderSection   -- The entire loader section (in a newly allocated pointer block).
    //
    // Also sets disposeSectionPointers to indicate whether
    // the last two pointers should be disposed of.
    //
    // Finally, it leaves the container file open for later
    // folks who want to read data from it.
{
    OSStatus            err;
    UInt16              sectionIndex;
    Boolean             found;

    MoreAssertQ(fragToFix != nil);
    MoreAssertQ(fragToFix->locator.fileSpec != nil);
    MoreAssertQ(fragToFix->connID != nil);
    MoreAssertQ(fragToFix->loaderSection == nil);
    MoreAssertQ(fragToFix->sectionHeaders == nil);
    MoreAssertQ(fragToFix->fileRef == 0);

    fragToFix->disposeSectionPointers = true;

    // Open up the file, read the container head, then read in
    // all the section headers, then go looking through the
    // section headers for the loader section (PEF defines
    // that there can be only one).

    err = FSpOpenDF(fragToFix->locator.fileSpec, fsRdPerm, &fragToFix->fileRef);
    if (err == noErr) {
        err = FSReadAtOffset(fragToFix->fileRef,
                                                        fragToFix->locator.offset,
                                                        sizeof(fragToFix->containerHeader),
                                                        &fragToFix->containerHeader);
        if (err == noErr) {
            if (   fragToFix->containerHeader.tag1 != kPEFTag1
                || fragToFix->containerHeader.tag2 != kPEFTag2
                || fragToFix->containerHeader.architecture != kCompiledCFragArch
                || fragToFix->containerHeader.formatVersion != kPEFVersion) {
                err = cfragFragmentFormatErr;
            }
        }
        if (err == noErr) {
            fragToFix->sectionHeaders = (PEFSectionHeader *) NewPtr(fragToFix->containerHeader.sectionCount * sizeof(PEFSectionHeader));
            err = MemError();
        }
        if (err == noErr) {
            err = FSReadAtOffset(fragToFix->fileRef,
                                                            fragToFix->locator.offset + sizeof(fragToFix->containerHeader),
                                                            fragToFix->containerHeader.sectionCount * sizeof(PEFSectionHeader),
                                                            fragToFix->sectionHeaders);
        }
        if (err == noErr) {
            sectionIndex = 0;
            found = false;
            while ( sectionIndex < fragToFix->containerHeader.sectionCount && ! found ) {
                found = (fragToFix->sectionHeaders[sectionIndex].sectionKind == kPEFLoaderSection);
                if ( ! found ) {
                    sectionIndex += 1;
                }
            }
        }
        if (err == noErr && ! found) {
            err = cfragNoSectionErr;
        }

        // Now read allocate a pointer block and read the loader section into it.

        if (err == noErr) {
            fragToFix->loaderSection = (PEFLoaderInfoHeader *) NewPtr(fragToFix->sectionHeaders[sectionIndex].containerLength);
            err = MemError();
        }
        if (err == noErr) {
            err = FSReadAtOffset(fragToFix->fileRef,
                                                            fragToFix->locator.offset + fragToFix->sectionHeaders[sectionIndex].containerOffset,
                                                            fragToFix->sectionHeaders[sectionIndex].containerLength,
                                                            fragToFix->loaderSection);
        }
    }

    // No clean up.  The client must init fragToFix to zeros and then
    // clean up regardless of whether we return an error.

    return err;
}

static UInt32 DecodeVCountValue(const UInt8 *start, UInt32 *outCount)
    // Given a pointer to the start of a variable length PEF value,
    // work out the value (in *outCount).  Returns the number of bytes
    // consumed by the value.
{
    UInt8 *                     bytePtr;
    UInt8                       byte;
    UInt32                      count;

    bytePtr = (UInt8 *)start;

    // Code taken from "PEFBinaryFormat.h".
    count = 0;
    do {
        byte = *bytePtr++;
        count = (count << kPEFPkDataVCountShift) | (byte & kPEFPkDataVCountMask);
    } while ((byte & kPEFPkDataVCountEndMask) != 0);

    *outCount = count;
    return bytePtr - start;
}

static UInt32 DecodeInstrCountValue(const UInt8 *inOpStart, UInt32 *outCount)
    // Given a pointer to the start of an opcode (inOpStart), work out the
    // count argument for that opcode (*outCount).  Returns the number of
    // bytes consumed by the opcode and count combination.
{
    MoreAssertQ(inOpStart != nil);
    MoreAssertQ(outCount  != nil);

    if (PEFPkDataCount5(*inOpStart) != 0)
    {
        // Simple case, count encoded in opcode.
        *outCount = PEFPkDataCount5(*inOpStart);
        return 1;
    }
    else
    {
        // Variable-length case.
        return 1 + DecodeVCountValue(inOpStart + 1, outCount);
    }
}

static OSStatus UnpackPEFDataSection(const UInt8 * const packedData,   UInt32 packedSize,
                                                                           UInt8 * const unpackedData, UInt32 unpackedSize)
{
    OSErr                       err;
    UInt32                      offset;
    UInt8                       opCode;
    UInt8 *                     unpackCursor;

    MoreAssertQ(packedData != nil);
    MoreAssertQ(unpackedData != nil);
    MoreAssertQ(unpackedSize >= packedSize);

    // The following asserts assume that the client allocated the memory with NewPtr,
    // which may not always be true.  However, the asserts' value in preventing accidental
    // memory block overruns outweighs the possible maintenance effort.

    MoreAssertQ( packedSize   == GetPtrSize( (Ptr) packedData  ) );
    MoreAssertQ( unpackedSize == GetPtrSize( (Ptr) unpackedData) );

    err          = noErr;
    offset       = 0;
    unpackCursor = unpackedData;
    while (offset < packedSize) {
        MoreAssertQ(unpackCursor < &unpackedData[unpackedSize]);

        opCode = packedData[offset];

        switch (PEFPkDataOpcode(opCode)) {
            case kPEFPkDataZero:
                {
                    UInt32                      count;

                    offset += DecodeInstrCountValue(&packedData[offset], &count);

                    MoreBlockZero(unpackCursor, count);
                    unpackCursor += count;
                }
                break;

            case kPEFPkDataBlock:
                {
                    UInt32                      blockSize;

                    offset += DecodeInstrCountValue(&packedData[offset], &blockSize);

                    BlockMoveData(&packedData[offset], unpackCursor, blockSize);
                    unpackCursor += blockSize;
                    offset += blockSize;
                }
                break;

            case kPEFPkDataRepeat:
                {
                    UInt32                      blockSize;
                    UInt32                      repeatCount;
                    UInt32  loopCounter;

                    offset += DecodeInstrCountValue(&packedData[offset], &blockSize);
                    offset += DecodeVCountValue(&packedData[offset], &repeatCount);
                    repeatCount += 1;                           // stored value is (repeatCount - 1)

                    for (loopCounter = 0; loopCounter < repeatCount; loopCounter++) {
                        BlockMoveData(&packedData[offset], unpackCursor, blockSize);
                        unpackCursor += blockSize;
                    }
                    offset += blockSize;
                }
                break;

            case kPEFPkDataRepeatBlock:
                {
                    UInt32                      commonSize;
                    UInt32                      customSize;
                    UInt32                      repeatCount;
                    const UInt8 *commonData;
                    const UInt8 *customData;
                    UInt32 loopCounter;

                    offset += DecodeInstrCountValue(&packedData[offset], &commonSize);
                    offset += DecodeVCountValue(&packedData[offset], &customSize);
                    offset += DecodeVCountValue(&packedData[offset], &repeatCount);

                    commonData = &packedData[offset];
                    customData = &packedData[offset + commonSize];

                    for (loopCounter = 0; loopCounter < repeatCount; loopCounter++) {
                        BlockMoveData(commonData, unpackCursor, commonSize);
                        unpackCursor += commonSize;
                        BlockMoveData(customData, unpackCursor, customSize);
                        unpackCursor += customSize;
                        customData += customSize;
                    }
                    BlockMoveData(commonData, unpackCursor, commonSize);
                    unpackCursor += commonSize;
                    offset += (repeatCount * (commonSize + customSize)) + commonSize;
                }
                break;

            case kPEFPkDataRepeatZero:
                {
                    UInt32                      commonSize;
                    UInt32                      customSize;
                    UInt32                      repeatCount;
                    const UInt8 *customData;
                    UInt32 loopCounter;

                    offset += DecodeInstrCountValue(&packedData[offset], &commonSize);
                    offset += DecodeVCountValue(&packedData[offset], &customSize);
                    offset += DecodeVCountValue(&packedData[offset], &repeatCount);

                    customData = &packedData[offset];

                    for (loopCounter = 0; loopCounter < repeatCount; loopCounter++) {
                        MoreBlockZero(unpackCursor, commonSize);
                        unpackCursor += commonSize;
                        BlockMoveData(customData, unpackCursor, customSize);
                        unpackCursor += customSize;
                        customData += customSize;
                    }
                    MoreBlockZero(unpackCursor, commonSize);
                    unpackCursor += commonSize;
                    offset += repeatCount * customSize;
                }
                break;

            default:
                #if MORE_DEBUG
                    DebugStr("\pUnpackPEFDataSection: Unexpected data opcode");
                #endif
                err = cfragFragmentCorruptErr;
                goto leaveNow;
                break;
        }
    }

leaveNow:
    return err;
}

/*      SetupSectionBaseAddresses Rationale
    -----------------------------------

    OK, here's where things get weird.  In order to run the relocation
    engine, I need to be able to find the base address of an instantiated
    section of the fragment we're fixing up given only its section number.
    This isn't hard for CFM to do because it's the one that instantiated the
    sections in the first place.  It's surprisingly difficult to do if
    you're not CFM.  [And you don't have access to the private CFM APis for
    doing it.]

    [Alan Lillich is going to kill me when he reads this!  I should point out
     that TVector's don't have to contain two words, they can be longer,
     and that the second word isn't necessarily a TOC pointer, it's
     just that the calling conventions require that it be put in the
     TOC register when the code is called.

     Furthermore, the code section isn't always section 0, and the data
     section isn't always section 1, and there can be zero to many sections
     of each type.

     But these niceties are besides the point: I'm doing something tricky
     because I don't have a nice API for getting section base addresses.
     If I had a nice API for doing that, none of this code would exist.
    ]

    The technique is very sneaky (thanks to Eric Grant).  The fragment to
    fix necessarily has a CFM init routine (because it needs that routine
    in order to capture the fragment location and connection ID).  Thus the
    fragment to fix must have a TVector in its data section.  TVectors are
    interesting because they're made up of two words.  The first is a pointer
    to the code that implements the routine; the second is a pointer to the TOC
    for the fragment that's exporting the TVector.  How TVectors are
    created is interesting too.  On disk, a TVector consists of two words,
    the first being the offset from the start of the code section to the
    routine, the second being the offset from the start of the data section
    to the TOC base.  When CFM prepares a TVector, it applies the following
    transform:

        tvector.codePtr = tvector.codeOffset + base of code section
        tvector.tocPtr  = tvector.tocOffset  + base of data section

    Now, you can reverse these questions to make them:

        base of code section = tvector.codePtr - tvector.codeOffset
        base of data section = tvector.dataPtr - tvector.dataOffset

    So if you can find the relocated contents of the TVector and
    find the original offsets that made up the TVector, you can then
    calculate the base address of both the code and data sections.

    Finding the relocated contents of the TVector is easy; I simply
    require the client to pass in a pointer to its init routine.
    A routine pointer is a TVector pointer, so you can just cast it
    and extract the pair of words.

    Finding the original offsets is a trickier.  My technique is to
    look up the init routine in the fragment's loader info header.  This
    yields the section number and offset where the init routine's unrelocated
    TVector exists.  Once I have that, I can just read the unrelocated TVector
    out of the file and extract the offsets.
*/

struct TVector {
    void *codePtr;
    void *tocPtr;
};
typedef struct TVector TVector;

static OSStatus SetupSectionBaseAddresses(FragToFixInfo *fragToFix)
    // This routine initialises the section0Base and section1Base
    // base fields of fragToFix to the base addresses of the
    // instantiated fragment represented by the other fields
    // of fragToFix.  The process works in three states:
    //
    // 1.       Find the contents of the relocated TVector of the
    //      fragment's initialisation routine, provided to us by
    //      the caller.
    //
    // 2.       Find the contents of the non-relocated TVector by
    //      looking it up in the PEF loader info header and then
    //      using that to read the TVector contents from disk.
    //      This yields the offsets from the section bases for
    //      the init routine.
    //
    // 3.       Subtract 2 from 3.
{
    OSStatus                            err;
    TVector *                           relocatedExport;
    SInt32                              initSection;
    UInt32                              initOffset;
    PEFSectionHeader *          initSectionHeader;
    Ptr                                         packedDataSection;
    Ptr                                         unpackedDataSection;
    TVector                             originalOffsets;

    packedDataSection   = nil;
    unpackedDataSection = nil;

    // Step 1.

    // First find the init routine's TVector, which gives us the relocated
    // offsets of the init routine into the data and code sections.

    relocatedExport = (TVector *) fragToFix->initRoutine;

    // Step 2.

    // Now find the init routine's TVector's offsets in the data section on
    // disk.  This gives us the raw offsets from the data and code section
    // of the beginning of the init routine.

    err = noErr;
    initSection = fragToFix->loaderSection->initSection;
    initOffset  = fragToFix->loaderSection->initOffset;
    if (initSection == -1) {
        err = cfragFragmentUsageErr;
    }
    if (err == noErr) {
        MoreAssertQ( initSection >= 0 );                        // Negative indexes are pseudo-sections which are just not allowed!
        MoreAssertQ( initSection < fragToFix->containerHeader.sectionCount );

        initSectionHeader = &fragToFix->sectionHeaders[initSection];

        // If the data section is packed, unpack it to a temporary buffer and then get the
        // original offsets from that buffer.  If the data section is unpacked, just read
        // the original offsets directly off the disk.

        if ( initSectionHeader->sectionKind == kPEFPackedDataSection ) {

            // Allocate space for packed and unpacked copies of the section.

            packedDataSection = NewPtr(initSectionHeader->containerLength);
            err = MemError();

            if (err == noErr) {
                unpackedDataSection = NewPtr(initSectionHeader->unpackedLength);
                err = MemError();
            }

            // Read the contents of the packed section.

            if (err == noErr) {
                err = FSReadAtOffset(                   fragToFix->fileRef,
                                                                fragToFix->locator.offset
                                                                + initSectionHeader->containerOffset,
                                                                initSectionHeader->containerLength,
                                                                packedDataSection);
            }

            // Unpack the data into the unpacked section.

            if (err == noErr) {
                err = UnpackPEFDataSection( (UInt8 *) packedDataSection,   initSectionHeader->containerLength,
                                                            (UInt8 *) unpackedDataSection, initSectionHeader->unpackedLength);
            }

            // Extract the init routine's TVector from the unpacked section.

            if (err == noErr) {
                BlockMoveData(unpackedDataSection + initOffset, &originalOffsets, sizeof(TVector));
            }

        } else {
            MoreAssertQ(fragToFix->sectionHeaders[initSection].sectionKind == kPEFUnpackedDataSection);
            err = FSReadAtOffset(fragToFix->fileRef,
                                                            fragToFix->locator.offset
                                                            + fragToFix->sectionHeaders[initSection].containerOffset
                                                            + initOffset,
                                                            sizeof(TVector),
                                                            &originalOffsets);
        }
    }

    // Step 3.

    // Do the maths to subtract the unrelocated offsets from the current address
    // to get the base address.

    if (err == noErr) {
        fragToFix->section0Base = ((char *) relocatedExport->codePtr) - (UInt32) originalOffsets.codePtr;
        fragToFix->section1Base = ((char *) relocatedExport->tocPtr)  - (UInt32) originalOffsets.tocPtr;
    }

    // Clean up.

    if (packedDataSection != nil) {
        DisposePtr(packedDataSection);
        MoreAssertQ( MemError() == noErr );
    }
    if (unpackedDataSection != nil) {
        DisposePtr(unpackedDataSection);
        MoreAssertQ( MemError() == noErr );
    }
    return err;
}

static void *GetSectionBaseAddress(const FragToFixInfo *fragToFix, UInt16 sectionIndex)
    // This routine returns the base of the instantiated section
    // whose index is sectionIndex.  This routine is the evil twin
    // of SetupSectionBaseAddresses.  It simply returns the values
    // for section 0 and 1 that we derived in SetupSectionBaseAddresses.
    // In a real implementation, this routine would call CFM API
    // to get this information, and SetupSectionBaseAddresses would
    // not exist, but CFM does not export the necessary APIs to
    // third parties.
{
    void *result;

    MoreAssertQ(fragToFix != nil);
    MoreAssertQ(fragToFix->containerHeader.tag1 == kPEFTag1);

    switch (sectionIndex) {
        case 0:
            result = fragToFix->section0Base;
            break;
        case 1:
            result = fragToFix->section1Base;
            break;
        default:
            result = nil;
            break;
    }
    return result;
}


static OSStatus FindImportLibrary(PEFLoaderInfoHeader *loaderSection, const char *libraryName, PEFImportedLibrary **importLibrary)
    // This routine finds the import library description (PEFImportedLibrary)
    // for the import library libraryName in the PEF loader section.
    // It sets *importLibrary to the address of the description.
{
    OSStatus                            err;
    UInt32                              librariesRemaining;
    PEFImportedLibrary          *thisImportLibrary;
    Boolean                             found;

    MoreAssertQ(loaderSection != nil);
    MoreAssertQ(libraryName != nil);
    MoreAssertQ(importLibrary != nil);

    // Loop through each import library looking for a matching name.

    // Initialise thisImportLibrary to point to the byte after the
    // end of the loader section's header.

    thisImportLibrary = (PEFImportedLibrary *) (loaderSection + 1);
    librariesRemaining = loaderSection->importedLibraryCount;
    found = false;
    while ( librariesRemaining > 0 && ! found ) {
        // PEF defines that import library names will have
        // a null terminator, so we can just use strcmp.
        found = (strcmp( libraryName,
                                        ((char *)loaderSection)
                                        + loaderSection->loaderStringsOffset
                                        + thisImportLibrary->nameOffset) == 0);
        // *** Remove ANSI strcmp eventually.
        if ( ! found ) {
            thisImportLibrary += 1;
            librariesRemaining -= 1;
        }
    }

    if (found) {
        *importLibrary = thisImportLibrary;
        err = noErr;
    } else {
        *importLibrary = nil;
        err = cfragNoLibraryErr;
    }
    return err;
}

static OSStatus LookupSymbol(CFMLateImportLookupProc lookup, void *refCon,
                                                        PEFLoaderInfoHeader *loaderSection,
                                                        UInt32 symbolIndex,
                                                        UInt32 *symbolValue)
    // This routine is used to look up a symbol during relocation.
    // "lookup" is a client callback and refCon is its argument.
    // Typically refCon is the CFM connection to the library that is
    // substituting for the weak linked library.  loaderSection
    // is a pointer to the loader section of the fragment to fix up.
    // symbolIndex is the index of the imported symbol in the loader section.
    // The routine sets the word pointed to by symbolValue to the
    // value of the symbol.
    //
    // The routine works by using symbolIndex to index into the imported
    // symbol table to find the offset of the symbol's name in the string
    // table.  It then looks up the symbol by calling the client's "lookup"
    // function and passes the resulting symbol address back in symbolValue.
{
    OSStatus                            err;
    UInt32                              *importSymbolTable;
    UInt32                              symbolStringOffset;
    Boolean                             symbolIsWeak;
    CFragSymbolClass            symbolClass;
    char                                *symbolStringAddress;
    Str255                              symbolString;

    MoreAssertQ(lookup != nil);
    MoreAssertQ(loaderSection != nil);
    MoreAssertQ(symbolIndex < loaderSection->totalImportedSymbolCount);
    MoreAssertQ(symbolValue != nil);

    // Find the base of the imported symbol table.

    importSymbolTable = (UInt32 *)(((char *)(loaderSection + 1)) + (loaderSection->importedLibraryCount * sizeof(PEFImportedLibrary)));

    // Grab the appropriate entry out of the table and
    // extract the information from that entry.

    symbolStringOffset = importSymbolTable[symbolIndex];
    symbolClass = PEFImportedSymbolClass(symbolStringOffset);
    symbolIsWeak = ((symbolClass & kPEFWeakImportSymMask) != 0);
    symbolClass = symbolClass & ~kPEFWeakImportSymMask;
    symbolStringOffset = PEFImportedSymbolNameOffset(symbolStringOffset);

    // Find the string for the symbol in the strings table and
    // extract it from the table into a Pascal string on the stack.

    symbolStringAddress = ((char *)loaderSection) + loaderSection->loaderStringsOffset + symbolStringOffset;
    symbolString[0] = strlen(symbolStringAddress);              // *** remove ANSI strlen
    BlockMoveData(symbolStringAddress, &symbolString[1], symbolString[0]);

    // Look up the symbol in substitute library.  If it fails, return
    // a 0 value and check whether the error is fatal (a strong linked
    // symbol) or benign (a weak linked symbol).

    err = lookup(symbolString, symbolClass, (void **) symbolValue, refCon);
    if (err != noErr) {
        *symbolValue = 0;
        if (symbolIsWeak) {
            err = noErr;
        }
    }
    return err;
}

// The EngineState structure encapsulates all of the persistent state
// of the CFM relocation engine virtual machine.  I originally defined
// this structure so I could pass the state around between routines
// that implement various virtual opcodes, however I later worked
// out that the relocation was sufficiently simple that I could put it
// in in one routine.  Still, I left the state in this structure in
// case I ever need to reverse that decision.  It's also a convenient
// instructional design.

struct EngineState {
    UInt32 currentReloc;                // Index of current relocation opcodes
    UInt32 terminatingReloc;            // Index of relocation opcodes which terminates relocation
    UInt32 *sectionBase;                // Start of the section
    UInt32 *relocAddress;               // Address within the section where the relocations are to be performed
    UInt32 importIndex;                         // Symbol index, which is used to access an imported symbol's address
    void  *sectionC;                            // Memory address of an instantiated section within the PEF container; this variable is used by relocation opcodes that relocate section addresses
    void  *sectionD;                            // Memory address of an instantiated section within the PEF container; this variable is used by relocation opcodes that relocate section addresses
};
typedef struct EngineState EngineState;

// Note:
// If I ever have to support the repeat opcodes, I'll probably
// have to add a repeat counter to EngineState.

static OSStatus InitEngineState(const FragToFixInfo *fragToFix,
                                                                UInt16 relocHeaderIndex,
                                                                EngineState *state)
    // This routine initialises the engine state suitably for
    // running the relocation opcodes for the section whose
    // index is relocHeaderIndex.  relocHeaderIndex is not a
    // a section number.  See the comment where it's used below
    // for details.  The routine basically fills out all the fields
    // in the EngineState structure as described by
    // "Mac OS Runtime Architectures".
{
    OSStatus err;
    PEFLoaderRelocationHeader *relocHeader;

    MoreAssertQ(fragToFix != nil);
    MoreAssertQ(state != nil);

    // This bit is tricky.  relocHeaderIndex is an index into the relocation
    // header table, starting at relocSectionCount (which is in the loader
    // section header) for the first relocated section and decrementing
    // down to 1 for the last relocated section.  I find the relocation
    // header by using relocHeaderIndex as a index backwards from the
    // start of the relocation opcodes (ie relocInstrOffset).  If you
    // look at the diagram of the layout of the container in
    // "PEFBinaryFormat.h", you'll see that the relocation opcodes
    // immediately follow the relocation headers.
    //
    // I did this because the alternative (starting at the loader
    // header and stepping past the import library table and the
    // import symbol table) was a pain.

    relocHeader = (PEFLoaderRelocationHeader *) (((char *) fragToFix->loaderSection) + fragToFix->loaderSection->relocInstrOffset - relocHeaderIndex * sizeof(PEFLoaderRelocationHeader));

    MoreAssertQ(relocHeader->reservedA == 0);                   // PEF spec says it must be; we check to try to catch bugs in calculation of relocHeader

    state->currentReloc = relocHeader->firstRelocOffset;
    state->terminatingReloc = relocHeader->firstRelocOffset + relocHeader->relocCount;
    state->sectionBase = (UInt32 *) GetSectionBaseAddress(fragToFix, relocHeader->sectionIndex);
    state->relocAddress = state->sectionBase;
    state->importIndex = 0;

    // From "Mac OS Runtime Architectures":
    //
    // The sectionC and sectionD variables actually contain the
    // memory address of an instantiated section minus the
    // default address for that section. The default address for a
    // section is contained in the defaultAddress field of the
    // section header. However, in almost all cases the default
    // address should be 0, so the simplified definition suffices.
    //
    // In the debug version, we drop into MacsBug if this weird case
    // ever executes because it's more likely we made a mistake than
    // we encountered a section with a default address.

    state->sectionC = GetSectionBaseAddress(fragToFix, 0);
    if (state->sectionC != nil) {
        #if MORE_DEBUG
            if (fragToFix->sectionHeaders[0].defaultAddress != 0) {
                DebugStr("\pInitEngineState: Executing weird case.");
            }
        #endif
        (char *) state->sectionC -= fragToFix->sectionHeaders[0].defaultAddress;
    }
    state->sectionD = GetSectionBaseAddress(fragToFix, 1);
    if (state->sectionD != nil) {
        #if MORE_DEBUG
            if (fragToFix->sectionHeaders[1].defaultAddress != 0) {
                DebugStr("\pInitEngineState: Executing weird case.");
            }
        #endif
        (char *) state->sectionD -= fragToFix->sectionHeaders[1].defaultAddress;
    }

    err = noErr;
    if (state->relocAddress == nil) {
        err = cfragFragmentUsageErr;
    }
    return err;
}

// kPEFRelocBasicOpcodes is a table that maps the top 7 bits of the opcode
// to a fundamental action.  It's contents are defined for me in "PEFBinaryFormat.h",
// which is really convenient.

static UInt8 kPEFRelocBasicOpcodes[kPEFRelocBasicOpcodeRange] = { PEFMaskedBasicOpcodes };

static OSStatus RunRelocationEngine(const FragToFixInfo *fragToFix,
                                                                                PEFImportedLibrary  *importLibrary,
                                                                                CFMLateImportLookupProc lookup, void *refCon)
    // This is where the rubber really hits the.  Given a fully
    // populated fragToFix structure, the import library description
    // of the weak imported library we're resolving, and a connection
    // to the library we're going to substitute it, re-execute the
    // relocation instructions (CFM has already executed them once)
    // but only *do* instructions (ie store the change to the data section)
    // that CFM skipped because the weak symbols were missing.
{
    OSStatus            err;
    EngineState         state;
    UInt16              sectionsLeftToRelocate;
    UInt32              totalRelocs;
    UInt16              *relocInstrTable;
    UInt16              opCode;

    MoreAssertQ(fragToFix != nil);
    MoreAssertQ(fragToFix->containerHeader.tag1 == kPEFTag1);
    MoreAssertQ(fragToFix->sectionHeaders != nil);
    MoreAssertQ(fragToFix->loaderSection != nil);
    MoreAssertQ(fragToFix->section0Base != nil);        // Technically, having a nil for these two is not a problem, ...
    MoreAssertQ(fragToFix->section1Base != nil);        // but in practise it a wildly deviant case and we should know about it.
    MoreAssertQ(importLibrary != nil);
    MoreAssertQ(lookup != nil);

    // Before entering the loop, work out some information in advance.

    // totalRelocs is only used for debugging, to make sure our
    // relocation PC (state.currentReloc) doesn't run wild.

    totalRelocs = (fragToFix->loaderSection->loaderStringsOffset - fragToFix->loaderSection->relocInstrOffset) / sizeof(UInt16);

    // relocInstrTable is the base address of the table of relocation
    // instructions in the fragment to fix.

    relocInstrTable = (UInt16 *)((char *) fragToFix->loaderSection + fragToFix->loaderSection->relocInstrOffset);

    // sectionsLeftToRelocate is the loop counter for the outer loop.

    MoreAssertQ(fragToFix->loaderSection->relocSectionCount <= 0x0FFFF);
    sectionsLeftToRelocate = fragToFix->loaderSection->relocSectionCount;

    // Now let's run the relocation engine.  We run it once per
    // section in the table.  Each time around, we init the engine
    // and then loop again, this time executing individual opcodes.
    // The opcode loop terminates when the relocation PC
    // (state.currentReloc) hits the final opcode (state.terminatingReloc).

    // Note:
    // One design decision I made was to totally re-init the engine state
    // for each section.  The CFM spec is unclear as to whether you're supposed
    // to totally re-init the engine state, or just re-init the section-specific
    // state (ie currentReloc, terminatingReloc, and relocAddress).  I hope this
    // is correct, but it's hard to test without having a fragment with multiple
    // relocated sections, which is difficult to create.

    // How do I decide which opcodes should be effective (ie make changes to
    // the section being relocated) and which opcodes should just be executed
    // for their side effects (ie updated state.relocAddress or state.importIndex)?
    // The answer is both simple and subtle.  Opcodes whose actions are dependent
    // on a symbol that was in the weak linked library are effective, those that
    // an independent of those symbols are not.  The only opcodes that use
    // symbolic values are kPEFRelocImportRun and kPEFRelocSmByImport, and
    // these are only if the symbol is in the weak linked library.
    // All other cases are executed for their side effects only.
    //
    // How do I determine if a symbol is in the weak linked library?
    // Well I know the symbol's index and I know the lower bound and count
    // of the symbols in the weak linked library, so I just do a simple
    // bounds test, ie
    //
    //   firstImportedSymbol <= importIndex < firstImportedSymbol + importedSymbolCount

    // From this code, it's relatively easy to see which relocation opcodes
    // aren't implemented.  If you ever encounter one, you'll find yourself
    // in MacsBug with a message telling you which opcode was found.  The
    // two big groups of opcodes I skipped were the large format opcodes
    // and the repeating opcodes.  I skipped them because:
    //
    // a) I haven't got a way to generate them in a PEF container that I can
    //    test against. Without that, there's no way I could be assured that
    //    the code worked.
    //
    // b) I'm lazy.

    err = noErr;
    while ( sectionsLeftToRelocate > 0 ) {
        err = InitEngineState(fragToFix, sectionsLeftToRelocate, &state);
        if (err != noErr) {
            goto leaveNow;
        }

        while ( state.currentReloc != state.terminatingReloc ) {

            MoreAssertQ( state.currentReloc < totalRelocs );

            opCode = relocInstrTable[state.currentReloc];
            switch ( PEFRelocBasicOpcode(opCode) ) {
                case kPEFRelocBySectDWithSkip:
                    {
                        UInt16 skipCount;
                        UInt16 relocCount;

                        skipCount = ((opCode >> 6) & 0x00FF);
                        relocCount = (opCode & 0x003F);
                        state.relocAddress += skipCount;
                        state.relocAddress += relocCount;
                    }
                    break;
                case kPEFRelocBySectC:
                case kPEFRelocBySectD:
                    {
                        UInt16 runLength;

                        runLength = (opCode & 0x01FF) + 1;
                        state.relocAddress += runLength;
                    }
                    break;
                case kPEFRelocTVector12:
                    {
                        UInt16 runLength;

                        runLength = (opCode & 0x01FF) + 1;
                        state.relocAddress += (runLength * 3);
                    }
                    break;
                case kPEFRelocTVector8:
                case kPEFRelocVTable8:
                    {
                        UInt16 runLength;

                        runLength = (opCode & 0x01FF) + 1;
                        state.relocAddress += (runLength * 2);
                    }
                    break;
                case kPEFRelocImportRun:
                    {
                        UInt32 symbolValue;
                        UInt16 runLength;

                        runLength = (opCode & 0x01FF) + 1;
                        while (runLength > 0) {
                            if ( state.importIndex >= importLibrary->firstImportedSymbol && state.importIndex < (importLibrary->firstImportedSymbol + importLibrary->importedSymbolCount) ) {
                                err = LookupSymbol(lookup, refCon, fragToFix->loaderSection, state.importIndex, &symbolValue);
                                if (err != noErr) {
                                    goto leaveNow;
                                }
                                *(state.relocAddress) += symbolValue;
                            }
                            state.importIndex += 1;
                            state.relocAddress += 1;
                            runLength -= 1;
                        }
                    }
                    break;
                case kPEFRelocSmByImport:
                    {
                        UInt32 symbolValue;
                        UInt32 index;

                        index = (opCode & 0x01FF);
                        if ( index >= importLibrary->firstImportedSymbol && index < (importLibrary->firstImportedSymbol + importLibrary->importedSymbolCount) ) {
                            err = LookupSymbol(lookup, refCon, fragToFix->loaderSection, index, &symbolValue);
                            if (err != noErr) {
                                goto leaveNow;
                            }
                            *(state.relocAddress) += symbolValue;
                        }
                        state.importIndex = index + 1;
                        state.relocAddress += 1;
                    }
                    break;
                case kPEFRelocSmSetSectC:
                    {
                        UInt32 index;

                        index = (opCode & 0x01FF);
                        state.sectionC = GetSectionBaseAddress(fragToFix, index);
                        MoreAssertQ(state.sectionC != nil);
                    }
                    break;
                case kPEFRelocSmSetSectD:
                    {
                        UInt32 index;

                        index = (opCode & 0x01FF);
                        state.sectionD = GetSectionBaseAddress(fragToFix, index);
                        MoreAssertQ(state.sectionD != nil);
                    }
                    break;
                case kPEFRelocSmBySection:
                    state.relocAddress += 1;
                    break;
                case kPEFRelocIncrPosition:
                    {
                        UInt16 offset;

                        offset = (opCode & 0x0FFF) + 1;
                        ((char *) state.relocAddress) += offset;
                    }
                    break;
                case kPEFRelocSmRepeat:
                    #if MORE_DEBUG
                        DebugStr("\pRunRelocationEngine: kPEFRelocSmRepeat not yet implemented");
                    #endif
                    err = unimpErr;
                    goto leaveNow;
                    break;
                case kPEFRelocSetPosition:
                    {
                        UInt32 offset;

                        // Lot's of folks have tried various interpretations of the description of
                        // this opCode in "Mac OS Runtime Architectures" (which states "This instruction
                        // sets relocAddress to the address of the section offset offset."  *smile*).
                        // I eventually dug into the CFM source code to find my interpretation, which
                        // I believe is correct.  The key point is tht the offset is relative to
                        // the start of the section for which these relocations are being performed.

                        // Skip to next reloc word, which is the second chunk of the offset.

                        state.currentReloc += 1;

                        // Extract offset based on the most significant 10 bits in opCode and
                        // the next significant 16 bits in the next reloc word.

                        offset = PEFRelocSetPosFullOffset(opCode, relocInstrTable[state.currentReloc]);

                        state.relocAddress = (UInt32 *) ( ((char *) state.sectionBase) + offset);
                    }
                    break;
                case kPEFRelocLgByImport:
                    {
                        UInt32 symbolValue;
                        UInt32 index;

                        // Get the 26 bit symbol index from the current and next reloc words.

                        state.currentReloc += 1;
                        index = PEFRelocLgByImportFullIndex(opCode, relocInstrTable[state.currentReloc]);

                        if ( index >= importLibrary->firstImportedSymbol && index < (importLibrary->firstImportedSymbol + importLibrary->importedSymbolCount) ) {
                            err = LookupSymbol(lookup, refCon, fragToFix->loaderSection, index, &symbolValue);
                            if (err != noErr) {
                                goto leaveNow;
                            }
                            *(state.relocAddress) += symbolValue;
                        }
                        state.importIndex = index + 1;
                        state.relocAddress += 1;
                    }
                    break;
                case kPEFRelocLgRepeat:
                    #if MORE_DEBUG
                        DebugStr("\pRunRelocationEngine: kPEFRelocLgRepeat not yet implemented");
                    #endif
                    err = unimpErr;
                    goto leaveNow;
                    break;
                case kPEFRelocLgSetOrBySection:
                    #if MORE_DEBUG
                        DebugStr("\pRunRelocationEngine: kPEFRelocLgSetOrBySection not yet implemented");
                    #endif
                    err = unimpErr;
                    goto leaveNow;
                    break;
                case kPEFRelocUndefinedOpcode:
                    err = cfragFragmentCorruptErr;
                    goto leaveNow;
                    break;
                default:
                    MoreAssertQ(false);
                    err = cfragFragmentCorruptErr;
                    goto leaveNow;
                    break;
            }
            state.currentReloc += 1;
        }

        sectionsLeftToRelocate -= 1;
    }

leaveNow:
    return err;
}

extern pascal OSStatus CFMLateImportCore(const CFragSystem7DiskFlatLocator *fragToFixLocator,
                                                                                CFragConnectionID fragToFixConnID,
                                                                                CFragInitFunction fragToFixInitRoutine,
                                                                                ConstStr255Param weakLinkedLibraryName,
                                                                                CFMLateImportLookupProc lookup,
                                                                                void *refCon)
    // See comments in interface part.
{
    OSStatus err;
    OSStatus junk;
    FragToFixInfo fragToFix;
    PEFImportedLibrary *importLibrary;
    char weakLinkedLibraryNameCString[256];

    MoreAssertQ(fragToFixLocator != nil);
    MoreAssertQ(fragToFixConnID != nil);
    MoreAssertQ(fragToFixInitRoutine != nil);
    MoreAssertQ(weakLinkedLibraryName != nil);
    MoreAssertQ(lookup != nil);

    // Fill out the bits of fragToFix which are passed in
    // by the client.

    MoreBlockZero(&fragToFix, sizeof(fragToFix));
    fragToFix.locator = *fragToFixLocator;
    fragToFix.connID  = fragToFixConnID;
    fragToFix.initRoutine = fragToFixInitRoutine;

    // Make a C string from weakLinkedLibraryName.

    BlockMoveData(weakLinkedLibraryName + 1, weakLinkedLibraryNameCString, weakLinkedLibraryName[0]);
    weakLinkedLibraryNameCString[weakLinkedLibraryName[0]] = 0;

    // Get the basic information from the fragment.
    // Fills out the containerHeader, sectionHeaders, loaderSection and fileRef fields
    // of fragToFix.

    err = ReadContainerBasics(&fragToFix);

    // Set up the base address fields in fragToFix (ie section0Base and section1Base)
    // by looking up our init routine (fragToFix.initRoutine) and subtracting
    // away the section offsets (which we get from the disk copy of the section)
    // to derive the bases of the sections themselves.

    if (err == noErr) {
        err = SetupSectionBaseAddresses(&fragToFix);
    }

    // Look inside the loader section for the import library description
    // of weakLinkedLibraryName.  We need this to know the range of symbol
    // indexes we're going to fix up.

    if (err == noErr) {
        err = FindImportLibrary(fragToFix.loaderSection, weakLinkedLibraryNameCString, &importLibrary);
    }

    // Do a quick check to ensure that the library was actually imported weak.
    // If it wasn't, it doesn't make much sense to resolve its weak imports
    // later on.  Resolving them again is likely to be bad.

    if (err == noErr) {
        if ((importLibrary->options & kPEFWeakImportLibMask) == 0) {
            err = cfragFragmentUsageErr;
        }
    }

    // Now run the main relocation engine.

    if (err == noErr) {
        err = RunRelocationEngine(&fragToFix, importLibrary, lookup, refCon);
    }

    // Clean up.

    if (fragToFix.disposeSectionPointers) {
        if (fragToFix.fileRef != 0) {
            junk = FSClose(fragToFix.fileRef);
            MoreAssertQ(junk == noErr);
        }
        if (fragToFix.loaderSection != nil) {
            DisposePtr( (Ptr) fragToFix.loaderSection);
            MoreAssertQ(MemError() == noErr);
        }
        if (fragToFix.sectionHeaders != nil) {
            DisposePtr( (Ptr) fragToFix.sectionHeaders);
            MoreAssertQ(MemError() == noErr);
        }
    }
    return err;
}

static pascal OSStatus FragmentLookup(ConstStr255Param symName, CFragSymbolClass symClass,
                                                                        void **symAddr, void *refCon)
    // This is the CFMLateImportLookupProc callback used when
    // late importing from a CFM shared library.
{
    OSStatus err;
    CFragConnectionID connIDToImport;
    CFragSymbolClass  foundSymClass;

    MoreAssertQ(symName != nil);
    MoreAssertQ(symAddr != nil);
    MoreAssertQ(refCon  != nil);

    connIDToImport = (CFragConnectionID) refCon;

    // Shame there's no way to validate that connIDToImport is valid.

    err = FindSymbol(connIDToImport, symName, (Ptr *) symAddr, &foundSymClass);
    if (err == noErr) {
        // If the symbol isn't of the right class, we act like we didn't
        // find it, but also assert in the debug build because weird things
        // are afoot.
        if (foundSymClass != symClass) {
            MoreAssertQ(false);
            *symAddr = nil;
            err = cfragNoSymbolErr;
        }
    }
    return err;
}

extern pascal OSStatus CFMLateImportLibrary(const CFragSystem7DiskFlatLocator *fragToFixLocator,
                                                                                CFragConnectionID fragToFixConnID,
                                                                                CFragInitFunction fragToFixInitRoutine,
                                                                                ConstStr255Param weakLinkedLibraryName,
                                                                                CFragConnectionID connIDToImport)
    // See comments in interface part.
{
    MoreAssertQ(connIDToImport != nil);
    return CFMLateImportCore(fragToFixLocator, fragToFixConnID, fragToFixInitRoutine,
                                                                            weakLinkedLibraryName, FragmentLookup, connIDToImport);
}

static pascal OSStatus BundleLookup(ConstStr255Param symName, CFragSymbolClass symClass,
                                                                        void **symAddr, void *refCon)
    // This is the CFMLateImportLookupProc callback used when
    // late importing from a CFBundle.
{
    OSStatus            err;
    CFBundleRef bundleToImport;
    CFStringRef symNameStr;

    MoreAssertQ(symName != nil);
    MoreAssertQ(symAddr != nil);
    MoreAssertQ(refCon  != nil);

    symNameStr = nil;

    bundleToImport = (CFBundleRef) refCon;

    // Shame there's no way to validate that bundleToImport is really a bundle.

    // We can only find function pointers because CFBundleGetFunctionPointerForName
    // only works for function pointers.  So if the client is asking for something
    // other than a function pointer (ie TVector symbol) then we don't even true.
    // Also assert in the debug build because this shows a certain lack of
    // understanding on the part of the client.
    //
    // CF is being revise to support accessing data symbols using a new API
    // (currently this is available to Apple internal developers as
    // CFBundleGetDataPointerForName).  When the new API is available in a
    // public header file I should revise this code to lift this restriction.

    err = noErr;
    if (symClass != kTVectorCFragSymbol) {
        MoreAssertQ(false);
        err = cfragNoSymbolErr;
    }
    if (err == noErr) {
        symNameStr = CFStringCreateWithPascalString(kCFAllocatorSystemDefault,
                                                                                                symName, kCFStringEncodingMacRoman);
        if (symNameStr == nil) {
            err = coreFoundationUnknownErr;
        }
    }
    if (err == noErr) {
        *symAddr = CFBundleGetFunctionPointerForName(bundleToImport, symNameStr);
        if (*symAddr == nil) {
            err = cfragNoSymbolErr;
        }
    }
    if (symNameStr != nil) {
        CFRelease(symNameStr);
    }
    return err;
}

extern pascal OSStatus CFMLateImportBundle(const CFragSystem7DiskFlatLocator *fragToFixLocator,
                                                                                CFragConnectionID fragToFixConnID,
                                                                                CFragInitFunction fragToFixInitRoutine,
                                                                                ConstStr255Param weakLinkedLibraryName,
                                                                                CFBundleRef bundleToImport)
    // See comments in interface part.
{
    MoreAssertQ(bundleToImport != nil);
    return CFMLateImportCore(fragToFixLocator, fragToFixConnID, fragToFixInitRoutine,
                                                                            weakLinkedLibraryName, BundleLookup, bundleToImport);
}