With the advent of XFree86 version 4 some fundamental assumptions about extending its functionality thankfully have changed. Metro Link has donated a module loading architecture that XFree86 has enhanced and extended so that now there exists a portable architecture to load object files (and even ar libraries) at run time into the address space of a process.
Several diffent object formats are supported in this architecture. While a dlopen() based loader is included, implementation issues with unresolved symbols in modules make it very hard to use. The obvious advantage of a dlopen() based loader (full support in system debuggers) contrasts the down side of implementation limitations and the non- portability of the resulting modules.
The most often used file format are ELF objects as created, e.g., on a Linux system. Additionally, a.out and COFF are supported as well. The host operating system and its preferred object file format have no influence on the selection, the loader code is fully selfcontained and needs no further support from the host OS beyond standard libc interfaces.
The server binary (which obviously has to be in the native executable file format of the host OS) can simply load modules at run time by calling
LoadModule(ModuleName, Path, SubDirs, Pattern, OptionList, ModReq, &errmaj, &errmin)This allows the server to load the given module, finding it according to the rules given in Pattern in the SubDirs of Path. The options given in the OptionList are passed to the setup function of the module when the module is first initialized. The loader framework can ensure that the required ABI versions given in ModReq are met.
Which modules are loaded at run time is defined through compiled in defaults (e.g., a bitmap font renderer is mandatory to be loaded), through the XF86Config file and through dependencies among the modules. The deferred symbol resolution strategy allows to load modules that have dangling references to other modules that will be loaded at a later point in time. Additionally, modules can reference symbols that the main server binary exports to them.
When a module is first loaded, before all symbol references are fulfilled, the loader code searches for a data element named <ModuleName>ModuleData that contains the module version information (including ABI versions that the module supports) and references to a setup and teardown function. In the next step the setup function is called with the options provided during the call to LoadModule().
At this phase only the symbols that are exported from the main server binary and all in-module references are resolved. The setup function therefore needs to avoid calling functions that are provided through other modules.
Once all modules are loaded, the remaining symbols in the modules are resolved and the main program can match addresses to symbols by looking them up with FunctionPtr = LoaderSymbol(FunctionName).