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.

--------<cut here>--------
DLX Module V2.91 provided by Nanosoft, Inc.
-------<stop cutting>-----

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.

