Mac OS X Internals: A Systems Approach

2.8. Bundles and Frameworks

Before discussing other layers of Mac OS X, let us look at the bundle and framework abstractions, since much of the user-level functionality in Mac OS X is implemented as frameworks, and a framework is a specific type of a bundle.

2.8.1. Bundles

A bundle is a collection of related resources packaged as a directory hierarchy. Examples of resources that reside in bundles include executables, shared libraries, plug-ins, header files, images, audio files, documentation, andrecursivelyother bundles. The bundle abstraction is very useful in packaging, deploying, maintaining, and using software. Consider the example of an application bundle, which is one of the most commonly used bundle types on Mac OS X. Figure 212 shows the hierarchical structure of the iTunes application bundle.

Figure 212. Hierarchical structure of the iTunes application bundle

An application bundle is a directory with a name that conventionally has a .app suffix. Mac OS X applications are often more than just a single stand-alone executable. For example, an application may use a variety of media filesicons, splash images, and soundsin its user interface. An application may implement much of its functionality modularly, across one or more dynamic shared libraries. An application may also support a plug-in architecture through which anybody may extend the application's functionality by writing loadable plug-ins. Bundles are well suited for such applications because they keep the various constituents of the application together in a structured manner. As shown in Figure 212, the iTunes application bundle contains the main iTunes executable, a loadable plug-in bundle, icons, media files, a helper application, localized documentation, and so on. Everything resides within the iTunes.app directory, rather than being strewn over many system directories. Given such bundles, application installation and removal can be as trivial as copying or deleting, respectively, a single .app directory. Applications can also be moved after installation since they are usually self-contained.

The Finder treats several types of bundles as opaque, atomic entitiesas if they were files and not directories. For example, double-clicking on an application bundle launches the application. The user may browse the bundle's contents by using the Finder's Show Package Contents contextual-menu item. A bundle's opaqueness may be asserted in several ways, for example, through a property list file, through a PkgInfo file, and through the kHasBundle file system attribute.

Several types of Mach-O binaries exist within bundles on Mac OS X. Bundles can be programmatically accessed using the CFBundle and NSBundle APIs. Table 21 lists several examples of bundle types on Mac OS X.

Table 21. Examples of Bundle Types on Mac OS X

Bundle Type

Default Extension

Explanation

Automator action

.action

Automator actions are extensions to the default set of actions supported by the Automator workflow-based application, which allows users to graphically construct a sequence of actions by picking one or more actions from a palette of available actions. These bundles can be implemented in Objective-C or AppleScript.

Application

.app

Mac OS X application bundles typically contain dynamically linked executable programs along with any resources needed by the programs.

Bundle

.bundle

A .bundle package is a loadable bundle containing dynamically linked code that can be loaded by a program at runtime. The program must explicitly load such a bundle using dynamic linking functions. Several Mac OS X applications use .bundle plug-ins to extend their feature sets. For example, the Address Book application can load Action Plug-In bundles from specific directories, allowing programmers to populate the Address Book rollover menus with custom items. iTunes uses bundles to implement visual plug-ins, whereas iMovie uses them to implement effects, titles, and transitions.

Component

.component

Core Audio plug-ins, which are useful for manipulating generating, processing, or receiving audio streams, are implemented as .component bundles.

Dashboard widget

.wdgt

Dashboard is a lightweight runtime environment introduced in Mac OS X 10.4 for running small accessory programs called widgets in a logically separate layer atop the desktop. Dashboard widgets are packaged as .wdgt bundles.

Debug (application)

.debug

Applications with debugging symbols can be packaged as .debug bundles, which the Finder treats similarly to .app bundles.

Framework

.framework

A framework is a dynamic shared library packaged with resources such as header files, API documentation, localized strings, helper programs, icons, splash images, sound files, and interface definition files. An umbrella framework is a bundle that contains one or more subframeworks in its Frameworks subdirectory. The Carbon and Core Services frameworks are examples of umbrella frameworks.

Kernel extension

.kext

Kernel extensions are dynamically loadable kernel modules. They are similar to .bundle bundles in that they contain code that is programmatically introduced into a running programthe program being the kernel in this case. However, kernel extensions are statically linked.

Keynote file

.key

Applications can use bundles as complex "file formats" to hold arbitrary data in documents. Apple's Keynote software uses bundles to store presentations. A .key bundle contains an XML-based representation of the presentation's structure, along with resources such as images, thumbnails, audio, and video.

Metadata importer

.mdimporter

Metadata importers are used by the integrated Spotlight search technology in Mac OS X. Such an importer gathers information from one or more specific file formats. Developers of applications with custom file formats can provide their own metadata importers that Spotlight can use as plug-ins while indexing a file system.

Package

.pkg, .mpkg

A .pkg bundle is an installation package created using the PackageMaker.app application. A package's contents include files and directories belonging to the installable software it represents, along with information that may be needed to install the software. A metapackagea .mpkg bundlecontains a file that includes a list of packages or metapackages and any other information that might be needed to install them. Unlike a package, a metapackage does not itself contain any installable software. Double-clicking a package or a metapackage launches the Mac OS X installer application.

Palette

.palette

A palette is a loadable bundle that contains code and user-interface objects used by Apple's integrated development environment (IDE).

Plug-in

.plugin

A .plugin is a loadable bundle similar in concept to a .bundle but has more architectural and implementation requirements. Contextual-menu plug-ins, which are used to extend the list of commands on Finder contextual menus, are implemented as .plugin bundles. Other examples of such bundles include certain QuickTime plug-ins and extensions called Image Units to the Core Image and Core Video technologies.

Preference pane

.prefPane

Preference panes are bundles used to manage system-wide software and hardware preferences. The System Preferences application displays the union of various .prefPane bundles located in /System/Library/PreferencePanes/, /Library/PreferencePanes/, and ~/Library/PreferencePanes/.

Profile (application)

.profile

Applications with profiling data can be packaged as .profile bundles, which the Finder treats similarly to .app bundles.

Service

.service

A service is a bundle that provides generic functionality for use by other applications. Examples of services include text-to-speech conversion, (SpeechService.service), spell checking (AppleSpell.service), and Spotlight searching (Spotlight.service). Services can be made available on a system-wide basis through .service bundles. A service is usually contextual in its operationit operates on a currently selected entity, such as a piece of text. For example, the AppleSpell service will run a spelling check on the selected text.

Screensaver

.saver, .slideSaver

Mac OS X provides APIs for creating screensavers based on program-generated content or slide shows. Such screensaver programs are packaged as .saver and .slideSaver bundles, respectively. Note that a .slideSaver bundle does not contain any executable codeit contains only a set of images in its Resources directory and an information property list file.

System Profiler reporter

.spreporter

The System Profiler application displays information about the system's hardware and software. It uses .spreporter bundles residing in /System/Library/SystemProfiler/ to collect information. Each reporter bundle provides information about a particular area. For example, there are system reporter bundles for attached displays, FireWire devices, Serial ATA devices, USB devices, and installed software and fonts.

Web plug-in

.webplugin

The Safari web browser supports an Objective-C-based plug-in model for displaying new types of content in the browser through plug-ins. QuickTime support in Safari is implemented as a .webplugin bundle.

Xcode plug-in

.ibplugin, .pbplugin, .xcplugin, .xctxtmacro, .xdplugin

The functionality of Apple's Xcode development environment can be extended through several types of plug-ins.

As shown in Table 21, bundles are used to implement several types of plug-ins. From a generic standpoint, a plug-in is an external piece of code that runs in a host environment. Typically the host is an application, but it can be an operating system or perhaps even another plug-in. The host must be designed to be extensible through plug-insit must export an API for the plug-in to conform to. Thus, by using the host's plug-in API, a plug-in can add functionality to the host without requiring recompilation of, or even access to, the host's source.

Note that the bundle extensions listed in Table 21 are only conventionsit is possible to have a bundle with an arbitrary extension, as long as certain requirements are satisfied or the application using the bundle knows how to handle the extension.

2.8.2. Property List Files

You will frequently come across property list (plist) files on Mac OS X. They are on-disk representations of organized data. When such data is in memory, it is structured using basic data types native to the Core Foundation framework.

The Core Foundation data types used in property lists are CFArray, CFBoolean, CFData, CFDate, CFDictionary, CFNumber, and CFString. An important feature of these data types is that they are readily portable across multiple Mac OS X subsystemsfor example, CF data types have analogs in the I/O Kit.

An on-disk property list is a serialized version of an in-memory property list. A plist file may store information in either binary format or human-readable XML format.[32] The two most common uses of plist files on Mac OS X are by bundles and by applications.

[32] Many people opine that XML is hardly readable by humans. Although it is cumbersome for a normal person to parse, XML is decidedly more human-friendly than a "raw" binary format.

A bundle uses a special type of plist filean information property listto specify its critical attributes, which are read by other programs while handling the bundle. The default name for an information property list file is Info.plist. Consider an application bundle's Info.plist file, whose contents may include attributes such as the following:

  • The document types handled by the application

  • The name of the bundle's main executable file

  • The name of the file containing icons for the bundle

  • The application's unique identification string

  • The application's version

Applications use property list files to store user preferences or other custom configuration data. For example, the Safari web browser stores a user's bookmarks in a plist file called Bookmarks.plist. Numerous configuration plist files can be found in a user's ~/Library/Preferences/ directory. Typically, applications store per-user configurations or preferences in this directory using a reverse DNS naming convention. For example, the Safari web browser's preferences are stored in com.apple.Safari.plist.

Property list files can be created and manipulated using several tools on Mac OS X. The plutil command-line program can be used to convert a plist file from one format to another. It also checks plist files for syntax errors. XML-format plist files can be edited using any text editor, although it might be more convenient (and less error-prone) to use the Property List Editor graphical application, which is part of Apple Developer Tools. The Cocoa and Core Foundation frameworks provide APIs for programmatically accessing plist files. As alluded to earlier, several standard object types in these frameworks are stored, organized, and accessed as property lists.

2.8.3. Frameworks

In its simplest form, a Mac OS X framework is a bundle containing one or more shared libraries. Besides stand-alone shared librariessuch as the ones in /usr/lib/Mac OS X contains a large number of frameworks. Whereas a shared library contains shared code, a framework usually contains one or more shared libraries along with other types of related resources.[33] From an implementation standpoint, a framework is a directory hierarchy encapsulating shared resources such as the following:

[33] It is possible for a framework to contain no shared code but only resources.

  • Dynamic shared libraries

  • Headers

  • Nib (NeXT Interface Builder) files

  • Localized strings

  • Images

  • Documentation files

  • Information property lists

  • Sounds

A framework directory has a well-defined structure consisting of files and folders, some of which are mandatory; the others are optional. Figure 213 shows the files and folders contained in the hypothetical Foo framework.

Figure 213. Bundle structure of a Mac OS X framework

Foo.framework/ # Top-level directory Headers -> Versions/Current/Headers # Symbolic link Foo -> Versions/Current/Foo # Symbolic link Libraries -> Versions/Current/Libraries # Symbolic link Resources -> Versions/Current/Resources # Symbolic link Versions/ # Contains framework major versions A/ # Major version A Foo # Framework's main dynamic library Headers/ # Public headers Bar.h Foo.h Libraries/ # Secondary dynamic libraries libfoox.dylib libfooy.dylib Resources/ English.lproj/ # Language-specific resources Documentation/ # API documentation InfoPlist.strings # Localized strings Info.plist # Information property list file B/ # Major version B Foo Headers/ Bar.h Foo.h Libraries/ libfoox.dylib libfooy.dylib Resources/ English.lproj/ Documentation/ InfoPlist.strings Info.plist Current -> B # Symbolic link to most recent version

The Info.plist file within the Resources subdirectory of a framework contains its identifying information. Frameworks support versioning: If an application depends on a particular version of a framework, either the exact version or a compatible version of that framework must exist for the application to run. Major version differences are incompatible, whereas minor version differences are compatible. A single framework bundle may contain multiple major versions. The framework shown in Figure 213 contains two versions, A and B. Moreover, all files and directories in the framework's top-level directory, except Versions, are symbolic links to entities belonging to the major version Current. Foo, the file with the same name as that of the framework directory's prefix, is the main dynamic shared library.

Figure 214 shows the standard Mac OS X frameworks categorized by the typical purposes they are used for.

Figure 214. Standard frameworks on Mac OS X

An umbrella framework can contain other subframeworks and even other subumbrella frameworks. Umbrella frameworks are used to hide interdependencies between discrete system libraries by effectively presenting a metalibrary that is the union of all libraries within it. An umbrella framework's bundle structure is similar to that of a standard framework, but it also contains a subdirectory called Frameworks, which contains subframeworks. Typically, the programmer may not link directly to subframeworks.[34] In fact, the programmer need not know whether a framework is an umbrella framework or notlinking to an umbrella framework automatically provides access to its constituents. At runtime, if the dynamic linker comes across a symbol that has been recorded as "contained in an umbrella framework," the linker will search the umbrella framework's subframeworks, sublibraries, and subumbrellas. Similarly, an umbrella framework's header files automatically include any header files in the subframeworks. Figure 215 shows the standard umbrella frameworks on Mac OS X.

[34] While compiling an umbrella framework, it is possible to specify through a linker option that a given client name can link to a given subframework, even though the client is externalsay, a bundle. The client name for a bundle is also set through a linker option while compiling the bundle.

Figure 215. Standard umbrella frameworks on Mac OS X

Frameworks can also be private in that they are unavailable for linking by user programs. Some frameworks are private because they are embedded within other bundles such as application bundles. Mac OS X contains a number of frameworks that are private to Apple. These reside in /System/Library/PrivateFrameworks/. Examples of private frameworks include DiskImages.framework, Install. framework, MachineSettings.framework, SoftwareUpdate.framework, and VideoConference.framework. Their privacy is manifested in the following ways from the standpoint of a third-party programmer.

  • Apple does not publish the APIs of these frameworks. Even their header files are not available.

  • By default, a third-party programmer cannot link with these frameworks. However, it is possible to link with a private framework by explicitly passing the full pathname of its containing directory to the linker. Doing so is unsupported by Apple.

When a dynamically linked program is compiled, the installation paths of the libraries it links with are recorded in the program. These paths are typically absolute for system frameworks that are part of Mac OS X. Some applications, particularly third-party applications, may contain their own frameworks within their application bundles. Paths to such frameworks or libraries, when recorded in an application, can be recorded relative to the application bundle.

When the dynamic link editor needs to search for frameworks, the fallback paths it uses are ~/Library/Frameworks/, /Library/Frameworks/, /Network/Library/Frameworks/, and /System/Library/Frameworks/, in that order. Similarly, fallback paths for dynamic libraries are ~/lib/, /usr/local/lib/, and /usr/lib/. Note that path searching is not performed in a user's home directory for setuid programs that are not executed by the real user.

2.8.4. Prebinding

Mac OS X uses a concept called prebinding to optimize Mach-O applications to launch faster by reducing the work of the runtime linker.

As we saw earlier, the dynamic link editor, dyld, is responsible for loading Mach-O code modules, resolving library dependencies, and preparing the code for execution. Resolving undefined symbols in executables and dynamic libraries at runtime involves mapping the dynamic code to free address ranges and computing the resultant symbol addresses. If a dynamic library is compiled with prebinding support, it can be predefined at a given preferred address range.[35] This way, dyld can use predefined addresses to reference symbols in such a library. For this to work, libraries cannot have overlapping preferred addresses. To support prebinding, Apple marks several address ranges as either reserved or preferred for its own software and specifies allowable ranges for use by third-party libraries.

[35] A framework's preferred address can be specified at compile time using the seg1addr linker flag.

dyld was optimized in Mac OS X 10.3.4 such that prebinding of applications is no longer necessary. Moreover, prebinding of applications is entirely deprecated on Mac OS X 10.4.

Let us consider an examplethat of the System framework (System.framework). The shared library within this framework is actually a symbolic link to the libSystem dynamic library in /usr/lib/. In other words, System.framework is a wrapper around libSystem. Let us use otool to display the load commands in libSystem to determine its preferred load address (Figure 216).

Figure 216. Listing the load commands in a Mach-O file

$ otool -l /usr/lib/libSystem.dylib /usr/lib/libSystem.dylib: Load command 0 cmd LC_SEGMENT cmdsize 872 segname __TEXT vmaddr 0x90000000 vmsize 0x001a7000 fileoff 0 filesize 1732608 maxprot 0x00000007 initprot 0x00000005 nsects 12 flags 0x0 ...

The vmaddr value shown in Figure 216 is the preferred load address of libSystem. We can use the C program in Figure 217 to print the names and load addresses of all Mach-O object files loaded into the program's address space and to check whether libSystem is loaded at its preferred address or not.

Figure 217. Printing libraries loaded in a program

// printlibs.c #include <stdio.h> #include <mach-o/dyld.h> int main(void) { const char *s; uint32_t i, image_max; image_max = _dyld_image_count(); for (i = 0; i < image_max; i++) if ((s = _dyld_get_image_name(i))) printf("%10p %s\n", _dyld_get_image_header(i), s); else printf("image at index %u (no name?)\n", i); return 0; } $ gcc -Wall -o printlibs printlibs.c $ ./printlibs 0x1000 /private/tmp/./printlibs 0x91d33000 /usr/lib/libmx.A.dylib 0x90000000 /usr/lib/libSystem.B.dylib 0x901fe000 /usr/lib/system/libmathCommon.A.dylib

The update_prebinding program is run to attempt to synchronize prebinding information when new files are added to a system. This can be a time-consuming process even if you add or change only a single file. For example, all libraries and executables that might dynamically load the new file must be found. Package information is used to expedite this process. A dependency graph is also built. Eventually redo_prebinding is run to prebind files appropriately.

After a software update or installation, while the installer program displays the "Optimizing..." status message, it is running the update_prebinding and redo_prebinding (if necessary) programs.

As shown in Figure 218, otool can be used to determine whether a binary is prebound.

Figure 218. Determining whether a Mach-O file is prebound

$ otool -hv /usr/lib/libc.dylib /usr/lib/libc.dylib: Mach header magic cputype cpusubtype filetype ncmds sizeofcmds flags MH_MAGIC PPC ALL DYLIB 10 2008 NOUNDEFS DYLDLINK PREBOUND SPLIT_SEGS TWOLEVEL

Категории