273 lines
14 KiB
C
273 lines
14 KiB
C
/*
|
|
File: CFMLateImport.h
|
|
|
|
Contains: Interface to 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):
|
|
|
|
<6> 21/9/01 Quinn Changes for CWPro7 Mach-O build.
|
|
<5> 19/9/01 Quinn Change comments to reflect the fact that an unpacked data
|
|
section is no longer required.
|
|
<4> 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.
|
|
<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.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
// MoreIsBetter Setup
|
|
|
|
//#include "MoreSetup.h"
|
|
|
|
// Mac OS Interfaces
|
|
|
|
#if ! MORE_FRAMEWORK_INCLUDES
|
|
#include <MacTypes.h>
|
|
#include <CodeFragments.h>
|
|
#include <Devices.h>
|
|
#include <CFBundle.h>
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/* FAQ
|
|
---
|
|
|
|
Q: What does this library do?
|
|
A: It allows you to resolve a weak linked library at runtime,
|
|
by supply a CFM connection to the library that should substitute
|
|
for the weak linked one.
|
|
|
|
Q: Does the substituted library have to have the same name as the
|
|
weak linked library.
|
|
A: No.
|
|
|
|
Q: What's this useful for?
|
|
A: The most obvious example of where this is useful is when
|
|
you rely on shared libraries that the user might delete
|
|
or move. To can find the shared library (possibly even
|
|
using CatSearch), call GetDiskFragment to open a connection
|
|
to it, late import it using this library, and then the
|
|
rest of your code can continue to use the shared library
|
|
as if nothing had happened. No more defining thousands
|
|
of stub routines which call through routine pointers.
|
|
|
|
There are, however, numerous less obvious uses. You can
|
|
use this code to make a 'self repairing' application. If
|
|
the user removes your shared library from the Extensions
|
|
folder, the startup code for your application can offer
|
|
tor re-install it. If the user agrees, you can then
|
|
re-install your shared library, late import it, and then
|
|
continue running your application if nothing happened.
|
|
|
|
You can even use this code to free yourself from the
|
|
Extensions folder entirely. Say you have a suite of
|
|
applications that currently installs a dozen shared
|
|
libraries in the Extensions folder. You can move those
|
|
libraries to another folder entirely and each application's
|
|
startup code can track down the library (using an alias
|
|
in the Preferences file) and late import it.
|
|
|
|
An even cooler use is to provide easy abstraction layers.
|
|
Say you have a network code for both the MacTCP
|
|
API and the Open Transport API. Typically, you would be
|
|
force to do this by having an abstraction layer where every
|
|
routine contains a switch between MacTCP and OT. Your
|
|
OpenSocket routine might look like:
|
|
|
|
static int OpenSocket(void)
|
|
{
|
|
if (gOTAvailable) {
|
|
return OpenSocketOT();
|
|
} else {
|
|
return OpenSocketMacTCP();
|
|
}
|
|
}
|
|
|
|
With this code, you can avoid that entirely. Simply
|
|
weak link to a shared library that you know is never
|
|
going to be implemented ("crea;MySocketsDummy") and then,
|
|
at runtime, decide whether the system has MacTCP or OT
|
|
and late import the relevant real implementation
|
|
("crea;MySocketsMacTCP" or "crea;MySocketsOT").
|
|
One benefit of this approach is that only the MacTCP or
|
|
the OT code is resident in memory on any given system.
|
|
*/
|
|
|
|
typedef pascal OSStatus (*CFMLateImportLookupProc)(ConstStr255Param symName, CFragSymbolClass symClass,
|
|
void **symAddr, void *refCon);
|
|
// CFMLateImportLookupProc defines a callback for CFMLateImportCore.
|
|
// The routine is expected to look up the address of the symbol named
|
|
// symName and return it in *symAddr. The symbol should be of class
|
|
// symClass, although the callback decides whether a class mismatch is
|
|
// an error. refCon is an application defined value that was originally
|
|
// passed in to CFMLateImportCore.
|
|
//
|
|
// If this routine returns an error, a symbol address of 0 is assumed.
|
|
// If the symbol is marked as a weak import, the CFMLateImportCore will
|
|
// continue, otherwise the CFMLateImportCore routine will fail with the
|
|
// error.
|
|
|
|
extern pascal OSStatus CFMLateImportCore(const CFragSystem7DiskFlatLocator *fragToFixLocator,
|
|
CFragConnectionID fragToFixConnID,
|
|
CFragInitFunction fragToFixInitRoutine,
|
|
ConstStr255Param weakLinkedLibraryName,
|
|
CFMLateImportLookupProc lookup,
|
|
void *refCon);
|
|
// This routine will link you, at runtime, to some library
|
|
// that you were weak linked to and wasn't present when your
|
|
// fragment was prepared. As well as the obvious functionality
|
|
// of being able to resolve weak links after prepare time,
|
|
// this functionality can be put to a number of less obvious uses,
|
|
// some of which are discussed at the top of this header file.
|
|
//
|
|
// To call this routine, you need a number of pieces of information:
|
|
//
|
|
// 1. fragToFixLocator, fragToFixConnID: The location of your own
|
|
// code fragment on disk and the CFM connection ID to your own
|
|
// code fragment. Typically you get this information from your
|
|
// fragment's CFM init routine. You must ensure that
|
|
// fragToFixLocator->fileSpec points to an FSSpec of the
|
|
// file which holds your code fragment.
|
|
//
|
|
// IMPORTANT:
|
|
// The fact that you pass in a CFragSystem7DiskFlatLocator as the
|
|
// fragToFixLocator implies that the fragment to be fixed up must
|
|
// be in the data fork of a file. The code could be modified
|
|
// to remove this requirement, but on disk code fragments are the most
|
|
// common case.
|
|
//
|
|
// IMPORTANT:
|
|
// The fragment to fix may have a packed data section. Packing the
|
|
// data section will reduce the size of your fragment on disk, but it
|
|
// will significantly increase the memory needed by this routine
|
|
// (it increases memory usage by the sum of the sizes of the packed
|
|
// and unpacked data section). See below for instructions on how to
|
|
// create an unpacked data section.
|
|
//
|
|
// 2. fragToFixInitRoutine: A pointer to your own code fragment's
|
|
// fragment initialiser routine. You necessarily have one of these
|
|
// because you need it to get values for the fragToFixLocator and
|
|
// fragToFixConnID parameters. Just pass its address in as a parameter
|
|
// as well.
|
|
//
|
|
// 3. weakLinkedLibraryName: The name of the weak linked library which
|
|
// failed to link. You must have weak linked to this library.
|
|
// It is oxymoric for you to pass a strong linked library here,
|
|
// because your code would not have prepared if a strong linked
|
|
// library failed to prepare, and so you couldn't supply a valid
|
|
/// fragToFix.
|
|
//
|
|
// 4. lookup, refCon: A pointer to a callback function that the
|
|
// routine calls to look up the address of a symbol, and a refCon
|
|
// for that callback routine.
|
|
//
|
|
// Note:
|
|
// The fragToFixLocator and fragToFixInitRoutine parameters
|
|
// are artifacts of the way in which this functionality is implemented.
|
|
// In an ideal world, where CFM exported decent introspection APIs
|
|
// to third party developers, these parameters would not be necessary.
|
|
// If you're using this code inside Apple, you probably should investigate
|
|
// using the CFM private APIs for getting at the information these
|
|
// parameters are needed for. See the comments inside the implementation
|
|
// for more details.
|
|
//
|
|
// Note:
|
|
// The extra memory taken when you use a packed data section is also an
|
|
// artifact of my workaround for the lack of CFM introspection APIs. In
|
|
// my opinion it's better to use an unpacked data section and consume more
|
|
// space on disk while saving memory. In CodeWarrior you can switch to an
|
|
// unpacked data section by checking the "Expand Uninitialized Data"
|
|
// checkbox in the "PPC PEF" settings panel. In MPW, specified the
|
|
// "-packdata off" option to PPCLink.
|
|
//
|
|
// When the routine returns, any symbols that you imported from the
|
|
// library named weakLinkedLibraryName will be resolved to the address
|
|
// of the symbol provided by the "lookup" callback routine.
|
|
//
|
|
// It is possible for an unresolved import to remain unresolved after
|
|
// this routine returns. If the symbol import is marked as weak (as
|
|
// opposed to the library, which *must* be marked as weak) and the symbol
|
|
// is not found by the "lookup" callback, the routine will simple skip
|
|
// that symbol. If the symbol isn't marked as weak, the routine will fail
|
|
// in that case.
|
|
//
|
|
// Most of the possible error results are co-opted CFM errors. These
|
|
// include:
|
|
//
|
|
// cfragFragmentFormatErr -- The fragment to fix is is an unknown format.
|
|
// cfragNoSectionErr -- Could not find the loader section in the fragment to fix.
|
|
// cfragNoLibraryErr -- The fragment to fix is not weak linked to weakLinkedLibraryName.
|
|
// cfragFragmentUsageErr -- The fragment to fix doesn't have a data section.
|
|
// -- The fragment to fix is strong linked to weakLinkedLibraryName.
|
|
// -- The fragment doesn't have an init routine.
|
|
// cfragFragmentCorruptErr -- Encountered an undefined relocation opcode.
|
|
// unimpErr -- Encountered an unimplement relocation opcode. The
|
|
// relocation engine only implements a subset of the CFM
|
|
// relocation opcodes, the subset most commonly used by
|
|
// MPW and CodeWarrior PEF containers. If you encounter
|
|
// this error, you'll probably have to add the weird
|
|
// relocation opcode to the engine, which shouldn't be
|
|
// be too hard.
|
|
// memFullErr -- It's likely that this error is triggered by the memory
|
|
// needed to unpack your data section. Either make your
|
|
// data section smaller, or unpack it (see above).
|
|
// errors returned by FindSymbol
|
|
// errors returned by Memory Manager
|
|
//
|
|
// The routine needs enough memory to hold the loader section of the fragment
|
|
// to fix in memory. It allocates that memory using NewPtr and dispsoses of
|
|
// it before it returns. You may want to change the memory allocator, which
|
|
// is very simple.
|
|
|
|
extern pascal OSStatus CFMLateImportLibrary(const CFragSystem7DiskFlatLocator *fragToFixLocator,
|
|
CFragConnectionID fragToFixConnID,
|
|
CFragInitFunction fragToFixInitRoutine,
|
|
ConstStr255Param weakLinkedLibraryName,
|
|
CFragConnectionID connIDToImport);
|
|
// A wrapper around CFMLateImportCore that looks up symbols by calling
|
|
// FindSymbol on a connection to a CFM library (connIDToImport).
|
|
// You can get this connection ID through any standard CFM API, for example
|
|
// GetSharedLibrary, GetDiskFragment, or GetMemFragment.
|
|
//
|
|
// IMPORTANT:
|
|
// The fragment name for connIDToImport *does not* have to match
|
|
// weakLinkedLibraryName. This is part of the power of this library.
|
|
|
|
extern pascal OSStatus CFMLateImportBundle(const CFragSystem7DiskFlatLocator *fragToFixLocator,
|
|
CFragConnectionID fragToFixConnID,
|
|
CFragInitFunction fragToFixInitRoutine,
|
|
ConstStr255Param weakLinkedLibraryName,
|
|
CFBundleRef bundleToImport);
|
|
// A wrapper around CFMLateImportCore that looks up symbols by calling
|
|
// CFBundleGetFunctionPointerForName on a reference to a Core Foundation
|
|
// bundle (bundleToImport). You can get this reference through any
|
|
// Core Foundation bundle API, for example CFBundleCreate.
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|