3

In order to achieve better encapsulation and modularity I've decided to split my kernel driver into 2 (can be more) modules where each is responsible for different functionality.

However, there are still some data+logic which I'd like to share between those modules (i.e. one module can manage the communication with user-space, while the other uses it as mediator) and I wonder if there's any easy way to do so.

For example, I'd like to publish some API from one module to another, which is absolutely doable since both modules are running under kernel process and are mapped in separated ranges in the same address space.

The catch is that each kernel module has symbol table of its own, and in order to publish the API, some sort of loader is needed to fix the addressing/pointers/etc.. It's like calling dlopen and dlsym from user-space when dynamically linking with library, but in kernel space and where each library also possess state (the state defined by the current snapshot of all its inner heap/global parameters).

My question is whether this approach is valid and accpeted in the realms of macOS?

EDIT, in the following question, it's explained the linux way of achieving my goal, Perhaps do you know what's the equivalent in macOS/XNU to symbol_get and symbol_put ?

  • Your question does not mention macOS/xnu, but does mention FreeBSD. Which is it? If the latter, the kernel-extension and xnu tags are not appropriate. – pmdj Dec 11 '17 at 16:45
  • Fixed the question to fit the tags. I know that xnu driver infrastructure is rather different than linux and it's based on C++ code, but i'm seeking to find our if sharing API/memory to communicate drivers is an appropriate approach in general.. if you have any thoughts/comment about this approach, I'd be happy to hear. thanks ! –  Dec 11 '17 at 20:07
  • Currently answering your question requires knowledge in **both** in Linux kernel and Mac OS kernel. E.g. I know about Linux (its kernel modules can interact in manner like user space libraries) but unaware about Mac OS. Ask about **single** (specific) OS instead. – Tsyvarev Dec 11 '17 at 23:06
  • @tsyvarev, ok i got your comment, i just assumed this concept is universal and widely used in the industry. I saw that in macOS you can define stateless libraries and link with them in another state-full kernel module. However, i wanted to interact between 2 "living" modules .. not just a bunch of functions. Anyway, I've removed the macOS tag.. perhaps you can share your knowledge in the linux realm ? thanks ! –  Dec 12 '17 at 07:03
  • 1
    Possible duplicate of [Inter-module communication in Linux kernel](https://stackoverflow.com/questions/15699518/inter-module-communication-in-linux-kernel) – mrdvlpr Dec 12 '17 at 11:21
  • 1
    In Linux, kernel-modules are objects modules, they can (and do) export and import symbols. Use `modinfo` and `nm -g` for details. – Lorinczy Zsigmond Dec 12 '17 at 11:29
  • @mrdvlpr, thanks for letting me know saved me quite a while of searching, perhaps you're familiar with xnu ? I wish to find a way how to do so in this platform as well .. notice that i've edited my question accordingly. –  Dec 12 '17 at 11:31

1 Answers1

2

It looks like the Linux side has already been answered in comments.

For macOS kexts, the mechanism to use are the OSBundleLibraries and OSBundleCompatibleVersion Info.plist properties. The kext exporting symbols must have the OSBundleCompatibleVersion property set. This must be less than or equal to its CFBundleVersion and allows you to version your API.

The kext which wishes to import the other kext's symbols must list the exporting kext's bundle identifier in the OSBundleLibraries dictionary, with the appropriate version number.

Note that linking against another kext will import all of its public symbols, so I strongly recommend making all symbols default-hidden and providing an explicit exports file. To do this, enable "Symbols hidden by default" in the Xcode target settings, create a new .exp (or .exports) file, and declare it in the "Exported Symbols File" setting. At a minimum, you will need to add _kmod_info to this file. Then add all the symbols you wish to export, one on each line.

C functions and global variables will need to be prefixed with an underscore, C++ functions and static class member variables will need to be mangled in the usual way. You can use * as a (partial) wildcard, which is sometimes handy for C++ classes with a lot of member functions, for example. The xnu source distribution contains plenty of examples of exports files if you need a reference. You can use the nm tool to generate a list of all the symbols in your kext, from which you can then pick & choose; this saves you from manually mangling names.

Kexts can't circularly depend on one another. One will need to be the "library," the other the "user" of that library. If they need to interact, you will need to use callbacks, virtual functions, etc.

The "library" kext must be installed in /Library/Extensions (/System/Library/Extensions on OS X 10.8 or earlier) or the user of the library will not find it, potentially even if the library kext is already loaded. If your "user" kext specifies in its OSBundleRequired property that it may be needed for local or network booting, the libraries it depends on should declare the same or a superset of those conditions, or they may not be appropriately prelinked/kextcached.

Apple does have a small amount of documentation on designing kext libraries too.

pmdj
  • 22,018
  • 3
  • 52
  • 103
  • Great answer, thanks ! I've started implementing accordingly, and let you know for any issues if any –  Dec 12 '17 at 13:34
  • Yes, it works ! thanks again... btw, I guess it's not possible, but perhaps you know if i can load my client kext even if the library kext is not loaded (with "Local" implementation of the exported symbols) ? or maybe even more extreme... perhaps there's a way to load a kext which dynamically decides if it's library is loaded, and toggle between the local and remote implementation accordingly. –  Dec 13 '17 at 09:28
  • You can have weak symbols like in any other Mach-O binary, but I don't think you can have a whole kext dependency missing. I suspect kextd won't load your kext if one of the OSBundleLibraries can't be found, even if there are no unresolved hard links. One way you could do it however would be by not marking it as a dependency, but instead do all of the communication via OSObject based C++ classes. You can create instances of such classes based on the class name (as a string), and if that class doesn't exist, it should just report an error. So if the library kext isn't loaded it should soft-fail – pmdj Dec 13 '17 at 09:33
  • That's a very interesting approach, I wasn't aware of the possibility to implement `createInstance` that will fail if the class name doesn't exist in run-time. But how exactly will the loading of new module, make the class suddenly as existing... perhaps I'll ask it in another thread since it's a broad topic –  Dec 13 '17 at 10:15
  • Hi, I've followed your instruction and build library kext which based on generic kernel extension, which uses c++ class to store all the logic (class that based on `OSObject` so that I can overwrite it with another derivative class). however, it seems like I cannot release the class object properly, perhaps you can address my question here : `https://stackoverflow.com/questions/47990273/macos-generic-kernel-extension-with-c-class` thanks –  Dec 27 '17 at 10:29