mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1360 lines
		
	
	
	
		
			48 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			1360 lines
		
	
	
	
		
			48 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable file
		
	
	
	
	
/*
 | 
						|
	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);
 | 
						|
}
 |