kldd: ldd Style Analysis For Solaris Kernel Modules
Ali Bahrami Thursday January 11, 2018
Solaris 11.4 comes with a new user level command, /usr/bin/kldd. kldd performs ldd style analysis for kernel modules.
The ldd utility is used to analyze the dependencies of a userland dynamic object. Given an object, it tells you what other objects it depends on, as well as revealing related details. This is very basic, and very useful ability. ldd has been part of our product since it debuted, along with support for dynamic linking in SunOS 4.0 in late 1988. It subsequently became part of AT&T System V Release 4 Unix, and has been adopted by numerous other Unix vendors, and Linux. The reason for this ubiquity is that ldd performs a basic and necessary service.
Until now, there was no similar utility for kernel modules, even though such a utility would be as useful for kernel modules as for any other type of object. There are many possible reasons for this lack of support, including:
In recent years, the Solaris linking group has done work to improve this situation, and bring more of the userland linking abilities we take for granted to the kernel. Early steps have included a recent overhaul of the kernel runtime linker (krtld), and the explicit tagging and finalization of kernel modules.
One can imagine future projects building on this base:
ldd is really a small wrapper program that causes the runtime linker
to run in a special
ldd has a variety of options for getting ld.so.1 to print differing types of information. These options are conveyed from the ldd wrapper to the runtime linker via private environment variables. In addition, knowledgeable users can use the LD_DEBUG environment variable to obtain information that the ldd options do not address. For instance, the lari utility runs a command similar to the following in order to obtain information about symbol binding:
% ldd -e LD_DEBUG=bindings,files,detail object
The runtime linker has no useful analysis to offer for objects other than dynamic objects (executables or shared objects). ldd is therefore useless with relocatable objects or kernel modules.
The implementation and features of ldd are all directly related to userland objects and the implementation of the userland runtime linker. I briefly explored the idea of having the runtime linker load kernel modules in a special mode in which it applies the rules employed by the kernel runtime linker (krtld) and disables any non-applicable userland functionality. However, it quickly became evident that this is a bad idea, as there is relatively little overlap between the way the userland ld.so.1 works, and krtld. I believe that the code in ld.so.1 that we could use for analyzing kernel modules is less than the amount of code we would need to add in order to special case ld.so.1 so that it could handle them. Adding such complexity to the already complex ld.so.1 is a move in the wrong direction.
Given that we cannot use the userland ld.so.1 to do this analysis for kernel modules, the next best idea would seem to be to write the necessary code independently of ld.so.1, and to have the ldd wrapper call that code for kernel modules, rather than doing its usual trick in running the runtime linker. This is technically feasible, but I believe that it would be counter productive. There are 2 main reason for this:
If ldd is implemented as a wrapper on the userland runtime linker, ld.so.1, should kldd be implemented as a wrapper on the kernel runtime linker, krtld? There is an appealing symmetry in that thought, but the two cases are rather different, and the ultimate answer is "no".
A basic truism of operating system development is that only things that cannot be achieved in userland belong in the kernel. kldd can be implemented as a userland process, and in so doing, benefit from the usual protections and resource management that provides.
$ kldd /kernel/misc/amd64/idmap sys/doorfs => /kernel/sys/amd64/doorfs strmod/rpcmod => /kernel/strmod/amd64/rpcmod misc/tlimod => /kernel/misc/amd64/tlimod unix (parent) => /platform/i86pc/kernel/amd64/unix genunix (parent dependency) => /kernel/amd64/genunix
$ kldd misc/idmap sys/doorfs => /kernel/sys/amd64/doorfs strmod/rpcmod => /kernel/strmod/amd64/rpcmod misc/tlimod => /kernel/misc/amd64/tlimod unix (parent) => /platform/i86pc/kernel/amd64/unix genunix (parent dependency) => /kernel/amd64/genunix
$ kldd -s misc/idmap search path=/platform/i86pc/kernel:/kernel:/usr/kernel (default) trying path=/platform/i86pc/kernel/misc/amd64/idmap trying path=/kernel/misc/amd64/idmap find module=sys/doorfs; required by /kernel/misc/amd64/idmap trying path=/platform/i86pc/kernel/sys/amd64/doorfs trying path=/kernel/sys/amd64/doorfs sys/doorfs => /kernel/sys/amd64/doorfs find module=strmod/rpcmod; required by /kernel/misc/amd64/idmap trying path=/platform/i86pc/kernel/strmod/amd64/rpcmod trying path=/kernel/strmod/amd64/rpcmod strmod/rpcmod => /kernel/strmod/amd64/rpcmod find module=misc/tlimod; required by /kernel/strmod/amd64/rpcmod trying path=/platform/i86pc/kernel/misc/amd64/tlimod trying path=/kernel/misc/amd64/tlimod misc/tlimod => /kernel/misc/amd64/tlimod find kernel=unix; implicit unix => /platform/i86pc/kernel/amd64/unix find module=genunix; required by /platform/i86pc/kernel/amd64/unix trying path=/platform/i86pc/kernel/amd64/genunix trying path=/kernel/amd64/genunix genunix => /kernel/amd64/genunix
This output has been formatted for display purposes, and errors not directly related to mod_a or mod_b have been omitted.
$ kldd -prUw -R real demo/mod_b demo/mod_a => /local/test/demo/amd64/mod_a misc/sha2 => /kernel/misc/amd64/sha2 misc/kcf => /kernel/misc/amd64/kcf unix (parent) => /platform/i86pc/kernel/amd64/unix genunix (parent dependency) => /kernel/amd64/genunix symbol not found: extern_sym (/local/test/demo/amd64/mod_a) symbol not found: parent_sym (/local/test/demo/amd64/mod_a) symbol not found: weakref_sym (/local/test/demo/amd64/mod_a) unreferenced object=/kernel/misc/amd64/sha2; unused dependency of /local/test/demo/amd64/mod_a
| Core File Enhancements for elfdump|| Testing...Without...System Into A Brick|