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?
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.