Section 10.4. Dynamically Extending the Kernel


10.4. Dynamically Extending the Kernel

The Mac OS X kernel is extensible through dynamically loadable components called kernel extensions, or simply kexts. A kext is loaded into the kernel either by the kernel's built-in loader (during early stages of bootstrapping) or by the user-level daemon kextd, which loads kexts when requested by user processes or the kernel. On being loaded, a kext resides in the kernel's address space, executing in privileged mode as part of the kernel. Numerous I/O Kit device drivers and device families are implemented as kexts. Besides device drivers, kexts also exist for loadable file systems and networking components. In general, a kext can contain arbitrary codesay, common code that may be accessed from multiple other kexts. Such a kext would be akin to a loadable in-kernel library.

10.4.1. The Structure of a Kernel Extension

A kext is a type of bundle, much like an application bundle. A kext bundle's folder has a .kext extension.[7] Note that the extension is not merely conventionalit is required by the Mac OS X tools that deal with kernel extensions. A kext bundle must contain an information property list file (Info.plist) in its Contents/ subdirectory. The property list specifies the kext's contents, configuration, and dependencies in an XML-formatted dictionary of key-value pairs. When a kext is loaded into the kernel, the contents of its Info.plist are converted to kernel data structures for in-memory storage.[8] A kext bundle normally also contains at least one kernel extension binary, which is a Mach-O executable. It can optionally contain resources such as helper programs and icons in its Resources/ subdirectory. Moreover, a kext bundle can contain other kext bundles as plug-ins.

[7] The Finder treats a kext bundle as a single, indivisible entity.

[8] Many of these data structures are analogs of Core Foundation data structures, such as dictionaries, arrays, strings, and numbers.

It is possible to have a valid kext bundle without any executables. The Info.plist file of such a kext may reference another kext in order to alter the characteristics of the latter. For example, ICAClassicNotSeizeDriver.kext does not contain an executable, but it holds several driver personalities that refer to AppleUSBMergeNub.kext, which is a plug-in kext within IOUSBFamily.kext.


Kernel-loadable binaries contained within kexts are statically linked, relocatable Mach-O binaries called kernel modules, or kmods. In other words, a kext is a structured folder containing one or more kmods along with mandatory metadata and optional resources. Figure 106 shows the structure of a simple kext bundle.

Figure 106. The contents of a simple kernel extension bundle

DummyKEXT.kext/ DummyKEXT.kext/Contents/ DummyKEXT.kext/Contents/Info.plist DummyKEXT.kext/Contents/MacOS/ DummyKEXT.kext/Contents/MacOS/DummyKEXT DummyKEXT.kext/Contents/Resources/ DummyKEXT.kext/Contents/Resources/English.lproj/ DummyKEXT.kext/Contents/Resources/English.lproj/InfoPlist.strings

It is important to note that even though a kmod is a statically linked Mach-O object file, nontrivial kmods usually have unresolved external references that are resolved when the kext is dynamically loaded into the kernel.


10.4.2. Creation of Kernel Extensions

Although most driver kexts are created using only I/O Kit interfaces, a kext maydepending on its purpose and natureinteract with the BSD and Mach portions of the kernel. In any case, loading and linking of kexts is always handled by the I/O Kit. The preferred and most convenient way to create a kernel extension is by using one of the kernel extension project templates in Xcode. In fact, other than for a contrived reason, it would be rather pointless to compile a kmod and package it into a kernel extension manuallysay, using a hand-generated makefile. Xcode hides several details (such as variable definitions, compiler and linker flags, and other rules for compiling and linking kernel extensions) from the programmer. Two kernel extension templates are available in Xcode: one for generic kernel extensions and one for I/O Kit drivers. A primary difference between them is that an I/O Kit driver is implemented in C++, whereas a generic kernel extension is implemented in C. It is also possible to create a library kext containing reusable code that can be used by multiple other kexts.

Definitions and rules for various types of Xcode projects reside in the /Developer/Makefiles/ directory.


Figure 107 shows an excerpt from the build output of a Universal kernel extensionan I/O Kit drivercontaining kmods for the PowerPC and x86 architectures. The path to the build directory, which is normally a subdirectory called build in the kernel extension's Xcode project directory, has been replaced by $BUILDDIR in the output shown.

Figure 107. Excerpt from the build output of a Universal kernel extension

/usr/bin/gcc-4.0 -x c++ -arch ppc -pipe -Wno-trigraphs -fasm-blocks -Os -Wreturn-type -Wunused-variable -fmessage-length=0 -fapple-kext -mtune=G5 -Wno-invalid-offsetof -I$BUILDDIR/DummyDriver.build/Release/DummyDriver.build/DummyDriver.hmap -F$BUILDDIR/Release -I$BUILDDIR/Release/include -I/System/Library/Frameworks/ Kernel.framework/PrivateHeaders -I/System/Library/Frameworks/Kernel.framework/Headers -I$BUILDDIR/DummyDriver.build/Release/DummyDriver.build/DerivedSources -fno-common -nostdinc -fno-builtin -finline -fno-keep-inline-functions -force_cpusubtype_ALL -fno-exceptions -msoft-float -static -mlong-branch -fno-rtti -fcheck-new -DKERNEL -DKERNEL_PRIVATE -DDRIVER_PRIVATE -DAPPLE -DNeXT -isysroot /Developer/SDKs/ MacOSX10.4u.sdk -c /tmp/DummyDriver/DummyDriver.cpp -o $BUILDDIR/DummyDriver.build/ Release/DummyDriver.build/Objects-normal/ppc/DummyDriver.o ... /usr/bin/g++-4.0 -o $BUILDDIR/DummyDriver.build/Release/DummyDriver.build/Objects- normal/ppc/DummyDriver -L$BUILDDIR/Release -F$BUILDDIR/Release -filelist $BUILDDIR/ DummyDriver.build/Release/DummyDriver.build/Objects-normal/ppc/ DummyDriver.LinkFileList -arch ppc -static -nostdlib -r -lkmodc++ $BUILDDIR/ DummyDriver.build/Release/DummyDriver.build/Objects-normal/ppc/DummyDriver_info.o -lkmod -lcc_kext -lcpp_kext -isysroot /Developer/SDKs/MacOSX10.4u.sdk ... /usr/bin/lipo -create $BUILDDIR/DummyDriver.build/Release/DummyDriver.build/ Objects-normal/ppc/DummyDriver $BUILDDIR/DummyDriver.build/Release/ DummyDriver.build/Objects-normal/i386/DummyDriver -output $BUILDDIR/Release/ DummyDriver.kext/Contents/MacOS/DummyDriver ...

Note that the Kernel framework (Kernel.framework), which is referenced by the compiler in the output shown in Figure 107, provides only kernel headersit does not contain any libraries.


We see in Figure 107 that the kmod being compiled is linked with several libraries and an object file called <kmod>_info.o, where <kmod> is the kmod's nameDummyDriver in our example. These entities serve the following purposes.

  • libkmodc++.a and libkmod.a both reside in /usr/lib/ and contain the runtime startup and shutdown routines for C++ and C, respectively.

  • <kmod>_info.c, the source file corresponding to the object file <kmod>_info.o, is generated during the kernel module's compilation. The combination of libkmodc++.a, libkmod.a, and <kmod>_info.a provides conceptually similar functionality as a user-space language runtime initialization object file (such as crt0.o).

  • libcc_kext.a is a specially compiled version of the GCC library (libgcc.a) that provides runtime support routines for code that runs in the kernel environment. Note that many standard libgcc routines are not supported in the kernel.

  • libcpp_kext.a is a minimal C++ librarya stripped-down version of libstdc++.a. Its purpose is similar to libcc_kext.a.

The order of arguments in the linker command line is instrumental in differentiating between the compilation of C++-based and C-based kmods. As shown in Figure 107, the order of object files and libraries in the linker command line is as follows:

...DummyDriver.LinkFileList ... -lkmodc++ ...DummyDriver_info.o -lkmod ...


The file DummyDriver.LinkFileList contains the pathnames of the kmod's object files. If the kmod uses C++, the compiler will add references in object files to undefined symbols called .constructors_used and .destructors_used. In the case of a kmod that does not use C++, the references to these symbols will not be present. Let us see how these symbols affect linking by examining the implementations of libkmodc++.a and libkmod.a, which are shown in Figures 108 and 1010, respectively.

Figure 108. Implementation of libkmodc++.a

// libkmodc++.a: cplus_start.c asm(".constructors_used = 0"); asm(".private_extern .constructors_used"); // defined in <kmod>_info.c extern kmod_start_func_t *_realmain; // defined in libkern/c++/OSRuntime.cpp extern kern_return_t OSRuntimeInitializeCPP(kmod_info_t *ki, void *data); __private_extern__ kern_return_t _start(kmod_info_t *ki, void *data) {     kern_return_t res = OSRuntimeInitializeCPP(ki, data);     if (!res && _realmain)         res = (*_realmain)(ki, data);     return res; } // libkmodc++.a: cplus_stop.c asm(".destructors_used = 0"); asm(".private_extern .destructors_used"); // defined in libkern/c++/OSRuntime.cpp extern kern_return_t OSRuntimeFinalizeCPP(kmod_info_t *ki, void *data); // defined in <kmod>_info.c extern kmod_stop_func_t *_antimain; __private_extern__ kern_return_t _stop(kmod_info_t *ki, void *data) {     kern_return_t res = OSRuntimeFinalizeCPP(ki, data);     if (!res && _antimain)         res = (*_antimain)(ki, data);     return res; }

Since libkmodc++.a exports the .constructors_used and .destructors_used symbols, it will be used to resolve references to these symbols in the case of a C++ kmod object file. As a side effect, the symbols _start and _stop will also come from libkmodc++.a. The <kmod>_info.c file uses these symbols to populate a kmod_info data structure (struct kmod [osfmk/mach/kmod.h]) to describe the kernel module. The kmod_info structure contains the starting address of the kernel module in its address field. Since the module is a Mach-O binary, the binary's Mach-O header is located at this address.

Juxtaposing the information in Figures 108 and 109, we see that the start and stop routines of an I/O Kit driver kmod will come from libkmodc++.a. Moreover, these routines will run OSRuntimeInitializeCPP() and OSRuntimeFinalizeCPP(), respectively. Since the _realmain and _antimain function pointers are both set to NULL in <kmod>_info.c, the start and stop routines will not invoke the corresponding functions.

Figure 109. Declaration of the kmod_info structure for an I/O Kit driver kernel module

// <kmod>_info.c for an I/O Kit driver (C++) ... // the KMOD_EXPLICIT_DECL() macro is defined in osfmk/mach/kmod.h KMOD_EXPLICIT_DECL(com.osxbook.driver.DummyDriver, "1.0.0d1", _start, _stop) __private_extern__ kmod_start_func_t *_realmain = 0; __private_extern__ kmod_stop_func_t *_antimain = 0; ...

In the case of a C++ kext, a particular virtual function may be overridden by another subclass at runtime. Since this cannot be determined at compile time, virtual function calls in a C++ kext are dispatched through the vtable. Depending on the kext ABI of the running system and the kext ABI of a given kext, the loading mechanism can patch vtables to maintain ABI compatibility.


OSRuntimeInitializeCPP() calls the preModLoad() member function of OSMetaClass, passing it the module's name as the argument. preModLoad() prepares the runtime system for loading a new module, including taking a lock for the duration of the loading. OSRuntimeInitializeCPP() then scans the module's Mach-O header, seeking segments with sections named __constructor. If any such sections are found, the constructors within them are invoked. If this process fails, OSRuntimeInitializeCPP() calls the destructors (that is, sections named __destructor) for those segments that had their constructors successfully invoked. Eventually, OSRuntimeInitializeCPP() calls the postModLoad() member function of OSMetaClass. postModLoad() performs various bookkeeping functions and releases the lock that was taken by preModLoad().

OSRuntimeFinalizeCPP() is called when a module is unloaded. It ensures that no objects represented by OSMetaClass and associated with the module being unloaded have any instances. It does this by checking all metaclasses associated with the module's string name and examining their instance counts. If there are outstanding instances, the unload attempt fails. The actual unloading operation is performed by OSRuntimeUnloadCPP(), the call to which is surrounded by preModLoad() and postModLoad(). OSRuntimeUnloadCPP() iterates over the module's segments, examining them for sections named __destructor and, if any are found, calling the corresponding destructor functions.

Next, let us see how libkmod.a (Figure 1010) is used in the implementation of a C-only kmod.

Figure 1010. Implementation of libkmod.a

// libkmod.a: c_start.c // defined in <kmod>_info.c extern kmod_start_func_t *_realmain; __private_extern___ kern_return_t _start(kmod_info_t *ki, void *data) {     if (_realmain)         return (*_realmain)(ki, data):     else         return KERN_SUCCESS; } // libkmod.a: c_stop.c // defined in <kmod>_info.c extern kmod_stop_func_t *_antimain; __private_extern__ kern_return_t _stop(kmod_info_t *ki, void *data) {     if (_antimain)         return (*_antimain)(ki, data);     else         return KERN_SUCCESS; }

In the case of a C-only kmod object file, which will not contain unresolved references to .constructors_used and .destructors_used, libkmodc++.a will not be used. The _start and _stop symbols referenced in <kmod>_info.c will come from the next library that contains these symbolsthat is, libkmod.a. As Figure 1011 shows, the <kmod>_info.c file of such a kernel module will set _realmain and _antimain to point to the module's start and stop entry points, respectively.

Figure 1011. Declaration of the kmod_info structure for a generic kernel module

// <kmod>_info.c for a generic kernel module (C-only) ... // the KMOD_EXPLICIT_DECL() macro is defined in osfmk/mach/kmod.h KMOD_EXPLICIT_DECL(com.osxbook.driver.DummyKExt, "1.0.0d1", _start, _stop) __private_extern__ kmod_start_func_t *_realmain = DummyKExtStart; __private_extern__ kmod_stop_func_t *_antimain = DummyKExtStart; ...

In other words, although every kmod has start and stop entry points, they can be implemented by the programmer only in the case of a generic kernel module. In the case of an I/O Kit driver, these entry points are unavailable to the programmer because they correspond to the C++ runtime initialization and termination routines. It follows that these two types of loadable entities cannot be implemented within the same kext.

10.4.3. Management of Kernel Extensions

The functionality for working with kernel extensions is implemented across several Darwin packages, such as xnu, IOKitUser, kext_tools, cctools, and extenTools. The following are the primary command-line programs available for managing kernel extensions.

  • kextd loads kexts on demand.

  • kextload loads kexts, validates them to ensure that they can be loaded by other mechanisms, and generates debugging symbols.

  • kextunload unloads code associated with a kext, terminating and unregistering I/O Kit objects, if any, associated with the kext.

  • kextstat displays the status of currently loaded kexts.

  • kextcache creates or updates kext caches.

  • mkextunpack extracts the contents of a multikext (mkext) archive.

kexTD, the kernel extension daemon, is the focal point of much of the activity that occurs when kexts are loaded or unloaded in a normally running system. During the early stages of bootstrapping, kextd is not yet available. The libsa support library in the kernel handles kernel extensions during early boot. Normally, libsa's code is removed from the kernel when kextd starts running. If you boot Mac OS X in verbose mode, you will see a "Jettisoning kernel linker" message printed by the kernel. kextd sends a message to the I/O Kit to get rid of the in-kernel linker. In response, the I/O Kit invokes destructors for the kernel's __KLD and __LINKEDIT segments and deallocates their memory. The memory set up by BootX is also freed. kexTD can be instructed (via the j option) not to jettison the kernel linker. This allows the kernel to continue handling all load requests. In this case, kexTD exits with a zero status if there is no other error. Bootable optical discs can use this option in startup scripts, along with an mkext cache, to accelerate booting. For example, as we saw in Section 5.10.4, Apple's Mac OS X installer disc runs kextd with the j option in /etc/rc.cdrom.

kextd registers com.apple.KernelExtensionServer as its service name with the Bootstrap Server. It processes signals, kernel requests, and client requests (in that order) in its run loop. kextd and command-line tools such as kextload and kextunload use the KXKextManager interface for manipulating kernel extensions. KXKextManager is implemented as part of the I/O Kit framework (IOKit.framework). Figure 1012 shows an overview of kextd's role in the system.

Figure 1012. Kext management


When kexTD starts running, it calls KXKextManagerCreate() to create an instance of KXKextManager and initializes it by calling KXKextManagerInit(). The latter creates data structures such as a list of kext repositories, a dictionary of all potentially loadable kexts, and a list of kexts with missing dependencies.

10.4.4. Automatic Loading of Kernel Extensions

If a kext is to be loaded every time the system boots, it must be placed in the /System/Library/Extensions/ directory. All contents of the kext bundle must have the owner and group as root and wheel, respectively. Moreover, all directories and files in the kext bundle must have mode bit values of 0755 and 0644, respectively. The system maintains a cache of installed kexts, along with their information dictionaries, to speed up boot time. It updates this cache when it detects any change to the /System/Library/Extensions/ directory. If an installer installs an extension as a plug-in of another, however, only a subdirectory of /System/Library/Extensions/ is updated, and the automatic cache update is not triggered. In such a case, the installer must explicitly touch /System/Library/Extensions/ to ensure that the caches are recreated to include the newly installed kext.

A kext can be declared as a boot-time kext by setting the OSBundleRequired property in its Info.plist file. The valid values this property can take include Root (required to mount root), Local-Root (required to mount root on locally attached storage), Network-Root (required to mount root on network-attached storage), Safe Boot (required even in safe-mode boot), and Console (required to provide character console supportthat is, for single-user mode).

Although a driver kext can be explicitly loaded with kextload, it is preferable to restart the system to ensure reliable matching so the driver can be considered for all potential devices it can drive. In the case of a running system, if a driver is already managing a given device, another driver will not be able to manage the device in question.





Mac OS X Internals. A Systems Approach
Mac OS X Internals: A Systems Approach
ISBN: 0321278542
EAN: 2147483647
Year: 2006
Pages: 161
Authors: Amit Singh

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net