Libraries On The Mac
Volume Number: 14
Issue Number: 6
Column Tag: Tools Of The Trade
Libraries on the Mac
by Jeremy Nelson
An overview of shared code mechanisms on the Macintosh
Libraries - precompiled collections of code - are a powerful tool for programmers.
They allow us to distribute valuable technologies without disclosing the secrets of the
proprietary source code from which those libraries were created. Because libraries
are precompiled, they also encourage users of the technology to treat them as a black
box - letting the user focus on their own code without worrying how library
accomplishes its purpose. This article will survey library mechanisms on the
Macintosh and offer a brief overview of the advantages of each.
Statically Linked Libraries
Libraries can be linked to other code statically or at runtime. A statically linked
library, like code you have written yourself, is integrated into your application's
binary during the compilation process. In contrast, a runtime library is loaded when
the application executes, and calls made to the library are redirected to the loaded code.
Both kinds of libraries are a good way to share new technologies.
Statically linked libraries are made up of a precompiled binary and an interface file
which contains the API. Using a statically linked library is easy, simply include the
binary and the C header file or Pascal interface file. During the linking stage of
compilation the binary portion of the library is integrated into your application,
exactly like the compiled code of the source you have written.
Most programs include some statically linked libraries. These libraries may provide
language specific functions, basic Macintosh calls or mathematical functions.
Runtime Libraries
Runtime libraries, also known as Dynamically Linked Libraries (DLL), can reduce
disk usage and lower memory requirements by allowing different applications to share
a single copy of the code. Runtime libraries can also allow portions of an application to
be selectively updated by simply replacing an old library file with a newer one.
Like statically linked libraries, runtime libraries are composed of a binary and an
interface file. However the binary is simply a stub library which contains the names
of the functions and version information. Mercifully, exactly how the functions are
made available to your program when the program is executed is hidden from you.
During your programs initialization phase, when your program is being readied to
execute, the Operating System prepares any Shared Libraries you reference.
There are a variety of Shared Library mechanisms on the Mac, each with strengths and
weakness. This diversity of library mechanisms exists largely due to historical
factors. The formal library mechanisms are:
• Apple Shared Library Manager (ASLM)
• Code Fragment Manager (CFM and CFM-68K)
• System Object Model (SOM)
For completeness, any discussion of shared code mechanisms also needs to include:
• Component Manager
• Code Resources
Custom mechanisms are also perfectly valid (if possibly application specific) ways of
sharing code. Creating a custom mechanism allows developers to tailor a solution, but
at the risk of recreating already tried and tested code- a somewhat ironic result.
Apple Shared Library Manager (ASLM)
ASLM is the one of the oldest solutions from Apple and it works under 68K and PPC.
Unfortunately you can only make the libraries with MPW. While ASLM is available on
every Mac and fairly straightforward to use, very little uses ASLM besides Open
Transport 68K. Most programmers will never use ASLM directly.
Example: OpenTransport 68K.
Reference: ASLM Developer's Guide, Developer CD Series Mac OS SDK.
System Object Model (SOM)
SOM was developed by IBM to work on their AIX systems, runs under OS/2 and was
ported to the Mac as part of the development of OpenDoc. SOM is both PowerPC and 68K
and is probably the most technically advanced solution on the Macintosh. It is also a
deprecated technology and as such it will probably not progress in the future.
SOM provides a number of desirable features. It can handle different calling
conventions between caller and callee, and there are standard ways to patch and
unpatch the code dynamically (so you can intercept a library call with your code). It
even tackles the so-called 'fragile base class' problem and other changes to the
interface of a library. SOM provides the calling program with full Object Oriented
class libraries, even translating across different compilers' Object implementations.
While SOM is a technically wonderful solution it is a complex technology which has
been quite difficult to use. Development tools have now reduced this difficulty
somewhat. SOM was deprecated along with OpenDoc.
Examples: Opendoc and Cyberdog.
Reference: SOMobjects(tm) for Mac OS, Developer CD Series Mac OS SDK.
Code Fragment Manager (CFM and CFM-68K)
CFM was made specifically for PowerPC: when the PowerPC system came out there was
no native shared library, so Apple developed a PowerPC native library format. CFM is
used by both ASLM and SOM to load code fragments on the PowerPC.
In 1994 CFM was ported to 68K to provide a universal solution. CFM-68K had
stability problems which discouraged its use for some time. This was fixed in version
4.0 of CFM-68K in November 1996 (as described in Technote 1084).
To use CFM-68K you need to use different versions of the runtime libraries, specially
modified to work with CFM-68K. This makes your application a 'CFM-68K
application', and you need to be careful about using custom preemptive threading
methods (this problem relates to the version 4.0 bug fix mentioned above). (It is
possible, but tricky, to call CFM-68K code from classic applications. See Technote
1077 for details.)
CFM is Apple's announced direction in shared code. There are good development tools
for CFM in both CodeWarrior and MPW. If you were developing a library for a
PowerPC application this should be your preferred choice. Under 68K some developers
may be discouraged from adopting CFM-68K applications because of the changes
necessary to their application.
Example: OpenTransport, MathLib, AppearanceLib.
References: IM PowerPC System Software, Chapter 3: Code Fragment Manager.
Component Manager
The Component Manager was written by the QuickTime team to meet their need for a
universal plug-in architecture. As a result, the Component Manager is available under
both 68K and PPC.
Components are code fragments with a particular type (like File Types in the file
system). All components of the same type have the same interface. This allows
applications to chose from a list of different code fragments, each of which may have
different behaviour, but can be loaded and used identically. This is equivalent to having
multiple different versions of a single library, each with different performance
characteristics.
Components can also be captured which permits functions to be overridden and
modified. A captured component is removed from the list of available components, but
can still be called by the capturing component.
The drawback is that this added versatility introduces additional complexity.
Applications must deal with selecting and loading the component: the code is not loaded
automatically when the program is being loaded.
QuickTime is the most obvious user of the Component Manager. An interesting user of
the Component Manager is Internet Config. When Internet Config was written this was
the only solution which worked well under PowerPC and 68K, and provided the
override functionality, which was a desired feature of Internet Config.
Examples: QuickTime, Internet Config.
Reference: IM More Toolbox, Chapter 6: Component Manager.
Code Resources
The most common example of code resources are List Definition Functions (LDEF),
Window Definitions Functions (WDEF) and Control Definition Functions (CDEF). These
are functions which are loaded using the Resource Manager. It is up to the programmer
to define the API and the program is responsible for loading and unloading the code.
Typically Code Resources only have a single entry point (function call) with an
argument which selects the actual function and a small number of other arguments,
often contained in a parameter block. Which values are extracted from the parameter
block may depend on what function is called.
Code Resources have been used in a number of applications and good example code is
available. For instance, on the Code Warrior Pro 2 CD, the "Pascal Code Resources
shows how to create 68K or PPC code resources, either of which can be called from
both 68K and PowerPC applications. (This is a useful way to update time-critical
portions of a 68K application to run under PowerPC without updating the entire
application.)
Examples: LDEFs, CDEFs, Codewarrior Plug-Ins under 68K.
Reference: IM More Toolbox, Chapter 1: Resource Manager.
Dangers
Probably the most confusing thing about libraries is that the binary can be compiled
using a variety of different compiler options. Thus there are usually many different
versions of any given library. For a splendid example of this, look in Codewarrior's
MSL C:Bin folder. There are over a dozen different versions of the Metrowerks
Standard C library (MSL C for 68K).
You need to make sure you include the appropriate version of the library in your code.
You will not always get compiler warnings if you include the wrong version, and some
mismatched program/library compiler settings can lead to intermittent errors which
may be very difficult to debug.
There are several common compiler options unique to 68K which result in different
versions. The main settings include:
• Near or Far: Under 68K calls to functions can use so called 'Near' or 'Far'
references.
• 2i or 4i: Two or four byte integers. You need to be careful here since it is
easy to include the wrong version, you will not get an error when the
application is compiled, and it can result in obscure bugs which are hard to
track down.
The PowerPC Runtime Architecture is much more precisely defined than under 68K.
For example, under PowerPC all code references use the same convention and all
integers are four bytes. As a result there are usually many fewer versions of the
PowerPC library.
Different compilers can also generate different implementations of the same library.
This is because the calling conventions for the functions (such as how the function
parameters are ordered on the stack) are not generally defined by the language so it is
up to the compiler vendors to choose. One good way around this is to export functions
using the 'pascal' keyword. The Pascal calling conventions are well defined, so
libraries compiled this way will work under different compilers and with different
languages (all other options being equal).
It is also advisable to avoid the 'int' integer type in the library interface, instead use
'Int16' or 'Int32'. This gives more robustness across different compilers, languages
and environments.
Conclusion
Libraries provide conceptually clean interfaces to code, which can be well tested and
re-used. They can also lower compile time and allow large projects to be factored into
smaller chunks. Runtime libraries provide additional benefits such as reduced disk and
memory overhead, plus they can provide novel functionality through dynamic
patching.
On the Macintosh under PowerPC the Code Fragment Manager is Apple's announced
direction in shared code, has good tool support and is relatively easy to use. CFM is
guaranteed to exist on all PowerPC Macs.
Unfortunately CFM-68K requires additional effort on behalf of programmers who
wish to utilise CFM-68K libraries and CFM-68K support is not always present on
older Macintoshes. For this reason Code Resources are often used as a relatively
straightforward library mechanism.
______________________________
Jeremy Nelson is a programmer and Technical Support Droid for Stairways
Software Pty Ltd.