Site hosted by Angelfire.com: Build your free website today!

Hands-on Projects for the Linux Graphics Subsystem

New book from Christos Karayiannis

Available in Amazon Kindle format at:

amazon.com   amazon.co.uk   amazon.de   amazon.es   amazon.fr   amazon.it

Module Loading

The xorg.conf modules

Modules specified in the config file and driver modules required by the X Server should by loaded at this stage. At xorg.conf man page we see a description of the "Module Section" and "Device Section" that include in the xorg.conf file the modules to be loaded. There is also an "Inputdevice Section" which includes the modules for the mouse and keyboard.

Consider for instance the following part of the xorg.conf:


Section "Module"
	Load  "extmod"
	Load  "glx"
	Load  "dri"
	Load  "dbe"
	Load  "record"
	Load  "xtrap"
	Load  "type1"
	Load  "freetype"
EndSection

Section "InputDevice"
	Identifier  "Keyboard0"
	Driver      "keyboard"
	Option	    "XkbRules" "xfree86"
	Option	    "XkbModel" "pc101"
	Option	    "XkbLayout" "us"
EndSection

Section "InputDevice"
     Identifier  "Mouse0"
     Driver	 "mouse"
     Option	 "Device" "/dev/sysmouse"
     Option	 "Protocol" "auto"
     Option	 "ZAxisMapping" "4 5 6 7"
EndSection

Section "Device"
	Identifier  "Card0"
	Driver      "mga"
	VendorName  "Matrox"
	BoardName   "MGA G400 AGP"
	ChipSet     "mgag400"
	BusID       "PCI:1:0:0"
	Option	    "NoDDC"
	Option      "AGPMode" "4"             	
EndSection

At the previous config file we see that the driver name is mga. The module loading stage is logged in /var/log/Xorg.0.log. At this file we find that the driver's module, mga_drv.so, is loaded along with the other modules:

(II) LoadModule: "extmod"
(II) Loading /usr/local/lib/xorg/modules/extensions//libextmod.so
(II) Module extmod: vendor="X.Org Foundation"
	compiled for 7.2.0, module version = 1.0.0
	Module class: X.Org Server Extension
	ABI class: X.Org Server Extension, version 0.3
. . .
(II) LoadModule: "glx"
(II) Loading /usr/local/lib/xorg/modules/extensions//libglx.so
(II) Module glx: vendor="X.Org Foundation"
	compiled for 7.2.0, module version = 1.0.0
	ABI class: X.Org Server Extension, version 0.3
(==) AIGLX disabled
(II) Loading extension GLX
(II) LoadModule: "dri"
(II) Loading /usr/local/lib/xorg/modules/extensions//libdri.so
(II) Module dri: vendor="X.Org Foundation"
	compiled for 7.2.0, module version = 1.0.0
	ABI class: X.Org Server Extension, version 0.3
(II) Loading extension XFree86-DRI
(II) LoadModule: "dbe"
(II) Loading /usr/local/lib/xorg/modules/extensions//libdbe.so
(II) Module dbe: vendor="X.Org Foundation"
	compiled for 7.2.0, module version = 1.0.0
	Module class: X.Org Server Extension
	ABI class: X.Org Server Extension, version 0.3
(II) Loading extension DOUBLE-BUFFER
(II) LoadModule: "record"
(II) Loading /usr/local/lib/xorg/modules/extensions//librecord.so
(II) Module record: vendor="X.Org Foundation"
	compiled for 7.2.0, module version = 1.13.0
	Module class: X.Org Server Extension
	ABI class: X.Org Server Extension, version 0.3
(II) Loading extension RECORD
(II) LoadModule: "xtrap"
(II) Loading /usr/local/lib/xorg/modules/extensions//libxtrap.so
(II) Module xtrap: vendor="X.Org Foundation"
	compiled for 7.2.0, module version = 1.0.0
	Module class: X.Org Server Extension
	ABI class: X.Org Server Extension, version 0.3
(II) Loading extension DEC-XTRAP
(II) LoadModule: "type1"
(II) Loading /usr/local/lib/xorg/modules/fonts//libtype1.so
(II) Module type1: vendor="X.Org Foundation"
	compiled for 7.2.0, module version = 1.0.2
	Module class: X.Org Font Renderer
	ABI class: X.Org Font Renderer, version 0.5
(II) Loading font Type1
(II) LoadModule: "freetype"
(II) Loading /usr/local/lib/xorg/modules/fonts//libfreetype.so
(II) Module freetype: vendor="X.Org Foundation & the After X-TT Project"
	compiled for 7.2.0, module version = 2.1.0
	Module class: X.Org Font Renderer
	ABI class: X.Org Font Renderer, version 0.5
(II) Loading font FreeType
(II) LoadModule: "mga"
(II) Loading /usr/local/lib/xorg/modules/drivers//mga_drv.so
(II) Module mga: vendor="X.Org Foundation"
	compiled for 7.2.0, module version = 1.4.6
	Module class: X.Org Video Driver
	ABI class: X.Org Video Driver, version 1.1
(II) LoadModule: "mouse"
(II) Loading /usr/local/lib/xorg/modules/input//mouse_drv.so
(II) Module mouse: vendor="X.Org Foundation"
	compiled for 7.2.0, module version = 1.1.1
	Module class: X.Org XInput Driver
	ABI class: X.Org XInput driver, version 0.7
(II) LoadModule: "keyboard"
(II) Loading /usr/local/lib/xorg/modules/input//keyboard_drv.so
(II) Module keyboard: vendor="X.Org Foundation"
	compiled for 7.2.0, module version = 1.0.0
	Module class: X.Org XInput Driver
	ABI class: X.Org XInput driver, version 0.7

As we see the following modules, which are included in the "Module" section, are loaded first:

libextmod.so
libglx.so
libdri.so
libdbe.so
librecord.so
libxtrap.so
libtype1.so
libfreetype.so

Then the modules of the video driver, found at the "Device" section, that is mga_drv.so is loaded next to be followed by the modules of the two "InputDevice" sections mouse_drv.so and keyboard_drv.so.

Each module is a Dynamically Linked "Shared Object" Libraries (.so) with a 'soname' (see Linux How-to) of the form lib<name>.so. Notice that a module can also call a submodule.

Where in memory are the modules loaded?

It certainly depends on many factors, for instance the module format (e.g. elf), the architecture (e.g. i386), the kind of the system the X server runs (32-bit or 64-bit), the number of the modules loaded, etc. To take a view from above we can read the Bactrace sections of the Xorg.0.log files from X serves that crashed. Such files can return a 'Backtrace Xorg.0.log' query from Google. Consider from instance the following example:

Backtrace:
0: /usr/bin/Xorg(xorg_backtrace+0x3c) [0x80a442c]
1: /usr/bin/Xorg [0x80a7c26]
2: [0x5ba40c]
3: /usr/lib/libpixman-1.so.0 [0x15be10]
4: /usr/lib/libpixman-1.so.0 [0x15c001]
5: /usr/lib/libpixman-1.so.0 [0x1275c4]
6: /usr/lib/libpixman-1.so.0(pixman_blt+0x79) [0x149aa9]
7: /usr/lib/xorg/modules/libfb.so(fbCopyNtoN+0x1aa) [0x291cca]
8: /usr/bin/Xorg(miCopyRegion+0x213) [0x818bf33]
9: /usr/bin/Xorg(miDoCopy+0x44d) [0x818c45d]
10: /usr/lib/xorg/modules/libfb.so(fbCopyArea+0x79) [0x2912b9]
11: /usr/lib/xorg/modules/libexa.so [0xc8d8b0]
12: /usr/lib/xorg/modules/libexa.so [0xc86d5e]
13: /usr/bin/Xorg(miCopyRegion+0x213) [0x818bf33]
14: /usr/bin/Xorg(miDoCopy+0x44d) [0x818c45d]
15: /usr/lib/xorg/modules/libexa.so [0xc851db]
16: /usr/bin/Xorg [0x811a894]
17: /usr/lib/xorg/modules/libfb.so(image_from_pict+0x332) [0x295982]
18: /usr/lib/xorg/modules/libfb.so(fbComposite+0xc4) [0x295c94]
19: /usr/lib/xorg/modules/libexa.so [0xc8e65b]
20: /usr/lib/xorg/modules/libexa.so [0xc8bd60]
21: /usr/bin/Xorg [0x81187d7]
22: /usr/bin/Xorg(CompositePicture+0x290) [0x810bf80]
23: /usr/bin/Xorg [0x80d17d8]
24: /usr/lib/xorg/modules/drivers/radeon_drv.so [0xe5a496]
25: /usr/bin/Xorg [0x81a87db]
26: /usr/bin/Xorg [0x80e572b]
27: /usr/bin/Xorg(BlockHandler+0x58) [0x8074278]
28: /usr/bin/Xorg(WaitForSomething+0x104) [0x80a1dd4]
29: /usr/bin/Xorg [0x806eb70]
30: /usr/bin/Xorg [0x8063115]
31: /lib/libc.so.6(__libc_start_main+0xe6) [0x5d1b36]

A backtrace shows the thread the functions follow inside X server (appears as 'Xorg' in the previous list), from the last function run when the program was crashed, to the first. The last routine is xf86SigHandler(). This is the function registered for X server, in the case unexpected signals, e.g. SIGSEGV (see InitOutput()). Then xf86SigHandler() calls xorg_backtrace(), which calls backtrace_symbols(), which is described from the Linux backtrace_symbols man page.

The address returned from the backtrace belongs to the virtual address space of the current process, i.e. the X server (Xorg). We also notice that the X server runs in a 32-bit system (8 hex numbers, e.g 0xffffff). We find that in the virtual address space, which is dedicated to the X server alone, the server itself is loaded near the memory location 0x8070e8b and the mga_drv.so driver near 0xe7d075 if we use as example the next backtrace:

Backtrace:
0: Xorg(xf86SigHandler+0x81) [0x80ceb41]
1: [0x88b420]
2: /usr/lib/xorg/modules/drivers//mga_drv.so(MGAClientGetBiosInfo+0x7f) [0xe6ea9f]
3: /usr/lib/xorg/modules/drivers//mga_hal_drv.so(HSLGBIOSReadPins+0x12d) [0xfa05fd]
4: /usr/lib/xorg/modules/drivers//mga_hal_drv.so(HSLCONGGetPInSInfo+0x44) [0xfa37a4]
5: /usr/lib/xorg/modules/drivers//mga_hal_drv.so(HSLCONGGetBoardInfo+0x3c) [0xfa2a4c]
6: /usr/lib/xorg/modules/drivers//mga_hal_drv.so(HSLGInit+0x65) [0xf85e25]
7: /usr/lib/xorg/modules/drivers//mga_hal_drv.so(HALGGetBoardInfo+0xc8) [0xf84368]
8: /usr/lib/xorg/modules/drivers//mga_hal_drv.so(MATROXOpenLibrary+0x58) [0xf79be8]
9: /usr/lib/xorg/modules/drivers//mga_drv.so [0xe7d075]
10: Xorg(InitOutput+0x9a8) [0x80a2f68]
11: Xorg(main+0x27b) [0x8070e8b]
12: /lib/libc.so.6(__libc_start_main+0xe0) [0x17cf70]
13: Xorg(FontFileCompleteXLFD+0x1e1) [0x8070391]

The source code of the loading part

The following part of InitOutput() is responsible for loading the three categories of modules mentioned previously, that is the modules specified explicitly in the config file, video drivers and input drivers:

#ifdef XFree86LOADER
    /* Load all modules specified explicitly in the config file */
    if ((modulelist = xf86ModulelistFromConfig(&optionlist))) {
      xf86LoadModules(modulelist, optionlist);
      xfree(modulelist);
      xfree(optionlist);
    }

    /* Load all driver modules specified in the config file */
    if ((modulelist = xf86DriverlistFromConfig())) {
      xf86LoadModules(modulelist, NULL);
      xfree(modulelist);
    }

#ifdef USE_DEPRECATED_KEYBOARD_DRIVER
    /* Setup the builtin input drivers */
    xf86AddInputDriver(&XF86KEYBOARD, NULL, 0);
#endif
    /* Load all input driver modules specified in the config file. */
    if ((modulelist = xf86InputDriverlistFromConfig())) {
      xf86LoadModules(modulelist, NULL);
      xfree(modulelist);
    }

    /*
     * It is expected that xf86AddDriver()/xf86AddInputDriver will be
     * called for each driver as it is loaded.  Those functions save the
     * module pointers for drivers.
     * XXX Nothing keeps track of them for other modules.
     */
    /* XXX What do we do if not all of these could be loaded? */
#endif

    /*
     * At this point, xf86DriverList[] is all filled in with entries for
     * each of the drivers to try and xf86NumDrivers has the number of
     * drivers.  If there are none, return now.
     */

    if (xf86NumDrivers == 0) {
      xf86Msg(X_ERROR, "No drivers available.\n");
      return;
    }

The first part is for modules specified explicitly in the config file, then follows the video drivers part and the last one is the input drivers part:

    if ((modulelist = xf86ModulelistFromConfig(&optionlist))) {
      xf86LoadModules(modulelist, optionlist);

At xf86ModulelistFromConfig() as the comment indicates:

/*
 * use the datastructure that the parser provides and pick out the parts
 * that we need at this point
 */
The data structure the parser prepares is:
conf_modules
which as explained in 3.2.2.1 is a field of the global struct XF86ConfigRec. conf_modules is actually of type XF86ConfModulePtr which is defined as:

typedef struct
{
	XF86LoadPtr mod_load_lst;
	char *mod_comment;
}
XF86ConfModuleRec, *XF86ConfModulePtr;

Therefore conf_modules->mod_load_lst is walked and the module names are returned and the module options update the routine's argument. For Module options see the xorg.conf man page.

xf86LoadModules() iterates over the list that is being passed as the first argument and for each module name calls LoadModule().

LoadModule()

LoadModule() is called with the following arguments that are explained in loadmod.c:


/*
 * LoadModule: load a module
 *
 * module       The module name.  Normally this is not a filename but the
 *              module's "canonical name.  A full pathname is, however,
 *              also accepted.
 * path         A comma separated list of module directories.
 * subdirlist   A NULL terminated list of subdirectories to search.  When
 *              NULL, the default "stdSubdirs" list is used.  The default
 *              list is also substituted for entries with value DEFAULT_LIST.
 * patternlist  A NULL terminated list of regular expressions used to find
 *              module filenames.  Each regex should contain exactly one
 *              subexpression that corresponds to the canonical module name.
 *              When NULL, the default "stdPatterns" list is used.  The
 *              default list is also substituted for entries with value
 *              DEFAULT_LIST.
 * options      A NULL terminated list of Options that are passed to the
 *              module's SetupProc function.
 * modreq       An optional XF86ModReqInfo* containing
 *              version/ABI/vendor-ABI requirements to check for when
 *              loading the module.  The following fields of the
 *              XF86ModReqInfo struct are checked:
 *                majorversion - must match the module's majorversion exactly
 *                minorversion - the module's minorversion must be >= this
 *                patchlevel   - the module's minorversion.patchlevel must be
 *                               >= this.  Patchlevel is ignored when
 *                               minorversion is not set.
 *                abiclass     - (string) must match the module's abiclass
 *                abiversion   - must be consistent with the module's
 *                               abiversion (major equal, minor no older)
 *                moduleclass  - string must match the module's moduleclass
 *                               string
 *              "don't care" values are ~0 for numbers, and NULL for strings
 * errmaj       Major error return.
 * errmin       Minor error return.
 *
 */

LoadModule() calls LoaderOpen() as:

    ret->handle = LoaderOpen(found, name, 0, errmaj, errmin, &wasLoaded);

where ret is of type ModuleDescPtr:

typedef struct module_desc {
    struct module_desc *child;
    struct module_desc *sib;
    struct module_desc *parent;
    struct module_desc *demand_next;
    char *name;
    char *filename;
    char *identifier;
    XID client_id;
    int in_use;
    int handle;
    ModuleSetupProc SetupProc;
    ModuleTearDownProc TearDownProc;
    void *TearDownData;		/* returned from SetupProc */
    const char *path;
    const XF86ModuleVersionInfo *VersionInfo;
} ModuleDesc, *ModuleDescPtr;

LoaderOpen()

Starting with Xorg 7.0, dlloader has become the default module loader for X (see this Gentoo link). LoaderOpen() calls therefore either the specific funcs[modtype].LoadModule routine for versions up to 6.9 or the DLLoadModule() for version 7.0 or latter.

Case I (Old versions!): LoaderOpen() calls funcs[modtype].LoadModule

LoaderOpen() opens the module as:

fd = open(module, O_RDONLY)

LoaderOpen() calls _GetModuleType() to return the Module type at modtype:

modtype = _GetModuleType(fd, 0)

If for instance the module object is ELF it returns LD_ELFOBJECT. _GetModuleType() does this by comparing the magic number of the ELF file header with the module's header data. The elf magic number is the hex 7f 45 4c 46 which stands in ascii for 7fELF. It is defined in elf.h as:

#define	ELFMAG		"\177ELF"

For more info on ELF see for instance this page.

Next tmp of type loaderPtr is used to store info about the module:

	tmp = _LoaderListPush();

	tmp->handle = modrec->handle;
	tmp->module = moduleseq++;
	tmp->cname = xf86loadermalloc(strlen(modrec->cname) + 1);
	strcpy(tmp->cname, modrec->cname);
	tmp->funcs = &funcs[modtype];
	if (longname == NULL) {
	    modnamesize = strlen(hdr.ar_name);

_LoaderListPush() returns tmp, a static loaderPtr. It is defined in loader.h as:

typedef struct _loader *loaderPtr;

where _loader is found in the same file as:

/* Each module loaded has a loaderRec */
typedef struct _loader {
    int handle;			/* Unique id used to remove symbols from
				 * this module when it is unloaded */
    int module;			/* Unique id to identify compilation units */
    char *name;
    char *cname;
    void *private;		/* format specific data */
    loader_funcs *funcs;	/* funcs for operating on this module */
    loaderPtr next;
} loaderRec;

Next the LoadModule specific routine for the current module is called as:

	if ((tmp->private = funcs[modtype].LoadModule(tmp, arfd, &lookup_ret))

The funcs[] item that provide the LoadModule is found at position 'modtype', which for our previous example was LD_ELFOBJECT (1). Looking therefore at the first position in funcs[] we find that LoadModule becomes ELFLoadModule:

static loader_funcs funcs[] = {
    /* LD_ARCHIVE */
    {ARCHIVELoadModule,
     ARCHIVEResolveSymbols,
     ARCHIVECheckForUnresolved,
     ARCHIVEAddressToSection,
     ARCHIVEUnload, {0, 0, 0, 0, 0}},
    /* LD_ELFOBJECT */
    {ELFLoadModule,
     ELFResolveSymbols,
     ELFCheckForUnresolved,
     ELFAddressToSection,
     ELFUnloadModule, {0, 0, 0, 0, 0}},
    /* LD_COFFOBJECT */
    {COFFLoadModule,
     COFFResolveSymbols,
     COFFCheckForUnresolved,
     COFFAddressToSection,
     COFFUnloadModule, {0, 0, 0, 0, 0}},
    /* LD_XCOFFOBJECT */
    {COFFLoadModule,
     COFFResolveSymbols,
     COFFCheckForUnresolved,
     COFFAddressToSection,
     COFFUnloadModule, {0, 0, 0, 0, 0}},
    /* LD_AOUTOBJECT */
    {AOUTLoadModule,
     AOUTResolveSymbols,
     AOUTCheckForUnresolved,
     AOUTAddressToSection,
     AOUTUnloadModule, {0, 0, 0, 0, 0}},
    /* LD_AOUTDLOBJECT */
     . . .

ELFLoadModule()

ELFLoadModule() mainly does the following:

It obtains the ELF header from the module binary by calling _LoaderFileToMem() as:

    elffile->header =
	    (Elf_Ehdr *) _LoaderFileToMem(elffd, 0, sizeof(Elf_Ehdr),
					  "header");

It then gets the section table from the header by calling again _LoaderFileToMem() as:

    elffile->sections =
	    (Elf_Shdr *) _LoaderFileToMem(elffd, header->e_shoff,
					  elffile->secsize, "sections");

For a description of the ELF sections see elf man page.

Next it gets the header string table:

    elffile->shstraddr =
	    _LoaderFileToMem(elffd, SecOffset(header->e_shstrndx),
			     SecSize(header->e_shstrndx), ".shstrtab");

ELFCollectSections() is called then foe a first pass (follows a second after the relocations, which completes the section loading), which encapsulates a _LoaderFileToMem() to load some sections of the module.

ELF_GetSymbols() is called next to return the module symbols to pLookup, which is a pointer to LOOKUP. LOOKUP is defined in sym.h as:

typedef struct {
    char *symName;
    funcptr offset;
} LOOKUP;

ELF_GetSymbols() fills therefore a lookup[] array item for each module symbol. lookup[] an array of LOOKUP has a size equal to the module's symbol number plus one. The last one is the null terminating item.

/*
 * add symbols
 */
    *ppLookup = pLookup = ELF_GetSymbols(elffile, &secttable);

After the specific LoadModule, e.g. ELFLoadModule() returns, LoaderOpen() calls LoaderAddSymbols() to add the symbols obtained from ELFLoadModule() to its pLookup array:

	LoaderAddSymbols(new_handle, tmp->module, pLookup);

On return from LoaderOpen() the LoadModule() checks if the special data object <modulename>ModuleData is present. Therefore the following instructions are executed:

    p = xalloc(strlen(name) + strlen("ModuleData") + 1);

    strcpy(p, name);
    strcat(p, "ModuleData");
    initdata = LoaderSymbol(p);

LoaderSymbol() calls the specific funcs[] member ResolveSymbols() routine, which for the example we follow is ELFResolveSymbols() and then calls LoaderHashFind() to return the address of the symbol that was passed as argument. Therefore on success the address of struct <ModuleName>ModuleData is found. The address is returned to initdata, which is a pointer to a XF86ModuleData struct:

typedef struct {
    XF86ModuleVersionInfo *	vers;
    ModuleSetupProc		setup;
    ModuleTearDownProc		teardown;
} XF86ModuleData;

Next some of the ret fields are filled and if there is a module setup routine it is executed.

	if (setup)
	    ret->SetupProc = setup;
	if (teardown)
	    ret->TearDownProc = teardown;
	ret->path = path;
	ret->VersionInfo = vers;

    if (ret->SetupProc) {
	ret->TearDownData = ret->SetupProc(ret, options, errmaj, errmin);

Case II (New versions!): LoaderOpen() calls dlopen()

In the realm of xorg version 7.0 and latter LoadModule() calls DoLoadModule() and this LoaderOpen(). LoaderOpen() calls DLLoadModule() and this dlopen() as:

    dlfile->dlhandle = dlopen(modrec->name, dlopen_flags);

A description of dlopen() is found in this man page. Also dlopen() is described in the Program Library HOWTO.

dlopen() is part of libdl, which allows manual dynamic linking. Also libdl is part of glibc. For instance for Debian Linux (see this link) we run dpkg-query as:
$ dpkg-query -S /lib/libdl-2.3.2.so
libc6: /lib/libdl-2.3.2.so

The source code of function dlopen() is found in the archives of glibc, by downloading via ftp a glibc archieve from the Index of /gnu/glibc, found at this gnu.org page, and then opening file dl-open.c.

As we read from Dynamic Link library support for the Linux OS:

Function: void *dlopen ( const char filename, int flags )

This function opens the dynamic library specified by filename and returns an abstract handle, which can be used in subsequent calls to dlsym.

A nice code example of how the dl interface is used comes from Shared Library Mini-Tutorial:

int main()
{
void * my_lib_handle;
int (*some_func)();

	my_lib_handle = dlopen("libmylib.so",RTLD_NOW);
	if(my_lib_handle==NULL) {
		/* ERROR HANDLING */
	}
	some_func = (int (*)()) dlsym(my_lib_handle,"some_function");
	if(some_func==NULL) {
		/* ERROR HANDLING */
	}
	printf("Return code is %i\n",(*some_func)());
	return 0;
}

doLoadModule() calls also LoaderSymbol(), which calls DLFindSymbol() and this DLFindSymbolLocal(). The latter is an encapsulation of dlsym(), which finds a symbol within the given shared library handle, passed as the first argument and returns a pointer to it. LoaderSymbol() is called in the following part of code:

    /*
     * now check if the special data object ModuleData is
     * present.
     */
    p = xalloc(strlen(name) + strlen("ModuleData") + 1);
    if (!p) {
	if (errmaj)
	    *errmaj = LDR_NOMEM;
	if (errmin)
	    *errmin = 0;
	goto LoadModule_fail;
    }
    strcpy(p, name);
    strcat(p, "ModuleData");
    initdata = LoaderSymbol(p);

Therefore a pointer to the ModuleData structure, for instance mgaModuleData for the mga video card, is returned (if found). The value is returned to variable initdata, which is a pointer to a XF86ModuleData, which is defined as:

typedef struct {
    XF86ModuleVersionInfo *	vers;
    ModuleSetupProc		setup;
    ModuleTearDownProc		teardown;
} XF86ModuleData;

Next the module's setup routine runs in DoLoadModule() as:

	if (setup)
	    ret->SetupProc = setup;
. . .
    if (ret->SetupProc) {
	ret->TearDownData = ret->SetupProc(ret, options, errmaj, errmin);

We continue here from the last important point of LoadModule() for the Old versions examined in Case I or of doLoadModule() for the New versions, examined in Case II, that is where the SetupProc() is called:

ret->TearDownData = ret->SetupProc(ret, options, errmaj, errmin);

Module processing

The important steps at this stage are the following:

Step 1: the Module gets loaded

This is described in the previous paragraphs.

Step 2: The Module's setup() routine is located and then is called

Loading a module, e.g. a video driver is one part. The other thing of interest at this point is its initialization. Finding the setup routine that does the job is straightforward when reading the source code however it is more complicated in run time when there is a binary executable, e.g. an with ELF format, which provides the addresses of its routines only by processing its header file.

Locating the setup routine requires to anchor first the XF86ModuleData struct in the module that includes it. From The XFree86 Architecture article we read:

 
In order to hook the module into the server, right after opening a module (and 
before the relocation of any symbols) the server will be looking for a data 
entry called <ModuleName>ModuleData. For example, Mark Vojkovich's Matrox module
(mgaModuleData) is used below:

The mga video driver source code, used as example in the previous article can be found by following Xorg's cvs or cgit sites. At mga_driver.c we find MGA_MODULE_DATA, which is resolved to mgaModuleData in mga.h .

static MODULESETUPPROTO(mgaSetup);

static XF86ModuleVersionInfo mgaVersRec =
{
	MGA_DRIVER_NAME,
	MODULEVENDORSTRING,
	MODINFOSTRING1,
	MODINFOSTRING2,
	XORG_VERSION_CURRENT,
	MGA_MAJOR_VERSION, MGA_MINOR_VERSION, MGA_PATCHLEVEL,
	ABI_CLASS_VIDEODRV,			/* This is a video driver */
	ABI_VIDEODRV_VERSION,
	MOD_CLASS_VIDEODRV,
	{0,0,0,0}
};

_X_EXPORT XF86ModuleData MGA_MODULE_DATA = { &mgaVersRec, mgaSetup, NULL };

Again from the The XFree86 Architecture article we read for mgaModuleData:

 
This structure includes versioning information, as well as function pointers 
that the server binary calls to initialize and (in case it is unloaded) 
de-initialize the module. The setup function is very important when you are 
planning to write a driver. This is the function that hooks the new driver into 
the server data structures and, therefore, makes the functionality of the driver
available. Again, the Matrox driver gives a good example. 

The important part (after making sure that this function is only called once) is
to add a structure describing the driver to the list of drivers using 
xf86AddDriver(). The DriverRec structure that is passed to xf86AddDriver() 
contains version and naming information on this driver, plus three function 
pointers: a function which can be used to print out the name of this driver, a 
function which allows this driver to probe for the hardware it supports, and a 
function which lists the option names (in the configuration file) this driver 
recognizes. 

With these functions the server can now announce that it has loaded this module,
parse the corresponding part of the configuration file, and finally ask the 
module to probe for supported hardware. If the module recognizes hardware in the
system that it supports, it then will create the full set of data structures 
that are needed for an XFree86 graphics card driver. 

The setup routine called from LoadModule() for the mga video driver, which we use here as a case study, is therefore found in mga_driver.c as:

static pointer
mgaSetup(pointer module, pointer opts, int *errmaj, int *errmin)
{
    static Bool setupDone = FALSE;

    /* This module should be loaded only once, but check to be sure. */

    if (!setupDone) {
	setupDone = TRUE;
	xf86AddDriver(&MGA_C_NAME, module, 0);

	/*
	 * Modules that this driver always requires may be loaded here
	 * by calling LoadSubModule().
	 */

	/*
	 * Tell the loader about symbols from other modules that this module
	 * might refer to.
	 */
	LoaderRefSymLists(vgahwSymbols, xaaSymbols,
			  xf8_32bppSymbols, ramdacSymbols,
			  ddcSymbols, i2cSymbols, shadowSymbols,
			  fbdevHWSymbols, vbeSymbols,
			  fbSymbols, int10Symbols,
#ifdef XF86DRI
			  drmSymbols, driSymbols,
#endif
#ifdef USEMGAHAL
			  halSymbols,
#endif
			  NULL);

	/*
	 * The return value must be non-NULL on success even though there
	 * is no TearDownProc.
	 */
	return (pointer)1;
    } else {
	if (errmaj) *errmaj = LDR_ONCEONLY;
	return NULL;
    }
}

Step 3: the Module's setup() routine calls xf86AddDriver()

Notice: this step depends on the video driver. Function setup() could be different in many points to other drivers.

xf86AddDriver is called from the Module's setup() routine to add the driver's DriverRec to the global xf86DriverList[]:

void
xf86AddDriver(DriverPtr driver, pointer module, int flags)
{
    /* Don't add null entries */
    if (!driver)
	return;

    if (xf86DriverList == NULL)
	xf86NumDrivers = 0;

    xf86NumDrivers++;
    xf86DriverList = xnfrealloc(xf86DriverList,
				xf86NumDrivers * sizeof(DriverPtr));
    xf86DriverList[xf86NumDrivers - 1] = xnfalloc(sizeof(DriverRec));
    if (flags & HaveDriverFuncs)
	*xf86DriverList[xf86NumDrivers - 1] = *driver;
    else {
	memcpy(xf86DriverList[xf86NumDrivers - 1], driver, sizeof(DriverRec1));
	xf86DriverList[xf86NumDrivers - 1]->driverFunc = NULL;
    }
    xf86DriverList[xf86NumDrivers - 1]->module = module;
    xf86DriverList[xf86NumDrivers - 1]->refCount = 0;
}

DriverPtr is defined in xf86str.h as:

typedef struct _DriverRec {
    int			driverVersion;
    char *		driverName;
    void		(*Identify)(int flags);
    Bool		(*Probe)(struct _DriverRec *drv, int flags);
    const OptionInfoRec * (*AvailableOptions)(int chipid, int bustype);
    pointer		module;
    int			refCount;
    xorgDriverFuncProc  *driverFunc;
} DriverRec, *DriverPtr;

xf86DriverList is defined in xf86Globals.c as:

DriverPtr *xf86DriverList = NULL;

Each time xf86AddDriver() adds a driver to xf86DriverList[] the number of the array's items, xf86NumDrivers, is updated.

Example

For the example we use in this sections, originated by this xorg.conf the DriverRec that xf86AddDriver() adds to xf86DriverList[] has the following format:

 DriverRec for mga driver
driverVersionMGA_VERSION
driverNameMGA_DRIVER_NAME
(*Identify)(int flags)MGAIdentify()
(*Probe)(struct _DriverRec *drv, int flags)MGAProbe()
(*AvailableOptions)(int chipid, int bustype)MGAAvailableOptions
moduleModuleDescPtr to the ModuleDesc of the next table
refCount0
driverFuncNULL

Since this was provided by the MGA_C_NAME DriverRec:

_X_EXPORT DriverRec MGA_C_NAME = {
    MGA_VERSION,
    MGA_DRIVER_NAME,
    MGAIdentify,
    MGAProbe,
    MGAAvailableOptions,
    NULL,
    0
};

Notice that the value of driverFunc is provided by xf86AddDriver().

module is of type ModuleDescPtr. It points for the current example to the following ModuleDesc struct (created by LoadModule()):

name 
filename 
identifier 
SetupProcmgaSetup
TearDownProcNULL
path 
VersionInfo&mgaVersRec
  

As we previously saw mgaVersRec is:

static XF86ModuleVersionInfo mgaVersRec =
{
	MGA_DRIVER_NAME,
	MODULEVENDORSTRING,
	MODINFOSTRING1,
	MODINFOSTRING2,
	XORG_VERSION_CURRENT,
	MGA_MAJOR_VERSION, MGA_MINOR_VERSION, MGA_PATCHLEVEL,
	ABI_CLASS_VIDEODRV,			/* This is a video driver */
	ABI_VIDEODRV_VERSION,
	MOD_CLASS_VIDEODRV,
	{0,0,0,0}
};

Hands-on Projects for the Linux Graphics Subsystem

New book from Christos Karayiannis

Available in Amazon Kindle format at:

amazon.com   amazon.co.uk   amazon.de   amazon.es   amazon.fr   amazon.it