symbian-qemu-0.9.1-12/python-2.6.1/Mac/Modules/cg/CFMLateImport.c
changeset 1 2fb8b9db1c86
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/symbian-qemu-0.9.1-12/python-2.6.1/Mac/Modules/cg/CFMLateImport.c	Fri Jul 31 15:01:17 2009 +0100
@@ -0,0 +1,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);
+}