The Anatomy of a Kernel Extension

Kexts are loadable bundles, and like all loadable bundles, they are loaded dynamically by another application. In the case of a kext, this application is the kernel itself. This has many implications for kexts, such as running in supervisor mode and the ability to load during early boot. Kexts have strict security and location requirements that you need to follow for your kext to work.

To understand the anatomy of a kext, you should have a basic understanding of bundles. For general information on the structure of a bundle, see Bundle Programming Guide.

A Kext Bundle Usually Contains Two Main Components

In the most general case, a kext bundle contains two components: an information property list and an executable. Along with these components, a kext bundle may include additional resources and plug-ins. Each of these components is described here.

The Information Property List

A kext’s Info.plist file describes the kext’s contents. Every kext must have an Info.plist file. Because a kext can be loaded during early boot when limited processing is available, this file must be in XML format and cannot include comments. The following keys are of particular importance in a kext’s Info.plist file:

  • CFBundleIdentifier is used to locate a kext both on disk and in the kernel. Multiple kexts with a given identifier can exist on disk, but only one such kext can be loaded in the kernel at a time.

  • CFBundleExecutable specifies the name of your kext’s executable, if it has one.

  • CFBundleVersion indicates the kext’s version. Kext version numbers follow a strict pattern (see Info.plist Properties for Kernel Extensions).

  • OSBundleLibraries lists the libraries (which are kexts themselves) that the kext links against.

  • IOKitPersonalities is used by an I/O Kit driver for automatically loading the driver when it is needed.

There are several more kext-specific Info.plist keys that allow you to further describe your kext. For a complete discussion of all kext Info.plist keys, including keys that refer to kernel-specific runtime facilities, see Info.plist Properties for Kernel Extensions.

The Executable

This is your kext’s compiled, executable code. Your executable is responsible for defining entry points that allow the kernel to load and unload the kext. These entry points differ depending on the Xcode template you use when creating your kext. Table 1 describes the default differences between the two kext Xcode templates. This table is intended to illustrate only the most common use of each template; the kernel does not differentiate between kexts created with different templates, and it is possible to incorporate elements of both templates into a kext.

Table 1  A comparison of the two Xcode templates for creating a kext

Generic kernel extension template

IOKit driver template

Programming language

Usually C

C++

Implementation

C functions registered as callbacks with relevant subsystems

Subclasses of one or more I/O Kit driver classes, such as IOGraphicsDevice

Entry points

Start and stop functions with C linkage

C++ static constructors and destructors

Loading behavior

Must be loaded explicitly

Loaded automatically by the I/O Kit when needed

Unloading behavior

Must be unloaded explicitly

Unloaded automatically by the I/O Kit after a fixed interval when no longer needed

Tutorial

Creating a Generic Kernel Extension with Xcode

Creating a Device Driver with Xcode

Some kexts don’t include an executable. These kexts (called codeless kexts) are typically used to tell I/O Kit to use an existing driver for your device. See Codeless Kernel Extensions Match New Devices to Existing Drivers for more information.

Additional Resources and Plug-ins

Kexts sometimes require additional resources, such as firmware for a device. If your kext requires a resource, put it in the Resources folder of your kext’s bundle. If you plan to localize your resources, keep in mind that kernel-space code does not detect localized resources. User-space code does detect localized resources in .lproj subfolders of the Resources folder, so if your resource is accessed only by user-space code, localization is straightforward.

In addition to general resources, kexts can contain plug-ins, including other kexts. If your kext uses a plug-in, put it in the PlugIns folder of your kext’s bundle. Make sure that plug-in kexts do not contain plug-in kexts of their own; only one level of plug-ins is detected in order to limit file system traversal during early boot.

Kernel Extensions Have Strict Security Requirements

Kexts execute in kernel space and run in supervisor mode; consequently, files and folders in a kext bundle must be owned by the root user and the wheel group. Files must have the permissions 0644, and folders must have the permissions 0755. A kext that fails to meet these requirements will not load into the kernel.

During development, to ensure that your kext has the proper ownership and permissions, create a copy of your kext as the root user.

% sudo cp -R MyKext.kext /tmp
Password:

This method requires creating a new copy of the kext every time you build it.

Kernel Extensions Must be Developer ID for Kexts Signed and Reside in /Library/Extensions

With System Integrity Protection, kernel extensions must be signed with a Developer ID for Signing Kexts certificate, and installed into the /Library/Extensions directory.

You can request a Developer ID Certificate for signing kexts by visiting https://developer.apple.com/contact/kext and filling out the required details.

See Kernel Extensions in System Integrity Protection Guide for more information.

Codeless Kernel Extensions Match New Devices to Existing Drivers

A codeless kext is a kext bundle that does not contain an executable. A codeless kext’s IOKitPersonalities dictionary names other kexts that are loaded when a personality matches on a device. Each of these other kexts must have an executable. Codeless kexts are commonly used with USB and HID devices that are driven from user space. Because the kernel driver implements a standard protocol, it can be used by nearly all devices in these categories.

For example, most USB printers share a generic driver provided by Apple, AppleUSBMergeNub.kext. Apple cannot include the matching dictionary for every printer in this kext, so you can install a codeless kext with a personality that matches your printer and set the personality's CFBundleIdentifier to com.apple.driver.AppleUSBMergeNub. When your printer is attached to the computer, AppleUSBMergeNub.kext is loaded to drive it. Listing 1 shows an example of such a codeless kext’s IOKitPersonalities dictionary, in XML format.

Listing 1  The IOKitPersonalities dictionary of a codeless kext

<key>IOKitPersonalities</key>
<dict>
    <key>My_USB_Printer</key>
    <dict>
        <key>CFBundleIdentifier</key>
        <string>com.apple.driver.AppleUSBMergeNub</string>
        <key>IOClass</key>
        <string>AppleUSBMergeNub</string>
        <key>IOProviderClass</key>
        <string>IOUSBInterface</string>
        <key>idProduct</key>
        <integer>0000</integer>
        <key>idVendor</key>
        <integer>0000</integer>
    </dict>
</dict>