Just did some testing and bug fixing. Module loader works as expected. Here's some technical info.
Each process has a list of loaded modules in it, including a reference count. When module A depends on module B, and A is loaded B is loaded automatically. If it was already a loaded (perhaps an already-loaded module C depended on it), its reference count gets incremented. This all assures that when all modules depending on a certain module are unloaded, it can as well be unloaded, so as to free up the address space it takes up. The per-process module list is just a linked list, containing base address, module name (not path), and a pointer to the system-global list.
The system-global module list is what enables memory sharing of code and read-only data between processes loading the same module. It is a hashtable, indexed by a hash of the PATH of the module. This means that process A can load library A version X from path Y ad process B can load library A version Z from path T and they will not collide. The global table entries also have reference count, but this is per-process. This means that if 2 processes load module A, it's ref count in global table will be 2. If one process loads the module 5 times, the global reference count will still be 1. In any case the point is that when no more processes have a module loaded, we can evict it from memory, and free it up.