DLX Dynamic Loading and EXecution V2.91 Copyright (c) 1997-1998, Nanosoft, Inc. There is absolutely NO WARRANTY OF ANY KIND on this code. Any loss of data, sleep, memory, or sanity is NOT MY PROBLEM! This code is provided free, with only a few restrictions: 1 - IF YOU USE IT, YOU MUST CREDIT ME!!!!!!! (see below). 2 - If you change the code, you must send me a copy of all modifications. 3 - If you use DLX for commercial use, you must send me a free licenced copy of your finished product. If you do so, you may consider DLX to be registered for your use with that project. 4 - If you want to port DLX to another system, CONTACT ME OR YOUR PORT WILL BE BLACKLISTED. All this means is that you must take the following (between cut lines) and put it in a visible place in your documentation, and preferrably on program load. I reiterate: VISIBLE. ---------------- DLX Module V2.91 provided by Nanosoft, Inc. ------------ Your cooperation is appreciated. Enjoy DLX. ======================================================================= Now for the technical details: ======================================================================= Compilation. ------------ Unlike the older DLX libraries, V2.X comes complete with a GNU Makefile. Just go into your DLX directory and type: MAKE It really is as simple as that! Files. ------ DLX.LD - The linker description, needed to make a DLX. DLX.H - The main header file, used internally and also included from DLX's. DLXGEN.CPP - The DLX Generator. I'm not completely sure of this utility, as it is not all my code (it is actually just DXEGEN reworked and rewritten in C++) but it works if you don't try to push it too far. DLXLOAD.CPP - The library source for the DLX Loader module. DLXLOAD.H - Function prototypes for the DLX Loader. TEMPLATE.CPP - An example layout for a DLX file. DLXLIST.CPP - A utility to list DLX file symbols. TLOADER.CPP - An example layout for a DLX Main Loader. Usage. ------ To generate a DLX file requires two simple steps. First, compile your program into a .o file and then run DLXGEN on it. For instance: gcc -c foo.cpp dlxgen foo.dlx foo.o This generates a DLX called "foo.dlx" from "foo.cpp". Note that DLX was designed with C++ in mind, so the C support may be a little shaky. Limitations. ------------ With the new V2.X DLX, most of the old limits have been removed. External variables are now supported. LIBEXVAR has been removed, as it is no longer necessary. LIBEXPORT will now export both functions and variables. Layout of a DLX. ---------------- A sample DLX is provided as TEMPLATE.CPP. This file shows all the basic parts of a DLX. Here is what everything does: DLX_FN - This tells the compiler that this function is exportable. DLX_EF - End the exportable function. LibMain - The standard library routine. Called as LibMain(0) upon load of the DLX, as LibMain(1) on unload, as LibMain(2) when the instance counter increases, and as LibMain(3) when the instance counter decreases. The LIBCONSTRUCT and LIBDESTRUCT macros instruct the compiler to allow C++ constructors and destructors. DLXUSE_BEGIN - This is the start of the library header data, and all header data follows. DLXUSE_END - This is the end of the header data. LIBLOADS_BEGIN - Begin the list of library loads. LIBLOADS_END - End the list of library loads. LIBLOAD(file) - Add file to the list of library loads. When the library is loaded, it will load all libraries in the LIBLOADS section just prior to its final load pass, right before symbols are resolved. This allows this library to use functions from other libraries, while making sure that the target library exists. Do not include the name of the current library, or it will crash. LIBEXPORT_BEGIN - The beginning of the export list. LIBEXPORT_END - The end of the export list. LIBEXPORT(s) - Export the symbol s. This allows other libraries to use that symbol. It can be used for both functions and variables. LIBALIAS(name,s) - Export the symbol s, and rename it to name. Other libraries can now use s, but must call it as name. LIBENTRY(ep) - Declare the function ep as an entry-point. This does NOT allow it to be used as an exported function. See the description on entry-points below for a full explanation. LIBWEAK(s) - Export the symbol s, as the DLX equivalent of a weak symbol. This symbol will always be overridden if there is another definition of it as a LIBENTRY. Note that this does not use attribute((weak)), and in fact, you should avoid the use of attribute((weak)) with all of the LIB* functions. LIBVERSION_BEGIN - Beginning of the version table. LIBVERSION_END - End of the version table. LIBVERSION( # ) - Use the version info number in #. See below for a full description of version tables. Out of all of these components, only some are required. The non- essential ones are the version information, all the LIBEXPORT/EXVAR/ENTRY's (but not the _BEGIN and _END, those are required), and the LIBLOAD's (again, the _BEGIN and _END are important). Any of this list may be left out without worry. Using the DLXLOAD library. -------------------------- Once you have written your DLX, the next step is to write your main loader program. This is the program that gets compiled as an EXE and loads the DLX files. An example is given as TLOADER.CPP. First, the standard DLXUSE_BEGIN ... DLXUSE_END block is used, but unlike in a DLX, when used in the EXE it must be exported explicitly, by writing: DLXImport(_LIBEXPORTTABLE); This takes care of all of the things that are normally handled by DLXGEN for DLX files. Now all functions in your block are exported. Use this mostly for library functions in your EXE (things like memcpy, printf, and even __builtin_delete MUST be exported in order to be used). The DLXLoad( filename ) is used to load a DLX called filename. It returns the DLX handle, which can be used for finding entry-points and for unloading the DLX. The DLXUnload( handle ) unloads a DLX loaded by DLXLoad. Note that an outdated DLXUnload( filename ) is also provided for compatibility with V1.X. It should be avoided, if possible, but if all else fails it still exists. If a DLX wishes to locate it's own handle, it should use the LIBMYHANDLE macro. It contains the exact data, as a hdlx_t. The DLXGetEntry( handle, name ) function returns a named entry- point from the DLX. It is returned as a void*, and must be cast as the appropriate function type. If it does not exist, it returns NULL. Since 2.9, eight overridables are now provided. The first, DLXOpenFile, accepts a filename and returns a user-defined handle as a void*. The second, DLXCloseFile, accepts the user-defined handle as a void*, and closes that file. The third, DLXReadFile, accepts a pointer to a buffer, a length to read, and a user-defined handle. This simply closes the file. For DLXOpenFile, if the file does not exist, NULL should be returned. This overridable also allows the use of library 'aliasing', where a library may have a common name (like "(*CORE)"), which is referenced to a real filename by DLXOpenFile. This way, the name does not change, even though the location does. **UPDATE** - Due to popular demand, the filename that is passed to DLXOpenFile is LOWER CASE, in keeping with GNU standard. The fourth overridable is DLXGetID. It accepts the name of a library, and returns the DLX handle if that library is in memory, or NULL if it is not. This should only return non-NULL if the library really exists. The fifth overridable, DLXError, accepts an error code as a long and an error specifier, as a char*. The possible error codes are: 0-File Error 1-DLX Not In Memory 2-Unresolved External 3-Error Unloading Named Symbols 4-Invalid DLX 5-Illegal Operating System 6-Load Error The sixth overridable, DLXMalloc, works exactly like malloc, except that it should never return NULL. The seventh overridable, DLXFree, works exactly like free. The eighth overridable, DLXRealloc, is just like realloc. These last three are provided to form the basis of user-defined memory allocation. Quirks and Notes. ----------------- DLX has a few important quirks that must be understood in order to use DLX. Firstly, the LIBLOADS sections require some explanation. In the version 1.X, any DLX could resolve symbols from any other DLX. In V2.X, however, a DLX can only resolve symbols from DLX's listed in the LIBLOADS section. Of course, GLOBAL symbols (those exported using DLXImport), can still be resolved from anywhere. Also, when using entry-points, the entry-point functions are NOT exported. They will show up in the entry-table ONLY, and will not resolve to a symbol. If you wish to both export it and use it, you must use LIBEXPORT and LIBENTRY both. Version Data. ------------- The version table in the DLX file holds version info. The first field is the manufacturer ID, the second is the product ID, the third is the version data. These numbers are actually ASCII character strings of up to 8 characters in length. Make sure to change these when you make a new DLX, as these numbers have a lot to do with the DLX uniquification. *** NEW *** For those of you who do not speak fluent HEX, a program to automagically generate these numbers is included. Simply execute VERGEN at your command line and type your required string, and out it comes, complete with comment and all! Entry Points. ------------- An entry point is a function that is local to a DLX, which can be called from another DLX or from the main routine, but is NOT linked to it. For example, you could make several different DLX's, all with a function called main. Then, you could make a program that would load one of these, call the main, and wait for more user input. It could then load several of these consecutively, and execute the main of all of them. Note that there is no conflict between entry points of the same name between different DLX's. Unlike normal symbols, which DO conflict, entry-points are LOCAL. Advanced Features. ------------------ The DLX library now supports block-returning. This means that the memory-block holding all DLX code can be accessed, for a variety of purposes, including multitasking and code-locking. The beginning of the block may be found using DLXGetMemoryBlock and the length may be found with DLXGetMemoryBlockLength. All code and static/global variables of the DLX target are stored in this contiguous block. Also note that in 2.9, a DLX will unload automatically at program end. The order of unloading is not guaranteed, but DLX's which have been loaded using DLXLoad will always unload before their child libraries. History. -------- V1.0 - First version, lots of bugs. V1.1 - Second version, corrected a bug that caused GPF's if you used more than one DLX, corrected a bug in default error handling, corrected some miscelaneous bugs. V1.2 - Error-trapping has been improved. V2.0 - External variables supported, complete rewrite of DLXError, improved manuals, major bug-fixes, version support, target system support, entry point support, symbol cross-reference support. V2.1 - Major bug-fix. Support added for bidirectional linking (parent & child DLX's linking to each other). Corrected a bug in the support of parent-child DLX loading due to DLXLoad not being reentrant. V2.11- Minor fix. Makefile fixed, and header files slightly changed. Added LIBALIAS and removed LIBEXVAR. V2.7 - Added support for LIBWEAK. Fixed a memory leak. V2.9 - Major rewrite. Instantiation reworked to allow much improved bidirectional linking, block-aquirer added, filename caps changed, weak pointers stored differently, DLXLoad and DLXUnload redesigned to be more crash-free, added five new overridables, auto-unloader added. V2.91- Bug-Fix. In 2.9, DLXLoad had been designed crash-free, but the error-trapping was disabled, causing a GPF every time an invalid DLX was loaded. Fixed now. VERGEN added. About Contacting Me. -------------------- The contact rules have been revised in V2.X. If you use DLX, drop me a line and tell me what you think. The documentation is still a little simplified (but as any of the people using V1.X will tell you, the older docs were much worse), so if you have trouble, contact me. Any reason at all is good enough. Luke Bishop (lbishop@calvin.stemnet.nf.ca) CEO, Head Programmer, Nanosoft, Inc.