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; }
pascal void MoreAEDisposeDesc(AEDesc *desc){ OSStatus junk; MoreAssertQ(desc != nil); junk = AEDisposeDesc(desc); MoreAssertQ(junk == noErr); MoreAENullDesc(desc); }
extern pascal DriveFlagsPtr MoreGetDriveFlags(DrvQElPtr drvQEl) // See comment in interface part. { MoreAssertQ(drvQEl != nil); return ((DriveFlagsPtr) drvQEl) - 1; }
static pascal Boolean MoreDriveSupportFileExchangeInternal(DriverRefNum refNum, SInt16 drive) // An internal version of MoreDriveSupportFileExchange that allows // you to pass in the refNum and the drive number. You can pass // in 0 for either refNum or drive (but not both) and the routine // will do the appropriate mapping. { DriverGestaltParam pb; Boolean result; MoreAssertQ( (refNum < 0 && drive >= 0 && (drive == 0 || MoreGetDriveRefNum(drive) == refNum)) || (refNum == 0 && drive > 0) ); if (refNum == 0) { refNum = MoreGetDriveRefNum(drive); } result = false; if ( MoreDriveSupportsDriverGestaltInternal(refNum) ) { pb.ioVRefNum = drive; pb.ioCRefNum = refNum; pb.csCode = kDriverGestaltCode; pb.driverGestaltSelector = kdgAPI; if ( PBStatusSync((ParmBlkPtr) &pb) == noErr && GetDriverGestaltAPIResponse(&pb)->partitionCmds & 0x01 ) { result = true; } } return result; }
//******************************************************************************* // Initialises desc to the null descriptor (typeNull, nil). pascal void MoreAENullDesc(AEDesc* desc) { MoreAssertQ(desc != nil); desc->descriptorType = typeNull; desc->dataHandle = nil; }//end MoreAENullDesc
/****************************************************************************** Returns the name of the process specified by ProcessSerialNumberPtr. The string pointed to by pProcessName will be untouched if the process can't be found. pPSN input: The process whose name you want (nil for current). pProcessName input: Pointer to a Str31 for the process name. output: The process name. RESULT CODES ____________ noErr 0 No error paramErr � Process serial number is invalid ____________ */ pascal OSStatus MoreProcGetProcessName( const ProcessSerialNumberPtr pPSN, StringPtr pProcessName) { OSStatus anErr = noErr; ProcessInfoRec infoRec; ProcessSerialNumber localPSN; MoreAssertQ(pProcessName != nil); infoRec.processInfoLength = sizeof(ProcessInfoRec); infoRec.processName = pProcessName; #ifndef __LP64__ infoRec.processAppSpec = nil; #endif if ( pPSN == nil ) { localPSN.highLongOfPSN = 0; localPSN.lowLongOfPSN = kCurrentProcess; } else { localPSN = *pPSN; } anErr = GetProcessInformation(&localPSN, &infoRec); return anErr; }//end MoreProcGetProcessName
extern pascal OSErr MoreUTFindDriveQ(SInt16 drive, DrvQElPtr *foundDrvQEl) // See comment in interface part. { OSErr err; UInt32 fsmVers; MoreAssertQ(drive > 0); MoreAssertQ(foundDrvQEl != nil); // Check to see whether we have a useful version of FSM. Versions of FSM // prior to 1.2 do not support the documented FSM API, so we just treat // them as if FSM wasn't installed. if ((Gestalt(gestaltFSMVersion, (SInt32 *) &fsmVers) == noErr) && (fsmVers >= 0x0120)) { // We have FSM, let's call its version of UTFindDrive, // and handle the weirdo error we get for non-HFS disks. err = MoreUTFindDrive(drive, foundDrvQEl); if (err == extFSErr) { err = noErr; } } else { DrvQElPtr thisDrv; // No FSM, let's go poking around in low memory )-: *foundDrvQEl = nil; thisDrv = (DrvQElPtr) GetDrvQHdr()->qHead; while (thisDrv != nil && *foundDrvQEl == nil) { if (thisDrv->dQDrive == drive) { *foundDrvQEl = thisDrv; } else { thisDrv = (DrvQElPtr) thisDrv->qLink; } } if (*foundDrvQEl == nil) { err = nsDrvErr; } else { err = noErr; } } return err; }
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; }
extern pascal SInt16 MoreFindFreeDriveNumber(SInt16 firstDrive) // See comment in interface part. { SInt16 candidate; DrvQElPtr junkDrvQElPtr; MoreAssertQ(firstDrive >= 5); candidate = firstDrive; while ( MoreUTFindDriveQ(candidate, &junkDrvQElPtr) == noErr ) { candidate += 1; } // This post condition checks that we didn't wrap // around to a negative drive number. MoreAssertQ(candidate >= 5); return candidate; }
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 pascal Boolean MoreDriveSupportsDriverGestaltInternal(DriverRefNum refNum) // An internal version of MoreDriveSupportsDriverGestalt that allows // you to pass in the refNum and the drive number. You can pass // in 0 for either refNum or drive (but not both) and the routine // will do the appropriate mapping. { OSErr junk; DriverFlags driverFlags; junk = TradGetDriverInformation(refNum, nil, &driverFlags, nil, nil); MoreAssertQ(junk == noErr); return TradDriverGestaltIsOn(driverFlags); }
extern pascal DriverRefNum MoreGetDriveRefNum(SInt16 drive) // See comment in interface part. { DrvQElPtr foundDrvQEl; MoreAssertQ(drive > 0); if ( MoreUTFindDriveQ(drive, &foundDrvQEl) == noErr) { return foundDrvQEl->dQRefNum; } else { return 0; } }
extern pascal DrvQElPtr MoreGetIndDrive(SInt16 index) // See comment in interface part. { DrvQElPtr thisDrv; SInt16 thisDrvIndex; MoreAssertQ(index > 0); thisDrvIndex = 1; thisDrv = (DrvQElPtr) GetDrvQHdr()->qHead; while (thisDrv != nil && thisDrvIndex != index) { thisDrvIndex += 1; thisDrv = (DrvQElPtr) thisDrv->qLink; } return thisDrv; }
extern pascal Boolean MoreProcIsProcessAtFront(const ProcessSerialNumber *pPSN) // See comment in header. { OSStatus anErr; ProcessSerialNumber localPSN; ProcessSerialNumber frontPSN; Boolean isSame; if ( pPSN == nil ) { localPSN.highLongOfPSN = 0; localPSN.lowLongOfPSN = kCurrentProcess; } else { localPSN = *pPSN; } anErr = GetFrontProcess(&frontPSN); if (noErr == anErr) { anErr = SameProcess(&localPSN, &frontPSN, &isSame); } MoreAssertQ(noErr == anErr); // If either of the previous return an error, we want to know why. return (noErr == anErr) && isSame; }
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 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; }
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; }
extern pascal OSErr MoreGetDriveSize(SInt16 drive, UInt32 *sizeInBlocks) // See comment in interface part. { OSErr err; DrvQElPtr drvQEl; CntrlParam pb; FormatListRec formatList[kFormatListEntryCount]; SInt16 formatIndex; Boolean foundFormat; Str255 driverName; DrvSts status; MoreAssertQ(drive > 0); MoreAssertQ(sizeInBlocks != nil); // Start by finding the drive queue element for // the drive, and by making sure that there's a disk // in the drive. err = MoreUTFindDriveQ(drive, &drvQEl); if (err == noErr) { if ( MoreGetDriveFlags(drvQEl)->diskInPlace <= 0 ) { err = offLinErr; } } // Wow, this is harder than it should be, all because of the // silly ".Sony" driver. The basic problem is that // the ".Sony" driver doesn't store the disk size in the // drive queue element like every other disk drive on the // planet. The solution is a three step process as described // in the comments below. if (err == noErr) { // Step 1. If the driver supports the kReturnFormatList status call, // use it to get a list of formats for the drive and then // return the format marked as current. pb.ioNamePtr = nil; pb.ioVRefNum = drvQEl->dQDrive; pb.ioCRefNum = drvQEl->dQRefNum; pb.csCode = kReturnFormatList; pb.csParam[0] = kFormatListEntryCount; *((FormatListRec **) &pb.csParam[1]) = formatList; err = PBStatusSync( (ParmBlkPtr) &pb); if (err == noErr) { foundFormat = false; for (formatIndex = 0; formatIndex < pb.csParam[0]; formatIndex++) { if ((formatList[formatIndex].formatFlags & diCIFmtFlagsCurrentMask) != 0) { *sizeInBlocks = formatList[formatIndex].volSize; foundFormat = true; } } if ( ! foundFormat ) { // Hmmm, this isn't good. The disk driver returned a format // list but none of the formats were marked as "current". // We handle this correctly but, in debug builds, we'll also // drop into MacsBug, just to let you know this is happening. MoreAssertQ(false); err = paramErr; } } else { // ¥¥¥ The logic here is slightly screwed up. The problem is that // I can't tell whether the kReturnFormatList call failed because // the driver just doesn't support it, or because the driver failed // to get the information for some other reason. If that driver // happens to be the ".Sony" driver, I'm going to take a wrong step // next. // // For example, say that there's a 1.4MB disk in the floppy drive // and I call kReturnFormatList and it fails with an error because // of a cosmic ray. I then test the driver name, find that it's // ".Sony", call DriveStatus, and then return noErr with a size // of either 400KB or 800KB. Not good. // // You might think this is an unlikely occurence, but it's exactly // what happens when there's no disk in the floppy drive. I've // special-cased that away above, but the general problem still stands. // // What are the alternatives? I could special case the error // result from kReturnFormatList and only run this code if I get // statusErr. But can you guarantee that all ".Sony" drives // return statusErr for an unrecognised status code? I thought not. // Beyond that, I can't think of any options. So this code // stands. It's probably never going to bite anyone, but it's // worth noting here, just in case. Besides, this is what // the equivalent routine in MoreFiles does (-: // // -- Quinn, 3 Mar 1999 // Step 2. If that doesn't work, then look at the driver. If it's // the ".Sony" driver (and this will be a really old ".Sony" driver // because new ones support kReturnFormatList), special case // the possible media types. err = TradGetDriverInformation(drvQEl->dQRefNum, nil, nil, driverName, nil); if (err == noErr) { if ( EqualString(driverName, "\p.Sony", false, true) ) { err = DriveStatus(drvQEl->dQDrive, &status); if (err == noErr) { if ( status.twoSideFmt == 0 ) { *sizeInBlocks = 400 * 2; } else { *sizeInBlocks = 800 * 2; } } } else { // Step 3. If it's not the ".Sony" driver, get the size out of the // drive queue element. if (drvQEl->qType == 0) { // Old style drive, with just 16 bits of size information // in dQDrvSz. *sizeInBlocks = drvQEl->dQDrvSz; } else { // New style drive, with 32 bits of size information spread // between dQDrvSz and dQDrvSz2. *sizeInBlocks = (((UInt32 ) drvQEl->dQDrvSz2) * 65536) + (UInt32 ) drvQEl->dQDrvSz; } } }
extern pascal Boolean MoreDriveSupportFileExchange(SInt16 drive) // See comment in interface part. { MoreAssertQ(drive > 0); return MoreDriveSupportFileExchangeInternal(0, drive); }
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; }