The following information is provided to assist you in understanding kernel extension symbol resolution:
A kernel extension provides additional kernel services and system calls by specifying an export file when it is link-edited. An export file contains a list of symbols to be added to the kernel name space. In addition, symbols can be identified as system calls for 32-bit processes, 64-bit processes, or both.
In an export file, symbols are listed one per line. These system calls are available to both 32- and 64-bit processes. System calls are identified by using one of the syscall32, syscall64 or syscall3264 keywords after the symbol name. Use syscall32 to make a system call available to 32-bit processes, syscall64 to make a system call available to 64-bit processes, and syscall3264 to make a system call available to both 32- and 64-bit processes. For more information about export files, see ld Command in AIX 5L Version 5.2 Commands Reference, Volume 3.
When a new kernel extension is loaded by the sysconfig or kmod_load subroutine, any symbols exported by the kernel extension are added to the kernel name space, and are available to all subsequently loaded kernel extensions. Similarly, system calls exported by a kernel extension are available to all user programs or shared objects subsequently loaded.
The kernel provides a set of base kernel services to be used by kernel extensions. Kernel extensions can export new kernel services, which can then be used by subsequently loaded kernel extensions. Base kernel services, which are described in the services documentation, are made available to a kernel extension by specifying the /usr/lib/kernex.imp import file during the link-edit of the extension.
If a kernel extension depends on kernel services provided by other kernel extensions, an additional import file must be specified when link-editing. An import file lists additional kernel services, with each service listed on its own line. An import file must contain the line #!/unix before any services are listed. The same file can be used both as an import file and an export file. The #!/unix line is ignored when a file is used as an export file. For more information on import files, see ld command in AIX 5L Version 5.2 Commands Reference, Volume 3.
A restricted set of system calls can be used by kernel extensions. A kernel process can use a larger set of system calls than a user process in kernel mode. System Calls Available to Kernel Extensions specifies which system calls can be used by either type of process. User-mode processes in kernel mode can only use system calls that have all parameters passed by value. Kernel routines running under user-mode processes cannot directly use a system call having parameters passed by reference.
The second restriction is imposed because, when they access a caller's data, system calls with parameters passed by reference access storage across a protection domain. The cross-domain memory services performing these cross-memory operations support kernel processes as if they, too, accessed storage across a protection domain. However, these services have no way to determine that the caller is in the same protection domain when the caller is a user-mode process in kernel mode. For more information on cross-domain memory services, see Cross-Memory Kernel Services.
Note: System calls must not be used by kernel extensions executing in the interrupt handler environment.
System calls available to kernel extensions are listed in /usr/lib/kernex.imp, along with other kernel services.
Kernel extensions can be loaded and unloaded by calling the sysconfig function from user applications. A kernel extension can load another kernel extension by using the kmod_load kernel service, and kernel extensions can be unloaded by using the kmod_unload kernel service.
Normally, kernel extensions that provide new system calls or kernel services only need to be loaded once. For these kernel extensions, loading should be performed by specifying SYS_SINGLELOAD when calling the sysconfig function, or LD_SINGLELOAD when calling the kmod_load function. If the specified kernel extension is already loaded, a second copy is not loaded. Instead, a reference to the existing kernel extension is returned. The loader uses the specified pathname to determine whether a kernel extensions is already loaded. If multiple pathnames refer to the same kernel extension, multiple copies can be loaded into the kernel.
If a kernel extension can support multiple instances of itself (particularly its data), it can be loaded multiple times, by specifying SYS_KLOAD when calling the sysconfig function, or by not specifying LD_SINGLELOAD when calling the kmod_load function. Either of these operations loads a new copy of the kernel extension, even when one or more copies are already loaded. When this operation is used, currently loaded routines bound to the old copy of the kernel extension continue to use the old copy. Subsequently loaded routines use the most recently loaded copy of the kernel extension.
Kernel extensions can be unloaded. For each kernel extension, the loader maintains a use count and a load count. The use count indicates how many other object files have referenced some exported symbol provided by the kernel extension. The load count indicates how many explicit load requests have been made for each kernel extension.
When an explicit unload of a kernel extension is requested, the load count is decremented. If the load count and the use count are both equal to 0, the kernel extension is unloaded, and the memory used by the text and data of the kernel extension is freed.
If either the load count or use count is not equal to 0, the kernel extension is not unloaded. As processes exit or other kernel extensions are unloaded, the use counts for referenced kernel extensions are decremented. Even if the load and use counts become 0, the kernel extension may not be unloaded immediately. In this case, the kernel extension's exported symbols are still available for load-time binding unless another kernel extension is unloaded or the slibclean command is executed. At this time, the loader unloads all modules that have both load and use counts of 0.
So far, symbol resolution for kernel extensions has been concerned with importing and exporting symbols from and to the kernel name space. Exported symbols are global in the kernel, and can be referenced by any subsequently loaded kernel extension.
Kernel extensions can also consist of several separately link-edited modules. This is particularly useful for device drivers, where a kernel extension contains the top (pageable) half of the driver and a dependent module contains the bottom (pinned) half of the driver. Using a dependent module also makes sense when several kernel extensions use common routines. In both cases, the symbols exported by the dependent modules are not added to the global kernel name space. Instead, these symbols are only available to the kernel extension being loaded.
When link-editing a kernel extension that depends on another module, an import file should be specified listing the symbols exported by the dependent module. Before any symbols are listed, the import file should contain one of the following lines:
#! path/file
or
#! path/file(member)
Dependent modules can be found in an archive file. In this case, the member name must be specified in the #! line.
While a kernel extension is being loaded, any dependent modules are only loaded a single time. This allows modules to depend on each other in a complicated way, without causing multiple instances of a module to be loaded.
The symbols exported by dependent modules are not added to the kernel name space. These symbols can only be used by a kernel extension and its other dependent modules. If another kernel extension is loaded that uses the same dependent modules, these dependent modules will be loaded a second time.
Dual-mode kernel extensions can be used to simplify the loading of kernel extensions that run on both the 32- and 64-bit kernels. A "dual-mode kernel extension" is an archive file that contains both the 32- and 64-bit versions of a kernel extension as members. When the pathname specified in the sysconfig or kmod_load call is an archive, the loader loads the first archive member whose object mode matches the kernel's execution mode.
This special treatment of archives only applies to an explicitly loaded kernel extension. If a kernel extension depends on a member of another archive, the kernel extension must be link-edited with an import file that specifies the member name.
The operating system provides the following two libraries that can be used by kernel extensions:
The libcsys.a library contains a subset of subroutines found in the user-mode libc.a library that can be used by kernel extensions. When using any of these routines, the header file /usr/include/sys/libcsys.h should be included to obtain function prototypes, instead of the application header files, such as /usr/include/string.h or /usr/include/stdio.h. The following routines are included in libcsys.a:
The libsys.a library provides the following set of kernel services:
When using these services, specify the -lsys flag at link-edit time.
To simplify the development of kernel extensions, you can choose to split a kernel extension into separately loadable modules. These modules can be used when linking kernel extensions in the same way that they are used when developing user-level applications and shared objects. In particular, a kernel module can be created as a shared object by linking with the -bM:SRE flag.. The shared object can then be used as an input file when linking a kernel extension. In addition, shared objects can be put into an archive file, and the archive file can be listed on the command line when linking a kernel extension. In both cases, the shared object will be loaded as a dependent module when the kernel extension is loaded.