Mac • 53:55
Snow Leopard provides a new kernel linker and kernel extension loading system that makes managing kernel extensions easier than ever, especially when dealing with multiple architectures. See new features, such as architecture-specific properties and cross-architecture symbol generation. Find out how to streamline your KEXT development process and adapt your KEXT to work with both 32-bit and 64-bit kernels.
Speakers: Nik Gervae, Andrew Myrick
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
Nick: Nick Gervae and I'll be talking to you about managing kernel extensions on Snow Leopard with my colleague Andrew Myrick. We are on the IOKit team and we rewrote the whole kext set for Snow Leopard. Before we start we have to do the obligatory do you really need to do this because you really shouldn't if you can avoid it. So, if you can, don't write a kernel extension. This is why, you're running in old school mode if you load into the kernel it's just like the old Classic days. You don't have memory protection.
If you crash the whole computer comes down. It's also true that debugging kernel code is a little more complicated than debugging user-space code and I'll be getting into that later. And it's worth pointing out that code's not going to run faster if it's in the kernel. Threads are scheduled the same way. It runs just as fast. There are a lot of things you can do from user-space without writing a kext, in particular if you're writing a lot of USB drivers including printer, scanner and camera drivers. You can do that from user-space.
A lot of other kernel services including Device Access is available through the IOKit framework in user-space so whenever possible check in user-space before you decide that you need to write a kext. That said, if you do have to write a kext it's going to be for one of these reasons. Low-level device drivers are the majority of kexts. These are things that need to get at the hardware directly through register access typically or that need to handle device interrupts directly.
Another reason you need a driver in the kernel is if your client is in their kernel. The same things apply to non-driver kext, these are typically network stack filters which have to work with the BSD networking layer above, kernel authorization plug-ins and file systems, of course, which work with the VFS layer above. What we're going to cover today is a little bit of basic information about the kext system as it has existed and then some new stuff as well.
So we're going to go over the general structure of a kernel extension, talk about the tools you use when working with and developing kexts. Debugging and installing as well and then we're going to get a little bit under the covers with how the kext system works and some of the runtime support that is available for kernel extensions. The big new features we're covering are these, of course, 64-bit kext. We're not going to go into detail but we're highlighting it here because we've got the new 64-bit kernel.
To support that we've got architecture specific properties that enable a kext to be portable as a single package on disk. And then probably the most significant change that you're going to see is that all of the developer functionality of kext that has been split out into a new tool for development work it's called kextutil and I'll be talking about that in a little bit.
We've got some new ways to load a kext with a C Call instead of forking and execing kextload as you used to have to do and there are also APIs within the kernel for getting resources out of your kext bundle now. What we're not going to cover is how to write any particular kind of kexts, so we're not going to go into the details of programming in the kernel, we're not going to cover any of the specific interfaces.
This is a generic session about kexts themselves. So let's get into the basics of what kext is and how you work with them. Pretty much like anything else a kext is a plug-in that is packages of bundle on disk. This is a very common way of packaging things in Mac OS X and you're probably already familiar with that. That bundle is just a folder that contains a few files.
The most important one for a kext or any bundle is the Info.plist; this describes what is in that bundle. It gives the identifier for the kext as well as a version and a couple of other properties that we'll be talking about in just a bit. Your kext probably also has an executable shown here as MyDriver. That gets loaded into the kernel.
And you may also have some resources. And I want to point out that unlike most bundles we only allow one level of plug-in kext; this is so that we can continue to boot the machine fast by not having to search deep recursive directory hierarchies. You'll see from the padlock there for security reasons because this code runs in the kernel all of the files and folders have to be owned by Root Wheel and Non-writable so that they can't be modified as an exploit. Finally, there are two kinds of kexts and they result in slightly different packages on disk. We've got Xcode templates to support those. One is an IOKit driver and the other is a generic kernel extension and I'll highlight the differences between those shortly.
To cover the Info.plist briefly it's a dictionary of properties and the core properties that a kext is going to have are, of course, the CFBundleIdentifier that uniquely identifies it as well as the executable that tells where to find the file on disk. The version is used along with the identifier so that we don't accidentally load a different version of the same kext.
And CFBundle allows a pretty free form use of the version. We actually define a specific format that has to be followed and you can see some examples of that right there. Also, for a kext because it's loaded at run time and has to get linked at that same time you have to list the libraries needed in the plist.
There are several optional properties that a kext can have depending on its needs. The first one is OSBundlerequired which allows your kext to load during very early boot, which is not the case by default, compatible version which allows your kext to be a library and export symbols for other kexts to link against and then two new properties we've added for Snow Leopard.
OSBundleAllowUserLoad which allows non-root users to load that kext and then another one for our new logging system that is described in the man page that I'll be mentioning. Also, IOKit drivers have IOKitPersonalities which is a nested dictionary of matching information that describes hardware the driver can operate. A new feature we've added in Snow Leopard is architecture specific properties.
To do one of these you just add the Architecture onto the end of any top-level OS or IO key with an _, so OSBundleLibraries_X86_64 for the K64 kernel; this allows a single kext with a single Info.plist to load on multiple platforms. If you are loading on a release prior to Snow Leopard you want to key the Base key without the architecture for backward compatibility. And I will point out this is specific to the kext system.
This isn't something you can use with CFBundle. The kext executable is just a Mach-O file typically multi-architectured these days and it embeds a little bit of bookkeeping info that lets us know it is a kext executable and we do use that at load and runtime. A kext is allowed to not have an executable and that's typically done for two reasons.
The main one is to publish IOKitPersonalities naming another kext. That is you would have a driver shipped with an executable and some personalities matching a particular set of devices and then you might ship a new bit of hardware with a different product idea or something but there's no new code that needs to be added.
You can just ship a kext with it that has personalities naming the other kext and it will get loaded. The other thing is that a kext can redirect linkage so it could say I'm a library of this name and I don't have an executable so when anybody links against me it's implicitly going to link against these other kext's.
Resource files, as I mentioned, are typically used in user-space for any data relevant to a bundle. They're used for NIB files, icons, other localized files in user-space. And now in Snow Leopard we can use them in kernel for firmware look-up tables and other such things. On previous releases you typically had to embed these in your Info.plist as driver matching properties which means they remain resident in wired memory forever.
We're hoping to make it possible that you can free up that wired memory by getting these resources when you need them, download them to your device and then get rid of them from wired kernel memory These are part of your kext bundle which means they can't change. They are read only, they are static. So, don't use these to store configuration or preferences.
Those have to be stored somewhere else. I mentioned some Xcode templates for kext earlier these are the two kinds and these are the principle differences between them. If you're writing an IOKit driver you're going to be working with our IOKit framework which is a C++ framework and it, together with a number of libraries called families define generic subclasses for different kinds of device drivers such as a network controller or a disk and you're going to subclass that and implement the bits that are just needed for your particular piece of hardware. An IOKit driver initializes and tears down through C++ statics and instance functions that automatically get called when it's loaded or when an instance needs to be created which means, also, that the kext can be loaded automatically by IOKit when it's needed.
Similarly when no more instances of your driver classes are present IOKit can just unload your kext automatically and you don't need to worry about that either. Things aren't quite so automatic for a generic kext. These are typically written in C++ and they register C functions with substances such as VFS so that you'll get a call-back, for example, when a mount event needs to occur for your file system or an open-call.
They're initialized and shut down through explicit start and stop functions that happen at load and unload time which must happen explicitly. Something external to the system has to ask for a generic kext to be loaded or unloaded. There are a number of tools that you're going to use when working with kexts for development and deployment.
These four are the ones you're going to be using as you create your kext. Kextutil is the new replacement for kextload while doing development and that's used for simple generation and sanity checking. Kextlibs is a program we introduced in Leopard to figure out your OSBundleLibraries for you automatically so you don't have to figure it out by yourself. And then, of course, kextstat lists the loaded kext on the system and kextfind is kind of like a super find for kext.
You can ask it all sorts of things about kext and find them. When deploying your kext you're going to be using kextloader and kextunload. That's pretty straight forward what those do. And then, of course, the system tools are kextd and kextcache. Kextd handles load requests from the kernel and a few other user-space RPC requests and kextcache rebuilds caches used at boot time and other times.
You're typically not going to be using either of these tools on Snow Leopard. Now that I've gone through those let's talk about how you use them. The first one is kextload, of course, and this is a significant change from Snow Leopard because we've removed about 2/3 of the command line options so that we can insulate installation and deployment from development. This tools now just sends and RPC to kextd, which does all the work of loading a kext into the kernel and it's got a really small set of arguments and options these days.
You can give it a kext bundle name to load or -bundle-id and it will go Have Kextd Look in System Library Extensions or you can tell it to look in another folder with -R or -D. And, of course, we've got the verbose options that have always been there so you can see if something went wrong and find out exactly what that was. The new tool is called kextutil. This is not for deployment loading.
It's installed on the base system for various packaging reasons I'm going into but you really should avoid using it in your shipping products if you can. This program requires root access because it talks directly to the Kernel, and it moves all those developer options that I mentioned from kextload for Snow Leopard. All the options for sanity checking, all the options for stage loading, generating debug symbols and for cross-architecture work. If you want to unload a kext you use kextunload. This is a pretty simple tool and it's primarily for development sellers but it's considered a deployment tool.
This tool, again, talks directly to the Kernel so it requires root access, it shuts down the kext, unloads it from kernel's address space and it also removes any IOKit driver matching information so that unless the caches get rebuilt and things get rescanned your driver's not going to load. It takes either bundle filename or an identifier and it will unload that kext if possible from the kernel.
Now we've got something new in Snow Leopard. We've had a lot of comments over the years that it's kind of a pain to have to fork an exec kextload to load a kext from a program so we've got CAPIs to do it now. You can do these RPCs to kextd to load a kext based on a path on disk.
That should be an absolute path, or with a bundle identifier, and the primary argument is that path or bundle identifier but there's a secondary argument that is basically equivalent to the -R and -D options to kextload so you can specify any other directories or kext URLS to use for dependency resolution. The other new feature we have for kext loading in Snow Leopard is kext that can be loaded by non-root users; this is something that kext itself has to allow because we don't want non-root users loading our arbitrary kext.
So if you as the developer of the kext know it's OK for your kext to be loaded at any time you can set this OSBundleAllowUser Load property to True, all of your dependencies also have to allow that, of course, and they also have to be in either /System/Library/Extensions/ or /System/Library/Files/Systems/.
We want to keep things really tight here, but we did want to make User Loading possible so that you can have non-set UID Binaries. This API is available for kextload and KextManager APIs. It's not available for kextutil because it has to talk to the kernel it has to run his root anyway. This facility is intended for generic kext.
IOKit kext doesn't need it so they shouldn't use it. And, again, unload requires root privilege because kextunload still has to talk to the kernel. And I do want to stress you should use this with care. It's best used with file systems because those typically shouldn't require set UID binaries. If you've got a reason for a network or a KOFF plug-in to use it then go ahead but think about that really seriously.
Now we're going to get into debugging kext and this is an area people often have trouble with I have people coming by all the time saying I don't know how to generate symbols from my kext and I can't debug so we figured we'd spend some time this year at WWDC going through that in some detail.
Here's a quick checklist of the techniques you might use to debug a kext. Starting from left to right, you know, based on how complicated the problem is, if your kext doesn't load you might get a message that tells you why right off the bat, but if it doesn't you can run kextutil with some sanity checking options to get more details. There are also the verbose flag options that will give you deeper details into the load process in case something's going wrong, and, of course, if it's something wrong with your kext start function you can log from your kext or you can use the debugger.
Similarly if you have a driver that isn't matching you can set a property in your IOKitPersonalities called IOKitDebug it's kind of outside the scope of this talk but I wanted to mention it here, or you can log from your kext or, again, resort to the debugger. Finally, if your kext is actually misbehaving or causes a panic you're typically going to want use one of those last two techniques.
So the first technique, sanity checking, is running kextutil-NT and I can't tell you how many times people come to me saying, my kext doesn't load, and I say, did you run kextutil-NT or kextload-NT on older systems, and they say, no I didn't, they do and it tells them right away what was wrong, so this is the first check to make when you're kext fails to load Always run that first if you have a problem.
-N or -NoLoad, we've got long options on Snow Leopard now, does most of these checks and prints some diagnostics about it. It does a trial link to report undefined symbols on Snow Leopard. This is new we've got a completely new link for architecture so we can actually do this dry run.
But it no longer authenticates ownership and permissions and we did that for convenience we got a lot of complaints from developers saying, I'm just trying to generate debug symbols why do I have to run this route or why do I have to add this extra option, so we don't do that now unless you add -T, which used to stand for Tests and the long option is now a print diagnostics because it prints some extra info as well.
This program uses the running kernel's architecture so if you're running on K64 but your kext didn't load on some other machine that's running K32 you want to be sure to add a -Arch flag to be sure you're using the right Architecture and that's particularly because of the cross-architecture properties and because you have a fat executable in your kext.
Using the Diagnostics has significantly improved on Snow Leopard. We've really tightened things up a lot. The Verbose Flag gives you more control and particular for kextutil it now focuses log messages only on the kext you're working with. You don't get spew about every single extension in the extension's folder but you can still ask for that if you want it.
These tools also now capture messages generated from within the kernel because we link kext in the kernel now so if something goes wrong there we'll capture those messages and print them right in your terminal session and you don't have to be running a tail -f in some other window on the System Logs to get that output. There's a lot of detail here that we just don't have time to go into so I recommend you check out the kext_logging man page and it will show you all the goodness that's there.
Similarly if you do need to check out what's going on in the kernel or with kextd you need to look in two places now in Snow Leopard. Kernel logging now goes to var/log/kernel.log so that we can kind of keep that separate from general user activity, and then kextd logging still goes to system.log.
You can use the same logging options for the tools in the Kernel Kext Logging System and kextd that's all documented in the man page as well. Now we're going to get into actually doing a debug session and this is where things get complicated. There are a number of ways you can do it.
I'm just going to go through the typical most common set-up and in that scenario you're going to have a development machine that you're doing your kext work on. You've got your kext there with the developer tools and the Kernel Debug Kit for the test release. You'll have Xcode running with your kext source and you're going to build an unstripped kext and Xcode will also build a dSYM file that is used by gdb.
Once you've done that you bring up your test machine and you'll want to start that with special debug boot-args, you're going to want to set the flags to be at least 0x14e, I'm not going to explain what those bits are but they are documented online, and probably also -V for a verbose kernel boot.
That machine should be connected to Ethernet when you start it up and at some point you're going to have the kext loaded in the kernel either before or after you start the debugger and then you're either going to have a panic that you're working with or you're going to run a non-maskable interrupt using the Power button, which that debugs0x14e enables. And once you've done that you'll run kextutil to generate relocated debug symbols for your kext and then you can load those into gdb. I'm going to walk through this process now in a number of different ways but first we'll talk about the Kernel Debug Kit.
This is a download from the ADC Member Site. It contains a symboled version of the kernel and all the Apple Library kexts so you can do some debugging with those. There is a different version for each release of Mac OS X including the software updates. And as of Snow Leopard we now include a debug build of the kernel and the system kext that you put on the test machine so that you can use those for debugging as well and there's a Read Me File in the Kernel Debug Kit that shows you how to do that. Those debug files I mentioned are used in a multi-staged process to get you from a source address of your PC to a symbol and then a line in your Source Code File.
The .sym, as I mentioned, is a relocated symbol info because kext get loaded into a different process and the debug process itself is so different we have to go through this in multiple stages, so the .sym gets us from an address to a symbol and then the de-sym generated by Xcode points that symbol and offset to the source code line. When you're ready to start gdb this is how you do it.
You're going to do this on your development machine and run gdb with -arch and you really want to specify that explicitly to avoid any confusion and you give it the mach kernel binary from the Kernel Debug Kit to start with. You're going to be loading your kext in a separate stage following this.
You'll then target gdb to the remote machine as remote -kdp. KDP stands for Kernel Debug Protocol and then you'll attach to that computer's IP address. Once you've done that kgmacros is a set of gdb macros available in the Kernel Debug Kit and those will give you a lot of useful utilities and some of them I'm going to mention here. And finally, for your .sym files you might want to tell gdb where to find those so you can set kext-symbol-file-path and that will ease some of the pain that's about to follow.
This is the point where you are going to have to generate your symbol files and the easiest way to do that is if you can load your kext on the test machine or if you have to do that just tell kextutil at the time you load to save the symbols and it will do so.
Kextutil, if you do this, will load the kext, will send the personalities down to the kernel for driver matching which may cause things to happen, it might cause your driver to actually get instantiated and then it will save the symbols to a directory that you specify. Once you've done that you want to copy those .sym files to your development machine for use in gdb and as it says there this is good for non-panic problems that occur after kext startup and I'll be covering dealing with panic problems in kext startup in just a little bit.
Another way you can do this if the kext is already loaded and it's too late you can get your symbols by telling kextutil not to load the kext but to save symbols in which case it will ask you for the load addresses. And on Snow Leopard now you can skip symbol generation for a given extension that kextutil, kextload, rather, on previously releases it used to be very fussy and if you didn't give an address it couldn't do the link. Now if we don't get an address we can fake an address and not save the symbol file and we'll just skip that for you.
So in this case we're going to skip USB family but then when it comes to MyDriver we enter a load address and then we're going to have symbols saved in /tmp and, again, you'll want to copy those on to your development machine or if you're working on your development machine you'll already have them there. You can add a -r and in this case to /tmp for dependency resolution wherever you need that. You may be wondering where we got that load address and this is one of the ways you can get it.
If you're running in gdb already and you're connected to the kernel you can source those kgmacros and then use showallkmods and it will print a list of all the loaded kext with their bundle ID's under the Name column and their addresses and that's the HEX number you'll want to put into kextutil not kmod and not Size which are HEX addresses that look like load addresses but they're not the ones you want to use. Similarly if you're on the test machine and it hasn't panicked you can run kextstat at the command li e and it gives you similar information.
Now, you can use the same setup to generate symbols for a different Architecture if you're working on K64 on your development machine and your test machine is running K32 you can speci2fy the architecture with -arch on the command line. If you do that kextutil can't talk to the kernel to get the symbol information for the kernel itself so you have to tell it where to look on disk for the kernel to be used and that will give you the symbols you need. I want to point out that K64 is actually a different architecture from K32 as far as the tools are concerned.
Similarly you can generate debug symbols for a different release of Mac OS X using the tools and if you want to do that you'll want to add a -e option which means don't look in the System Library Extension's folder to resolve dependencies because you're generating symbols for a different release. And then you use -r to add the directory that actually does contain the contents of System Library Extensions for that other release.
Make sure you use the right Kernel Debug Kit for the release you're doing your testing for. Once you've done that and you finally have your .sym file with the relocated info you can use the add -kext command in gdb and you just give it your kext bundle and it will look for that kext and the related files that .dsym and the .sym in the same folder, so they all have to be together unless you set that kext symbol file path in which case gdb will also go and look there for the .sym files so that may make your life a little bit easier. This is the point where you can set your break points and proceed to debug your kext and gdb.
Now, if you have to debug your kext start routine there's a bit of a problem because you can't get the load address until you load the kext but if you load the kext it's going to start running, or is it? You can use kextutil to pause before starting that kext so it will load the kext and then wait and similarly it will wait before starting Matching it will ask you for a Yes or No answer and it also delays Auto Unload by at least 2 minutes now on Snow Leopard it used to be just 1 minute on prior releases, so you have time to set up your debugger now.
For drivers I want to point out that you should unload your kext first to make sure those IOKitPersonalities aren't in the kernel already because matching might start while you're in the middle of things and that will just confuse matters. This is what things look like when you run kextutil with -i for Interactive.
When you do that it asks you if it's OK to load the driver now and you say Yes and then it says, I have loaded the driver, assuming it works, of course, and it says that this driver and its dependencies are now loaded but they're not started unless they were already running and that's for you to figure out; this is where you set up your break points in the debugger so you go back to your development machine and set everything up in gdb, you've add kext and you set your break points and then you say, Start my kext, Yes, and once you've done that it will say, is it OK to send personalities down to the kernel now, and you say Yes and then for each personality it asks and so on. Now that can get a little bit tedious if your driver has dozens and dozens of personalities and I know there are drivers that do.
So there's a slightly quicker way to do this for drivers only and that's to run kextutil twice. You can run it once with -l which means load it but don't do anything else, that will load and start if you've got a start routine for generic kext but it doesn't do driver matching.
Similarly this will delay Auto Unload for at least 2 minutes, actually, kextutil, in general, delays Auto Unload and kextload is the thing that doesn't now. Once you've got your symbols you copy them on to the development machine and then you can add your kext and set your break points.
After you've done that then you run kextutil with -m which means match and that sends the IOKitPersonalities down to the kernel. Your driver instances will be created and Probe and Start and what not will be called and you will hit your break points. There are a number of other ways to generate debug symbols and we're not going to bother going through them all you can find out about them on the man page.
But just to summarize if you already have the kext loaded on the machine and you need to generate symbols on that machine you can run kextutil with a -A option which means use the load addresses in the running kernel. You can also specify load addresses on the command line with -a or -address and you just give it a BundleID@ address and then you can also do the interactive thing for libraries.
If you're debugging a library kext you'll run -I and that will ask you for each library, whether it's OK to load, start and send personalities. All these options and more are documented in the kextutil man page. And if you want to find out more on kernel debugging there are some documents on the developer website you want to download.
The first one is Kernel Extension Programming Topics; this is a general introduction to kernel extension programming and some tutorials which are admittedly a little bit out of date but we're working on that so as you go through those apply what you've learned here today and make those changes as needed.
There's also a Kernel Programming Guide that covers the general issues of programming in the kernel address space and then two really excellent tech notes you should check out. 2063, which is all about understanding debugging kernel panics goes into a lot more detail about the stuff I just mentioned in the last few slides.
And another one for debugging kernel core dumps if that's all you've got if you don't have a live machine to attach to you can actually work with a core dump of a kernel. Now I'm going to hand things over to Andrew and he's going to talk about dependencies and a bunch of other cool stuff. There you go.
[Andrew Myrick]
Thank you Nick.
[ Applause ]
[Andrew Myrick]
Alright, good morning everyone. The first thing I'm going to talk to you today is about kext dependencies. Kext dependencies are the libraries that you use to develop your kext for kernel programming, and when I say libraries, loading kext into the system really is little more than static linking so the libraries that you're using with your kext are really just the kext binaries being statically linked against your kext in the system. We have two types of kext dependencies the first type are the built-in kernel programming interfaces what we call KPIs and they export the kernel subsystem symbols for a few different subsystems in the kernel, this is built-in.
There are also library kexts, the IOKit families and any library kext that you define which are loaded into the system along with your kext they define an export C++ sub classes, or -- sorry -- C++ super classes that you can derive and use in your kext. All of the headers for Apple's KPI's and libraries and IOKit families are in the kernel framework in the Headers Directory.
There are four types of public kernel programming interfaces, the major ones that we export free to use in your kexts. The first is the mach KPI and this has all the really basic primitives of the OS X kernel, things for time, threading, locks, stuff like that. Next is the libkern KPI and this is used for your general Lib C type stuff, things like memory and strings, atomic operations and the Utility C++ classes for your kext to use, which include the various containers that we provide like OS Array and OS Dictionary.
Next is the BSD KPI which, as its name implies, handles all the things for the BSD kernel, things like networking, file systems and K Off. And, finally, we have the IOKit KPI which exports all of the Core IOKit classes like IO Service. You may have also noticed that there is an unsupported KPI and as its name implies all the symbols in here are unsupported.
If you link against this KPI and use symbols from it your kext can break in any future release of OS X including a software update, so avoid this if you can at all possible and file a bug for supported interface so we can get you the tools you need to build your kext properly.
The kernel also has some compatibility interfaces, these are of the form com.apple.kernel and they're provided only for compatibility with Panther and earlier systems; this also implies that they're only available for the 32 bit kernel. So if your kext is already using KPIs there's absolutely no need to use com.apple.kernel interfaces.
Multi-architecture kexts are something that you may have encountered before and on Snow Leopard they still exist. Because we have a 64-bit kernedl and a 32-bit kernel this means that your kext need to run both in 64-bit and 32-bit mode.2 The available kernel interfaces for K32 and K64 do vary a little bit a lot of the changes focus around just getting rid of the 32-bit specific2 address methods and things like that. So the big take-away there is that the KPIs are really the way to move forward with your kext. If at all possible please stick to just the KPIs. The unsupported KPI is still available on 64-bit.
We've removed a lot of symbols. Please avoid if you can and file a bug with us. The com.apple.kernel interfaces, the old compatibility interfaces are gone. Now, as Nick mentioned before we've introduced architecture specific properties and one excellent way to use these is for your OSBundleLibraries property If your 32-bit kext needs to load on Panther and you need to depend on those old com.apple.kernel interfaces you can use OSLibrariesProperty to specify that for the 32-bit kext and still have just KPI interfaces for your 64-bit version.
I'll show you an example here of the architecture specific property in just a minute. First let's look at the structure of an OSBundleLibrary; this property lists the load time kext dependencies for your kext; this is not bill time these are the kext links against when loading into the kernel. Its keys are the bundle identifiers of the library kext against which you depend and the values are the minimum required versions of those libraries.
Here's an example of the OSBundleLibrariesProperty for a HelloIOKit.kext, as you can see it depends against the IOKit and libkern KPIs and the versions are 10.0b1 which is the Snow Leopard seed that you have in your hands. An architecture specific version of this kext here I've added a dependency, an extra symbol in the 64-bit version of this kext which uses something from the mach KPI. You can see the I386 version depends against just IOKit and LibKern. The x86_64 version also depends on the mach KPI.
As Nick mentioned earlier we introduced a tool in Leopard called Kextlibs and kextlibs can be used to determine the OSBundleLibraries needed by your kext. What it does is it looks at all the undefined symbols in your kext and goes through all of the kext in the system library extensions to see which kext define -- which libraries define those symbols for you to use.
It's based on the current running system so you should run it on the oldest release that you're targeting with your kext, the earliest of which is Leopard. So this means that for now you're a bit out of luck if you want to use this targeting in an older system. It also has a convenience flag -XML to generate raw XML and here you can see an example of kextlibs running against that HelloIOKit.kext we were working with earlier to generate the same OSBundleLibraries property we have.
Kextlibs is also capable of handing architecture specific properties which is new in Snow Leopard. And here we have the second kext that I showed you HelloIOKitwhich had that extra dependency on the mach KPI for x86_64 and kextlibs will handle this without a problem. There are a couple of known issues with kextlibs, we're sorry, the first is that as documented kextlibs will use the current versions of the libraries that your kext depends on when generating its output, the XML that it gives. And then you should be able to use -C to have it print the compatible versions of the libraries.
In Snow Leopard we got this a little backwards and kextlibs will print the compatible versions by default and then you can use -C to get the current versions, so we'll fix that in a future release. Also, just a heads up, the -r flag for specifying another repository is currently ignored so kextlibs only looks in the system library extensions. The final tool I want to mention is kextfind.
Kextfind is incredibly powerful it's got a ton of options and predicates that you can use to find different kext on your system and they're all documented in detail in the man page. One thing I want to call out is that you can use kextfind as an alternative to kextlibs when targeting older releases.
It has a predicate -dSYM which means Define Symbol which will fine all kext that define the symbol that you pass to it on the command line. You can also use the -f flag to specify an older release's system library extensions so you can use this from Snow Leopard to find a symbol for your Tiger kext if you have a Tiger install lying around. So here we have an example of trying to load a kext, a HelloIOKit kext that I've broken by having it use a new symbol that I haven't added a dependency for. So we try and load it and, oh no, it has an undefined symbol clock getup time.
What we can do is pass kextfind -dsym_clock_get_uptime and it will tell us that you can find this symbol in the mach kext which is the mach KPI. If you check in the Info.plist for this kext it will tell you the bundle ID and version that you should use and add to your Info.plist OSBundleLibraries property. Next I'm going to talk to you a little bit about kext linking. In Snow Leopard we've introduced a new kext linker that we've lovingly call KXLD. The kext linker is what takes the kext executables and bind them into the kernel.
It does a couple of things; first it resolves undefined symbols that your kext has against the libraries that you specified with your OSBundlesLibraries property. And second it does a little bit of behind the scenes work to ensure binary compatibility for older kexts on newer systems, but the details of that aren't really important.
We've built it from scratch for Snow Leopard with a lot of goals in mind we wanted to make it really fast, really portable, we got this new 64-bit kernel it has the support and we wanted it to be extensible so that we could take advantage of having this link here to add some new features. The primary goal for our architecture is that we wanted to link a kext only once.
Since the previous linker was built around the model of static linking every time you loaded a kext it would have to link your kext and all of its dependencies all over again. We didn't want to have to go through all that work so we came up with this idea of intermediate state objects which we call Link States and these objects contain the information that a loaded kext will export. We use it for library kexts and it just tracks all the exported symbols and V Tables that we need for future links.
We keep it in Page Memory so it doesn't have any wired memory impact and this really lets us speed up linking quite a bit. We also designed the kext linker to be completely cross architecture, 64-bit capable, biordinary dependant; it can handle any architecture you throw at it. And, finally, we designed it to run in both kernel space and user-space so not only does it run in the kernel but we also generate a library that the kext tools can use to do things like try a links when testing to see if your kext loads.
Linking a kext with KXLD looks like this, we have our kext and the linker and the linker's pool of link state objects that it has from previous link operations. When it's time to link the kext it gathers the kext and all the link state dependencies that it has, loads them in the linker, processes them and if we have another library kext that we're linking it generates a link state, adds it back to the pool that it can use for reference later and then takes the link kext and places it in its spot in the kernel address space ready for running.
Were we successful, how did performance do? Well, first looking at memory on a Mac Pro early 2008 we looked at the memory allocated during boot which is the working set size of the kext Linker. The old linker used about 90MB from power on to log-in window over the course of boot and the new linker uses just 13. Looking at speed the actual time it took to link a kext when linking the ATI Radion X2000 kext on the same Mac Pro; this is the graphics driver for that Mac, the old linker took 210 milliseconds to process that kext and the new linker takes just 25.
We've also been able enable a lot of new features because of KXLD. First there's cross architecture symbol generation not only will it generate symbols for your K32 kext and K64 kext regardless of what the Mac generating the symbols is running it can even generate power PC symbols for older release kext that you may have.
It also enables weak linking for C symbols for K64 kext I'll get into the details of that in just a minute. And, finally, enables segment protection for K64 kext so that dthe kext segment is right protected and the data segment is actually heat protected; this is something new as well.
Alright, so, we talked to you about loading kext's and debugging kext's and dealing with dependencies in the new linker now let's talk about how your kext actually runs in the kernel, what the run-time environment is like. A little background first, as you know, the kernel is the longest running task on the system it's really important not to leak and use resources that you don't need.
Allocated memory is wired so it can't be paged out and there's a lot of restrictions there's limited stack space, restricted floating point, restricted file IO, an incomplete C library and as Nick showed us debugging can be a little bit complicated. Kexts are not insulated from this environment in any way they run within the kernel's address space, there's no sandboxing, no address space protection, errors bring down the OS.
All code running within the kernel is inherently trusted so as we said before if you can do what you're trying to do without a kext please pursue that. The kext runtime has a lot of new features in Snow Leopard and we've introduced the following things: some Kext Introspection so you can learn more about your kext, Libkern C++ generic kexts, Opt-in auto-unload for generic kexts, Weak linking for C symbols on K64, Resource Requests for kext and load requests.
The introspection stuff is pretty straightforward we provide API's to get your current bundle identifier, get your kext version string and those are good for logging and just keeping track of information about your kext. And then something called OSKextGetCurrentLoadTag and the Load Tag is something we use in some of the other run-time APIs I'm about to introduce.
We strongly encourage you to adopt these APIs instead of using the old kmod_info struct and kmod_ functions. That struct has been deprecated those functions have either been deprecated or removed where possible we're going to be getting rid of those quickly as possible so please move to this new stuff for your future kexts.
In the past generic kexts while we've allowed C++ in them we haven't allowed you to use LibKernC++. The collections and other support C++ classes we provide and that's changed in Snow Leopard we now support it, it's now safe to use but please keep in mind that it is not supported prior to Snow Leopard so you should take care to set your OSBundleLibraries property appropriately so that your kext doesn't load on older systems and run into some tough to debug problems.
One feature we've gotten a lot of requests for is Auto Unload for generic kext so we've introduced an opt-in mechanism based on retained release model to auto unload your generic kext's. There's two APIs associated with this feature. First is OSKextRetainKextWithLoadTag and this takes the load tag parameter that I discussed a little bit ago that you get from calling OSKext GetCurrentLoadTag. Calling retained will increment the reference count and the first time you call it it will enable Auto Unload for this kext. Then you call OSKextReleaseKextWithLoadTime which will decrement the reference count and schedule an auto unload scan.
Now, when your retain count hits 0 your kext will be auto unloaded on the next pass so it's very important to track the resources that your kext has outstanding so that your kext doesn't get unloaded out from under them. You can do this in two ways. You can either use the Retain Count API itself or you can have some sort of internal tracking mechanism to track your kext resource usage and fail when your Stop function is called so your kext doesn't get unloaded until those are cleaned up.
For K64 we've introduced weak linking for C symbols in the kernel; this is a simple API OS kext symbol is resolved just pass in the symbol that you want to check and it will return true if the symbol is valid. Now the alerted attendee might notice that K64 is new so there are no symbols to link weakly, however, I can assure you that we've actually implemented this and we are checking every single symbol in the kernel to make sure that it exists and it is available for your use. This will become a bit more relevant in future releases as we add symbols that you want to use but will still want to be able to deploy to Snow Leopard.
Kext Resource Requests are another oft requested feature. They allow you to request a resource from a kext bundle to be passed down to your kext in the kernel. It's useful for briefly needed or optional resources such as device firmware, look-up tables, state machine data, generally large things that you don't want to include in the kext Binary or the Info.plist itself. It's important to note that resources are not localizable for kernel to use and they're not available before kextd starts so you need to keep that in mind when designing how you use this.
To request a resource simply call OSKextRequestResource; this function has 5 parameters; first is the Bundle Identifier of the kext you're requesting a resource from; this means that you can request the resource of another kext, potentially a kext that isn't loaded for your kext to use. Then you pass in the resource name which is the file name of the resource in the resources folder of the kext bundle. Next you pass in a call-back which will be invoked when the resource is available. There's a context pointer which you can pass in just general context that you may need.
And then there's a return pointer the request tag out which will give you a tag to track this resource request; this call will cause an asynchronous request to go to kextd to get this resource and then your call-back will be invoked when the resource is available. OSKextrequestResourceCallback looks like it's -- similarly it has 5 parameters first there's the request tag which is the tag you received when you called RequestResource. It will give you a result and if that result is successful it will pass you a pointer to the data and the data's link of that resource. And then, finally, the context pointer that you passed in when you requested the resource.
This callback is guaranteed to be invoked unless you cancel the request but it's important to note two things, first of all your kext does not own the thread that this callback is invoked on and the resource data will be destroyed when the callback returns so if you need to do anything non-trivial to this data copy it out into another bit of memory and process it on a thread that you own. If you need to cancel a kext resource request call OSKextCancelRequest and pass in that request tag it will return the context that you passed in for you to clean up if you need to do so.
Finally, we've introduced a mechanism for you to request that a kext be loaded from the kernel; this function's called OSKextLoadKextwithIdentifier Pass in the Bundle Identifier of the kext that you want to load. It's an asynchronous and there is no callback for this one. What this means is you need to provide your own rendezvous mechanism to determine when the kext that you requested has been loaded and is ready for you to use.
The last thing I want to talk to you about today is installing and uninstalling kexts. Kext installation really is nothing short of brain surgery on OS X. It affects everything it affects the kernel, kextd, IOKit matching, running applications that use services provided by drivers, anything you name it affects.
The kext system guards against gross failures but poor installation can still yield a bad user experience, so these bad user experiences consist of things like silent failures of the new device, incorrect driver used for the new device and an alert about an improperly installed kext made with bad permissions.
So when installing drivers first you should make sure that your device is not attached. If it is existing drivers may match immediately on that device and matching will get invoked again during installation so if you make sure the device is not attached you won't get any unpredictable results. Drivers, IOKit drivers should be installed to system library extensions.
Library extensions while not used right now is reserved for future use so please don't put anything there. Also, try to please avoid using symlinks the booter can not resolve them and it can result in some other hard to diagnose problems with the various kext tools. When installing generic kexts they can be installed anywhere and we recommend in putting them in system library extensions so that tools like kextlibs can find them, but you can also put them in your application bundle or support folder. Library extensions is still reserved so don't put them there and please don't put them in your User's folder.
If you're actually installing the kextfiles on releases prior to Snow Leopard meaning you're either creating a custom install or a package for installer.app we require that you have admin access and that you install or uninstall the whole kext atomically. And what I mean by that is you write your kext to temporary folder on the destination volume and then use a postflight script to set the owner and group to root wheel and remove writable permissions from the kext.
Finally, move the kext into the destination folder, for example, /System/Library/Extensions/. Then to reset the kext system on releases prior to Snow Leopard you can validate the kext caches by touching the destination folder which is, again, usually System Library Extensions, and then you can trigger a rescan of the extensions folder and restart driver matching on 10.2 and earlier by just requiring a restart.
10.3 and later you can send a SIGHUP to kextd and on Leopard and later this is all done automatically kextd watches the destination folder for you so after a small delay it will handle all of this regenerating the caches and resetting the kext system. Once this is done it's safe to go ahead and load generic kexts and the user can go ahead and attach device. You can get more information on this by checking out Q&A QA1319: Installing an IOKit Kext Without Rebooting.
There's some installer.app changes in Snow Leopard that you can take advantage of when installing your kext on products that support only Snow Leopard and later. Installer.app already performs atomic installs of all files and it will invalidate the kext caches for you following the post flight script. It does not, however, send a kextd a SIGHUP so if you can't wait that small delay that kextd has before it restarts matching and regenerates the caches you can force immediate matching by touching the destination folder again and sending SIGHUP to a kextd. Finally, a note on the kext caches. The cache formats and locations have changed with every release and I promise you we will change them again in every release so please make no assumptions about them not even where they are.
Just touch the install destination folder usually /System/Library/Extensions/ to get those caches regenerated and if you find any unexpected behavior file a bug and we'll get it fixed. A side note, if you're creating a bootable DVD there are some caveats with this so you can either find us in the labs or contact Developer Support.
Nick: So in summary we've made a ton of changes to the kext system, introduced a lot of new features, there's a new developer tool kextutil, we have 64-bit kext and cross architecture tools support to support those kexts along with architecture specific properties. There's an improved kext runtime that includes programmatic kext loading and repeats source requests in the kernel and it's all backwards compatible. So thank you very much for your time.