Shared Libraries - DOS

A shared library is compiled code that can be loaded and used later when 
running an executable.

Description
DOS supports shared libraries (dynamic link libraries) with some 
limitations compared to Linux and Windows.  The DOS target uses the dxe3gen 
utility from DJ's GNU Programming Platform (DJGPP) for creating a DXE file 
(shared library).

Supported
   * shared library file names have .DXE file name extension
   * DyLibLoad, DyLibSymbol, and DyLibFree for dynamic loading
   * import libraries can be used (but see below in Limitations)
   * #inclib and Declare for declaring subs and functions when using 
     import libraries.

Limitations
   * no global variables shared between DXE's (extern / common shared, 
     etc)
   * no gfx functions allowed in DXE's (dylibload() will fail)
   * no static libraries allowed in DXE's (dylibload() will fail)
   * full file name with .DXE must be used with DyLibLoad
   * DXE's must only use procedures available in libfb or the library 
     source code itself, otherwise dylibload() will fail (even though 
     compilation of the DXE will have succeeded)
   * no complex Type's or types with Extends.  Types having member 
     procedures can not be exported
   * Export is allowed but also ignored; all public procedures are 
     exported
   * Exported symbols on DOS have the underscore ( '_' ) and is required 
     with DyLibSymbol.
   * Mutli-threaded build of the fb runtime not exported and is likely not 
     to work (more testing needed at the time of this writing).

DXE3GEN
   dxe3gen is a utility which allows you to create files which contain 
   dynamically loadable code (DXE). DXE is used as a synonym for 
   `dynamically loadable executable module'.  See dxe3gen.

   dxe3gen expects that DXE_LD_LIBRARY_PATH to be set.  If the environment 
   variable is not already set, fbc will set the variable to fbc's library 
   path before invoking dxe3gen.

   In fbc standalone set-up, custom linker script dxe.ld is expected to be 
   present in fbc's library path since dxe3gen will invoke 'ld' linker to 
   generate and executable, which in turn is converted to a .DXE file.

Shared Library Using Run-time Dynamic Loading
In this first example, we use d1.bas source for the DXE and m1.bas for the 
test.  This example will also work on Linux and Windows with no changes, 
however, differences are noted in the example sources.

   $ fbc d1.bas, on DOS, will produce d1.dxe dynamic link library and 
   libd1_il.a import library. 
   $ fbc m1.bas, on DOS, will produce m1.exe executable.

Library loading and function pointer loading is handled manually by the 
user at run-time.

   '' d1.bas - dynamic link library

   '' tell fbc to build a dynamic link library
   #cmdline "-dll"

   '' on DOS:
   ''   - creates d1.dxe (dynamic link library)
   ''   - creates libd1_il.a (import library)
   '' on Windows:
   ''   - creates d1.dll (dynamic link library)
   ''   - creates libd1.dll.a (import library)
   ''   - either d1.dll or libd1.dll.a must be found to link an
   ''     executable using d1.dll
   ''   - d1.dll must be found to load and run an executable
   ''     using d1.dll
   '' on Linux:
   ''   - creates libd1.so (dynamic link library)
   ''   - libd1.so must be found to load and run an executable
   ''     using libd1.do
   ''   - $ LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./executable

   Sub proc_d1( ByRef fromproc As Const String ) Export
      Print __FILE__ & " : " & __FUNCTION__ & " @ " & _
         Hex(ProcPtr(proc_d1),SizeOf(Any Ptr)*2) & _
         " called from: " & fromproc
   End Sub

   '' m1.bas - dynamically load "D1" library and symbol "PROC_D1" at run time
   ''
   '' library loading and symbol pointers are managed 
   '' manually by the user.

   #define NULL 0

   '' variable to hold a handle to the loaded dynamic link libray
   Dim library As Any Ptr

   '' prototype the function pointer
   Type PROC_D1_PTR As Sub( ByRef from As Const String )

   '' variable to hold a pointer to the sub/function in the dll
   Dim proc_d1 As PROC_D1_PTR

   '' some differences in naming between DOS and other targets,
   '' so assign the names to some constants so the build process
   '' can work on DOS and other targets:
   #ifdef __FB_DOS__
      Const D1_DLL_NAME = "d1.dxe"
      Const PROC_D1_NAME = "_PROC_D1"
   #else
      Const D1_DLL_NAME = "d1"
      Const PROC_D1_NAME = "PROC_D1"
   #endif

   '' try to load in the libray
   library = DyLibLoad( D1_DLL_NAME )

   If( library = NULL ) Then
      Print "unable to load library " + D1_DLL_NAME
      End 1
   End If

   '' get a function pointer to the procedure defined in the DXE
   proc_d1 = DyLibSymbol( library, PROC_D1_NAME )
   If( proc_d1 = NULL ) Then
      Print "unable to load symbol " + PROC_D1_NAME
      End 1
   End If

   '' call the loaded procedure
   proc_d1( __FILE__ & " : " & __FUNCTION__ )

   '' release the library
   DyLibFree( library )

Shared Library Using Import Library
In this example, we use d1.bas source (from above) for the DXE and m2.bas 
below for the test.  This example will also work on Linux and Windows with 
no changes, however, differences are noted in the example sources.

   $ fbc d1.bas (from above), on DOS, will produce d1.dxe dynamic link 
   library and libd1_il.a import library. 
   $ fbc m2.bas, on DOS, will produce m2.exe executable.

Library loading and function exports are handled automatically by the 
executable loader and run time start up code.  The include file d1.bi is 
not strictly needed, however is good practice to have a single place where 
functions are declared so there are no mismatches between usage across 
multiple modules.

   '' d1.bi - include file for d1.bas declarations
   #pragma Once
   Declare Sub proc_d1( ByRef from As Const String )

   '' m2.bas - link to import library

   '' use an import library for d1.dxe/d1.dll/d1.so
   ''
   '' library loading and symbol pointers are managed 
   '' automatically by the operating system and/or
   '' runtime start-up code.

   '' import libraries are named differently on 
   '' DOS compared to other targets.  Or in case of
   '' at least win/linux not actually needed.  On DOS 
   '' #inclib will cause linker to look for libd1_il.a 
   '' when linking this executable.  The import library includes
   '' some start-up code to export functions from the dynamic 
   '' link library when it is loaded at runtime.
   #ifdef __FB_DOS__
      #inclib "d1_il"
   #else
      #inclib "d1"
   #endif

   '' include declarations for the library from a header 
   #include Once "d1.bi"

   '' call the function in the DLL
   proc_d1( __FILE__ & " : " & __FUNCTION__ )

   #ifdef __FB_DOS__

   '' add a module constructor to initialize the exports from
   '' rtlib at run time.  Should be the first call made.
   '' Careful: module constructor order is not guaranteed so
   '' this is likely the only constructor that can be present
   '' in the entire application, aside from the implicit
   '' constructors in the DXE file that initialize it's exports.
   ''
   Private Sub __fb_init_libfb_dxe Constructor
      DyLibLoad( "" )
   End Sub

   #endif

Version
   * some support since fbc 1.06.0 with improved support in fbc 1.10.0

See also
   * Shared Libraries
   * #inclib
   * #include
   * Compiler Option: -dll
   * Compiler Option: -dylib

