Haven't written down my approach yet, but in a very short overview it equates normal linking.
Each system module (different name, same content) is a normal .o file produced by the compiler (ELF) with all linking information in there. All namespace the module uses is completely oblivious to the kernel, no symbols are imported. It contains the normal ELF header with the entry point set to the constructor method (still have to figure out some decent way to get gcc to do this). A module is loaded into memory, it is moved to a place in kernel space (nope, not microkernelish) and from there the links to all symbols (both kernel & module) are established. Then, all module namespace entries are removed, and the module is attempted to be started with any object that is not yet handled from a type it can handle. If one or more of these is returned true, the module itself is marked as in use (separate table with module list) for that many times.
The module itself takes the objects provided in the constructor method and constructs one or more new objects. These are then added to the object repository. All objects have default ACL's associated with them, which are changed when the system has loaded the registry (something similar to windows's registry) where the acl's are stored. Each object has a number of default functions which are in the global object (such as the destructor and the setting-set and -get function) and a list of functions in the function list for normal module use. Thank god casts are cheap
In effect, each module has all core functions, and all modularly defined functions of a set of objects. It can use the core functions to do most core actions (take memory, map memory, take IO space, write to IO port, use mutex/semaphore etc) and the module functions to do whatever it requires from its parent module. It thus functions kind of like a layer on top of the parent object (which is what a kernel should be imo). The user can configure each present module to be loaded at bootup or not (dynamically, just stuff them in a library (ar-style)) so the boot process doesn't take longer than it should.
No modules that are not required are loaded, and if nothing new is detected the system should boot really fast. If something new is detected a new driver has to be found. This will be done someday by a userlevel database that connects to any system object that does not have a handler (ie, is not controlled).