Code Fragment Manager (CFM)
The Code Fragment Manager is the Operating System loader for executable code and data that are contained in fragments. Its operations are loosely analogous to those of the Segment Manager in previous versions of the Macintosh system software. The Code Fragment Manager, however, provides a much richer set of services than the Segment Manager, including
- loading and preparation of fragments for execution
- automatic resolution of imported symbols by locating and loading import libraries used by a fragment
- automatic execution of a fragment's initialization and termination routines
- support for updated versions of import libraries
The following sections describe how fragments are structured, how the Code Fragment Manager searches fragments for unresolved symbols, and how it manages different versions of import libraries.
The Code Fragment Manager operates primarily on fragments. A fragment is a block of executable code and its associated data. Fragments can be loosely differentiated into three categories, based on how they are used:
- import libraries
Fragments contain symbols, some or all of which may be referenced by code or data in other fragments; these kinds of symbols are called exported symbols (or, for brevity, exports). An import library is a fragment that consists primarily of exported symbols and their associated code and data. Other kinds of fragments can contain references to the exported symbols of an import library; these references are called imported symbols (or, for brevity, imports).
During the linking phase of building a fragment, the linker creates an import for each external symbol that is resolved to an export from some import library. The code or data referenced by that import is not copied into the fragment. Instead, as part of the process of loading the fragment into memory and preparing it for execution, the Code Fragment Manager replaces the imported symbol with the address of the exported code or data.
Note: Both code and data may be exported by name. However, routines are usually exported indirectly, via a transition vector to the routine. A routine's transition vector is stored in the fragment's data area.
A fragment is stored in a container, which can be any logically contiguous object accessible by the Operating System. For example, the executable code and global variables of a PowerPC application are typically stored in a fragment in the application's data fork. The Macintosh ROM is itself a container for the import library that exports the Macintosh system software and for several other import libraries. Application extensions, such as dynamically loadable filters or other code modules, can be stored in resources in the application's resource fork. It's better, however, to use the data fork of some file as the container of an application extension fragment. The extension can be put into the application's data fork (either before or after the application's code fragment) or into the data fork of some other file.
Note: A single data fork can contain multiple containers. The 'cfrg' resource in the file's resource fork allows the Operating System to find each individual container in a data fork.
The Code Fragment Manager is responsible for loading fragments (by calling the Code Fragment Loader) and preparing them for execution. It resolves the imported symbols in a fragment, loading and preparing any additional fragments whose exports are referenced by that fragment. Loading a given fragment, such as an application, usually involves loading and preparing additional fragments.
An import library can have its exported symbols imported by any number of other fragments. When the Code Fragment Manager resolves the imports in a particular fragment, it establishes a connection to each individual fragment whose code or data that fragment references. In general, the connections are transparent to the importing fragment. If you call the Code Fragment Manager directly, however, it returns a connection ID to you that uniquely identifies the connection. You can use the connection ID to perform various actions on the exporting fragment (for example, to break the connection and unload the fragment or to get information about its exported symbols).
Note: There is no practical limit on the size of a fragment.
Import Library Searching
When searching for an import library to find code or data that is imported by some other fragment, the Code Fragment Manager follows a standard search path. It looks in various files and folders in a specific order until it finds an import library that exports the code or data imported by the fragment being loaded. Once the Code Fragment Manager finds a library that it deems compatible with the fragment it's loading, it stops searching and resolves imports in the fragment to code or data in that library. In general, the exact order in which the Code Fragment Manager searches for import libraries is transparent to your software. However, you might need the information in this section to ensure that a particular import library is found before some other import library, which might also be compatible with your fragment.
When loading and preparing an application that imports code or data from an import library, the Code Fragment Manager searches first in the application file itself, by looking for import libraries indicated in the application's 'cfrg' resource. Typically, any import libraries contained in your application are located in your application's data fork, either before or after the container that holds your application's code and data. Less commonly, you can put an import library into a resource in your application's resource fork. The 'cfrg' resource specifies the location of any import libraries that you've included with your application, whether in the data or the resource fork.
If an import library used by your application is not found in the application file itself, the Code Fragment Manager next searches in any directory designated as the application's library directory, a directory used by the application to store import libraries or aliases to import libraries. You specify a library directory by including in the appropriate field of your 'cfrg' resource the ID of an alias resource that picks out the library directory.
The Code Fragment Manager searches a directory by looking for files of type 'shlb' that contain a resource of type 'cfrg'. The 'cfrg' resource identifies the logical name of the import library, which is needed to match the library's name generated at link time. There can be more than one logical name listed in a single 'cfrg' resource. This might happen if there are multiple import libraries contained in the data fork of a single 'shlb' file. This might also happen if a single import library or application is to be identified by more than one name. Within a directory, the Code Fragment Manager also looks for aliases to files of type 'shlb' and resolves them to their targets. The alias file must itself be of type 'shlb'.
If no suitable import library has been found yet, the Code Fragment Manager searches next in the directory that contains the application. If any import libraries--whether located in the application's directory or targeted by an alias in the application's directory--are determined to be compatible with the fragment whose imports are being resolved, the Code Fragment Manager chooses the most compatible library and stops searching.
IMPORTANT: The Code Fragment Manager looks only in the top level of the application's directory, not in any subdirectories contained in it.
If no suitable import library has been found yet, the Code Fragment Manager searches next in the Extensions folder in the System Folder and in all the subdirectories of the Extensions folder, including any directories that are targets of directory aliases in the Extensions folder. Once again, both files of type 'shlb' and targets of aliases of type 'shlb' are candidates for compatibility checking. This scheme allows you to store your import libraries in a vendor-specific location in the Extensions folder.
If the Code Fragment Manager still hasn't found a compatible import library that exports the imported symbols in the fragment it's trying to prepare, it continues by looking in a ROM registry, which keeps track of all import libraries that are stored in the ROM of a Macintosh computer. The Code Fragment Manager registers all ROM-based import libraries in this registry at system startup time.
The final stage of the search path is a file and directory registry that it maintains internally. This registry is a list of files and directories that, for various reasons, cannot be put into the normal search path followed by the Code Fragment Manager or would not be recognized as import libraries even if they were in that path. For example, to be registered automatically by the Component Manager, a component must be stored in a file of type 'thng'. To inform the Code Fragment Manager that the file also contains one or more import libraries in its data fork, it can be registered in the file and directory registry.
Note: The Code Fragment Manager routine to register a file or directory is currently private.
If your application or other software loads a fragment explicitly from disk by calling the GetDiskFragment routine, the Code Fragment Manager first looks for any needed import libraries in the load directory, the directory that contains the fragment being loaded. (This directory is the one specified in the fileSpec parameter you pass to GetDiskFragment.) If no suitable import library is found there, the search continues along the path followed when loading and preparing an application. However, the Code Fragment Manager looks in the load directory first only if it is different from the application's directory. Otherwise, the load directory is searched in its normal sequence, after the application file itself and the library directory.
In summary, the Code Fragment Manager looks in the following places when searching for an import library to resolve one or more imports in a fragment being loaded:
- The load directory (the directory containing the fragment being loaded). The load directory, however, is searched only when a fragment is loaded in response to a call
- to GetDiskFragment or GetSharedLibrary, and only when it's different from the application's directory.
- The application file, if the application's 'cfrg' resource indicates that the application file contains import libraries. The application fragment is implicitly treated here as an import library.
- The application's library directory (as specified in the application's 'cfrg' resource).
- The application's directory. Only the top level of this directory is searched.
- The Extensions folder in the System Folder. The Extensions folder and all directories in the Extensions folder are searched.
- The ROM registry maintained internally by the Code Fragment Manager.
- The file and directory registry maintained internally by the Code Fragment Manager.
At any stage, the Code Fragment Manager selects the one import library of all those available to it that best satisfies its compatibility version checking. If an import library meets the relevant criteria, the library search stops. Otherwise, the search continues to the next stage. If the final stage (the file and directory registry) is reached and no suitable library can be found, the Code Fragment Manager gives up and does not load the original fragment.
One of the principal benefits of import libraries, aside from their ability to reduce the size of applications and other fragments, is the ease with which a library developer can make improvements in portions of the import library without requiring developers to modify or rebuild any applications that use the import library. The library developer needs only to ensure that the updated version is compatible with the version expected by the applications using the library. In general, this means that the external programming interface provided by the import library remains unchanged throughout changes in the underlying implementation.
The Code Fragment Manager provides a simple but powerful version-checking scheme intended to prevent incompatibilities between import libraries and the fragments that use them. This checking is always performed automatically as part of the normal fragment loading and preparation process. In general, your application does not need to concern itself with checking the version of an import library whose code or data it uses.
To take a simple example, suppose that an application uses a single import library. When the application is created, it is linked with some version of that library. Unresolved external symbols in the application are resolved, by the linker, to exported code or data in the import library. The version of the import library used at link time is called the definition version of the library (because it supplies the definitions of exported symbols, not the actual implementation of routines and initialization of variables).
When the application is loaded and prepared for execution, it must be connected to a version of that import library. The version of the import library used at load time is called the implementation version of the library (because it supplies the implementations of routines and initializations of variables exported by the library). The essential requirement is that the implementation version of an import library used at run time be compatible with the definition version used at link time. The two versions do not need to be identical, but they must satisfy the same programming interface. (The implementation can be a superset of the definition library.)
To allow the Code Fragment Manager to check the implementation version of an import library against the definition version used when linking the application, the linker copies version information from the definition library into the application. When the application is launched, the version information in the application is compared with the version information stored in the implementation library. If the version of the import library is identical to that expected by the application, the library and the application are deemed compatible. If, however, the two versions are not identical, the Code Fragment Manager inspects additional information in whichever of the two fragments (the application and the import library) is the newer fragment. The idea is to allow the newer fragment to decide whether it is compatible with the older fragment.
Every import library contains three version numbers: the current version number, the oldest supported definition version number, and the oldest supported implementation version number. The two latter version numbers are included to provide a way for the Code Fragment Manager to determine whether a given definition version is compatible with a given implementation version, if the current versions of the library and the definition version used to link the application are not identical.
IMPORTANT: The current version number must always be greater than or equal to both the oldest supported definition version number and the oldest supported implementation version number.
The linker copies into the application both the current version number of the definition library and the oldest supported implementation version number. When the application is launched, the Code Fragment Manager checks those numbers with the version numbers in the implementation libraries according to the algorithm shown in Listing 3-1.
Listing 3-1 Pseudocode for the version-checking algorithm
if (Definition.Current == Implementation.Current) return(kLibAndAppAreCompatible); else if (Definition.Current > Implementation.Current) /*definition version is newer than implementation version*/ if (Definition.OldestImp <= Implementation.Current) return(kImplAndDefAreCompatible); else return(kImplIsTooOld); else /*definition version is older than implementation version*/ if (Implementation.OldestDef <= Definition.Current) return(kImplAndDefAreCompatible); else return(kDefIsTooOld);
If the current version number copied into the application from the definition library at link time is the same as the current version number of the candidate version of the implementation import library, then the Code Fragment Manager accepts that version of the implementation import library and continues with the loading and preparation of the application. Otherwise, the Code Fragment Manager determines which of the two fragments is newer and then applies a further check.
If the current version number copied into the application from the definition library at link time is greater than the current version number of the candidate version of the implementation import library, the Code Fragment Manager compares the oldest supported implementation version number in the application with the current version number of the implementation library. If the definition library's oldest supported implementation version number is less than or equal to the library's current version number, the application and library are deemed compatible. Otherwise, the library is too old for the application.
If the current version number copied into the application from the definition library at link time is less than the current version number of the most recent version of the implementation import library, the Code Fragment Manager compares the oldest supported definition library version number (stored in the implementation library) with the current definition library version number (stored in the application). If the oldest supported definition library version number is less than or equal to the application's current version number, the application and library are deemed compatible. Otherwise, the application is too old for the library.
Note: In general, of course, the Code Fragment Manager checks the compatibility of a fragment being loaded and all of the import libraries from which it imports code and data.
The version numbers in both the definition and implementation versions of an import library should have the same format as the first 4 bytes of a version resource (that is, a resource of type 'vers'). See the chapter "Finder Interface" in Inside Macintosh: Macintosh Toolbox Essentials for complete information on version resources. When comparing version numbers, however, the Code Fragment Manager treats those 4 bytes simply as an unsigned long quantity. As a result, the value 0x00000000 is interpreted as a valid version number.