macoskernel-extensionxnu

"Failed to bind" while loading a kernel extension on macOS 13 Ventura


I try to build and load a basic kernel extension on macOS 13.2 Ventura on M1 (Apple Silicon).

There is a lot of literature online on macOS kext. However, many things have changed in recent versions of macOS regarding kext and most of this literature is obsolete.

Disclaimer: I know that "system extensions" should now be used instead of kext in most cases. In my specific case, this is a test kext to learn more on the Arm64 architexture and my purpose is only to read a few CPU system registers. These registers can be read at EL1 (kernel) only. This is why a system extension is not suitable and a kext is required. BTW, I already did the same thing on Linux with a dedicated loadable kernel module. I now try to transpose this to macOS.

As a prerequisite, I disabled SIP and allowed custom kexts.

I started with the kext template in Xcode and added a few traces, either using printf() (several articles on kexts mention a printf function in the kernel) or os_log(). The build is successful. I copied the kext bundle in another directory and make it owned by root.

When loading the module with sudo kmutil load -p test-kext.kext, I get the following errors when using os_log(). When using printf(), I have the same messages with _printf instead of __os_log_internal.

Error Domain=KMErrorDomain Code=31 "Error occurred while building a collection: 
    1: One or more binaries has an error which prevented linking.  See other errors.
    2: Could not use 'test-kext' because: Failed to bind '__os_log_internal' in 'test-kext' (at offset 0x0 in __DATA_CONST, __auth_got) as could not find a kext which exports this symbolFailed to bind '__os_log_default' in 'test-kext' (at offset 0x0 in __DATA_CONST, __got) as could not find a kext which exports this symbol
test-kext specific: 
    1: Failed to bind '__os_log_internal' in 'test-kext' (at offset 0x0 in __DATA_CONST, __auth_got) as could not find a kext which exports this symbolFailed to bind '__os_log_default' in 'test-kext' (at offset 0x0 in __DATA_CONST, __got) as could not find a kext which exports this symbol
" UserInfo={NSLocalizedDescription=Error occurred while building a collection: 
    1: One or more binaries has an error which prevented linking.  See other errors.
    2: Could not use 'test-kext' because: Failed to bind '__os_log_internal' in 'test-kext' (at offset 0x0 in __DATA_CONST, __auth_got) as could not find a kext which exports this symbolFailed to bind '__os_log_default' in 'test-kext' (at offset 0x0 in __DATA_CONST, __got) as could not find a kext which exports this symbol
test-kext specific: 
    1: Failed to bind '__os_log_internal' in 'test-kext' (at offset 0x0 in __DATA_CONST, __auth_got) as could not find a kext which exports this symbolFailed to bind '__os_log_default' in 'test-kext' (at offset 0x0 in __DATA_CONST, __got) as could not find a kext which exports this symbol

The Xcode build is successful. In the build log, I see that the link command for the kext uses the options -no_adhoc_codesign -kext -nostdlib -lkmodc++ -lkmod -lcc_kext. It seems that some kernel libraries are referenced and I would expect to find the log functions.

In /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform, I have found libkmod.a and libkmodc++.a but I could not find any *cc_kext*. There is no symbol *printf* or *os_log* in libkmod.a and libkmodc++.a.

How would you log kernel messages from a kext on macOS 13 and how would you build the result?


Solution

  • libkmod and libkmodc++ don't provide access to general kernel APIs; they implement some small stubs of functionality related to kext startup and shutdown code which interact with the boilerplate kext code auto-generated by Xcode, as well as a handful of OSKext*() functions.

    Kernel APIs are exported to kexts from the core kernel via "KPIs" which have bundle identifiers in the same way that kexts do. So the mechanism for linking against core kernel APIs is the same as declaring a dependency on another kext: the OSBundleLibraries Info.plist property.

    Because identifying which KPI a particular symbol is defined in, there's a tool that scans your kext and spits out a list of bundle identifiers: kextlibs. There is a man page for kextlibs which goes into detail on more advanced uses of the tool.

    As established in the comments, missing the requisite libkern KPI from OSBundleLibraries is what's causing the link errors in this case.