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.
Thanks to the various help here, I managed to complete my kext, without Xcode, using only makefiles.
If anyone is interested by a complete kext example for macOS Ventura using makefiles only, you may have a look here:
https://github.com/lelegard/arm-cpusysregs
Related
I am looking through a PowerPC Mach-O executable in different programs, and I noticed something strange. For symbols that are to stubbed functions, XMachOViewer and any normal hex viewing of the file reports the address differently than Ghidra does.
For example, in a Mach-O for cc1 from the version of GCC 3 that came with the Mac OS 10.1 SDK, the address corresponding to the symbol "_exit" is said to be 0x9002c860.
Ghidra, however says it is 0x27981c.
Is the address masked and is Ghidra unmasking it somehow if that's the case? If so, how do I unmask it myself?
I've realized how dumb I am.
Ghidra is reporting the address of the executable's pointer to the actual function, which is in a dylib, and the hex is actually reporting the address of the actual function.
I looked at the libsystem dylib and found functions in that address space.
My kext file should be loaded automatically at mac os boot time.
It currently appears in / Library / Extention, but it does not seem to be loaded automatically at boot time.
Is there a way?
For it to load automatically, it must either be an IOKit based kext, or another kext that gets loaded must depend on it.
If it's an IOKit kext, and there is no specific device which should trigger the loading of the kext, you can simply match the IOResources object. The official documentation on device & driver matching talks about how to set up matching in general, and about matching IOResources specifically.
I've not clear what is the difference between drivers that can be "embedded" inside a monolithic kernel and drivers available only as external modules.
What kind of effort is requested to "port" some driver (provided as "external module" only) to a monolithic kernel?
I would like to be able to run Vmware Tools disabling loadable modules support and getting rid of the initrd bazaar.
Though the driver more or less remains the same(in both cases),there are definitely benefits for using "drivers" embedded in monolithic kernel.
I'll try to explain the "effort in porting" the driver part which you've asked.
Depending on the kind of driver you've, essentially you've to figure out how it will fit in the current kernel source tree, its compilation(include your .ko in the uImage) and loading of it while kernel booting. Let's illustrate each step a bit:
a.) Locate the folder (in the kernel source tree) where you think it is best suited to keep your driver code.
b.) Work on to make sure your driver code is getting compiled.[i.e ultimately it will be part of monolithic kernel image(uImage or whatever you call it)]. In this context, You've to work on your Makefile for your driver. You might have to introduce some CONFIG flags to compile your driver code. There are tons of Makefiles' and driver code lying in the source tree. Roam around and you will get a good reference of how it is being done.
c.) Make sure that your driver code is independent of any other
loadable kernel module(i.e such modules which are not part of the
"monolithic" kernel image). Because if you invoke your driver
code(which is monolithic now and is in memory) which depends on
loadable module code then it may cause some kernel
panic/segmentation fault kind of error.
d.) Make sure that your driver is registered with a higher level of
subsystem which will be initializing all the registered drivers
during boot-up time.(for example: an i2c driver once registered
with i2c driver framework will be loaded automatically when i2c subsystem is initialized during system startup). This step might not be really required if you can figure out another way of invoking your driver's __init and __exit functions.
e.) Now, Your Driver _init and (_exit sections) "should" be called
if it is getting loaded by any device driver framework or directly(i.e. while
kernel is booting up ).
f.) In case of h/w drivers, we have .probe implementation in driver
which will be invoked once the kernel finds a corresponding device.
In case of s/w drivers, I guess __init and __exit is all you have.
g.) Once it is loaded, you can use it like you were using it earlier as a loadable kernel module
h.) I'll recommend reading source code of similar device drivers in the linux kernel tree and see how they are operating.
Hope this helps.
This question already has an answer here:
Linking Dylibs in Kexts?
(1 answer)
Closed 9 years ago.
I am writing a kext driver for OS X and would like to use functions from the library libpcap.dylib. Libpcap.dylib lives in /usr/lib on OS X. Can it be used from kernel space? How can I use libpcap.dylib from a kext using Xcode?
I manage to compile -- (-lpcap apears as link option) but:
got an warning on "unexpected dylib" by linker. It is clear that is misplaced somehow.
kextload can't resolve libpcap dependencies.
kextlibs shows only libs that I include thru OsBundleLibraries suggesting that my dylib is ignored.
I am aware of similar question Linking Dylibs in Kexts? but want to know if someone have have used libpcap on a kext.
As is noted in Linking Dylibs in Kexts?, it's not possible to load a dylib in to the kernel via a kernel extension.
You don't mention what it is you're trying to achieve so it's difficult to know what alternatives would be relevant to you. I'd suggest reading up on [Network Kernel Extensions][1] to see if one of the techniques they cover could be used instead of pcap. Alternatively, you could make use of pcap from a userspace program and communicate with it from your kernel extension.
WinPcap has both user-land and kernel-mode components, because the Windows kernels don't provide the necessary kernel-mode components.
On UN*X systems - for example, on OS X - the kernel-mode components are part of the OS, and libpcap only includes user-mode code.
The equivalent, in *BSD and OS X, of WinPcap's kernel-mode code is BPF, which you won't be able to use from a kext. In addition, BPF has no equivalent of the send-queue stuff to do synchronized transmission of packets - you can send packets, but that just immediately injects the packet into the network stack - so neither using libpcap from your kext, nor using raw BPF from your kext, would help you with your timing needs.
I am using the u-boot-2011.12 on my OMAP3 target, the cross tool chain is CodeSourcery arm-none-linux-gnueabi, I compiled u-boot, downloaded it onto the target and booted it, everything went fine,but I have some questions about the u-boot relocation feature, we know that this feature is base on PIC(position independent code), position independent code is generated by setting the -fpic flag to gcc, but I don't find fpic in the compile flags. Without the PIC, how can u-boot implement the relocation feature?
Remember when u-boot is running there is no OS yet. It doesn't really need the 'pic' feature used in most user applications. What I'll describe below is for the PowerPC architecture.
u-boot is initially running in NV memory (NAND or NOR). After u-boot initializes most of the peripherals (specially the RAM) it locates the top of the RAM, reserves some area for the global data, then copies itself to RAM. u-boot will then branch to the code in RAM and modify the fixups. u-boot is now relocated in RAM.
Look at the start.S file for your architecture and find the relocate_code() function. Then study, study, study...
I found this troubling too, and banged my head around this question for a few hours.
Luckily I stumbled upon the following thread on the u-boot mailing list :
http://lists.denx.de/pipermail/u-boot/2010-October/078297.html
What this says, is that at least on ARM, using -fPIC/-fPIE at COMPILE TIME is not necessary to generate position independent binaries. It eases the task of the runtime loader by doing as most work up-front as possible, but that's all.
Whether you use fPIC or not, you can always use -pic / -pie at LINK TIME, which will move all position-dependent references to a relocation section. Since no processing was performed at COMPILE TIME to add helpers, expect this section to be larger than when using -fPIC.
They conclude that for their purposes using -fPIC does not have any significant advantage over a link-time only solution.
[edit] See commit u-boot 92d5ecba for reference
arm: implement ELF relocations
http://git.denx.de/cgi-bin/gitweb.cgi?p=u-boot.git;a=commit;h=92d5ecba47feb9961c3b7525e947866c5f0d2de5