///////////////////////////////////////////////////////////////////////////////
/                                                                             /
/               DPMILD32 - load PE executables as DPMI apps                   /
/                                                                             /
///////////////////////////////////////////////////////////////////////////////

0.   Contents

     1.   Introduction
     2.   How the Loader gets invoked
     3.   Requirements
     4.   Loader API
     5.   Win32 API Emulation
     6.   PX executables
     7.   Memory Model
     8.   Environment variable DPMILDR
     9.   Debugging Support
     10.  Returncodes
     11.  Error Messages
     12.  History
     13.  Copyright


1.   Introduction

     DPMILD32 can load PE-executables as DPMI clients. It supports both
     dlls and exes, loaded statically or dynamically.
     The newest version may be downloaded from http://www.japheth.de.

2.   How the Loader gets invoked

     DPMILD32.EXE is a MZ-executable, so you can start it by simply
     typing its name in the command line. A parameter is required, which
     is the name of the program to load and execute.
     DPMILD32 will search for that program (.EXE extension may be omitted)
     in the current directory and then in all directories of the PATH
     environment variable (thats the same method as how the loader
     searches for referenced dlls).
     The loader may be called automatically with the help of a small stub
     (DPMIST32.EXE) which has to be appended to your DPMI application
     linked as PE executable. This stub replaces the stub supplied by
     the linker ("this app cannot be run in DOS mode") and is 512 bytes 
     in size. It will search the loader DPMILD32.EXE in the current directory
     and in the directories of the PATH variable. 

     After DPMILD32 has been started, it first checks if the DPMI API is 
     available. If this is not the case, it will try to silently start 
     HDPMI32.EXE, which will be found only if it is located in the same
     directory as DPMILD32.EXE.
     

3.   Requirements

     DPMILD32 supports Win32 PE executables and dlls (there is also
     build in support for NE executables, but this is really old stuff).
     Only PE binaries marked as console applications will be loaded.

     Since the DPMI V0.9 API doesn't offer to allocate memory with a given
     base address, the PE executable to load should include relocation
     information so DPMILD32 can load it at any address. This may require
     to set linker option /FIXED:NO.
      Some DPMI hosts may implement function 0504h (allocate committed/
     uncommitted memory), which may also allow to specify a base address.
     DPMILD32 will use this function if supported, which may allow to
     load and execute Win32 console applications with no relocation infos.
     

4.   Loader API
     
     DPMILD32 itself offers a simple API for loading exes and dlls.

        - Int 21h, AX=4B00h (LoadModule/LoadLibrary): Loads a PE/NE module
          dynamically. Returns the module handle in EAX. DS:EDX must
          point to the name of the module to load (and, for applications,
          ES:EBX must point to a execute parameter block). Applications will
          execute synchronously, like in standard DOS, and your application
          will regain control when the loaded application has finished.
          For dlls, the module handle is returned in EAX, which for PE
          modules is simply the address the image has been loaded to.
          For NE modules, the module handle is a selector containing
          the NE module header and HIWORD(eax) will be zero.

        - Int 21h, AX=4B80h (FreeLibrary): Free a PE/NE module.
          The module handle must be in EDX. On return, EAX=0 will indicate
          a failure.

        - Int 21h, AX=4B81h (GetProcAddress): Get the address of an export
          in a PE dll. In EBX there must be the module handle of the dll,
          EDX must point to the name of the function you are interested in.
          The address of the function is returned in EAX. EAX=0 will indicate
          a failure.

        - Int 21h, AX=4B82h (GetModuleHandle): Get the handle of a PE module.
          EDX must point to the name of the module the handle is requested
          from. The handle will be returned in EAX. EAX=0 indicates a failure.

        - Int 21h, AX=4B83h: Get next module handle. Current module handle
          has to be in EDX (or 0 for the first module). Returns next module
          handle in EAX, module count in ECX. In EDX the DPMI memory handle
          for this image is returned.

        - Int 21h, AX=4B84h (CallProc32W): Call 32-bit flat procedure from a
          16-bit dll. In EDX the flat address of the proc to call is expected.

        - Int 21h, AX=4B85h (GetProcAddress16): Get the address of a procedure
          in a 16-bit module. BX holds the module handle, DS:EDX points 
          to name of procedure. If CL=1, EDX is interpreted as a number.
          Address is returned in DX:AX.

        - Int 21h, AX=4B86h (GetModuleFileName): Get a pointer to a 
          module's full path and file name. EDX holds module handle.
          The pointer will be returned in EAX. Works for 16bit dlls as well.

        - Int 21h, AX=4B87h (CallProc16): call a 16-bit proc from a 32-bit
          module. EDX holds value for CS:IP, CX holds number of WORD
          parameters to copy to the 16-bit stack and EBX holds pointer to
          these parameters.

        - Int 21h, AX=4B88h (GetModuleHandle16): Get the handle of a
          16-bit NE module. DS:(E)DX must point to the name of the module the
          handle is requested from, and CL should be 00. If CL=1, EDX is
          interpreted as a selector. The handle will be returned in AX.
          EAX=0 indicates a failure. In DX the module handle of kernel is
          returned.

        - Int 21h, AX=4B91h: enable/disable loader. BL contains new
          state (1=enable, 0=disable). This feature may be useful for
          DPMI applications, which want to start a true Win32 console
          application and so first have to prevent the loader from trying
          to load it as DPMI client.

        - Int 21h, AX=4B92h: Set the start of the module list. The new
          start address must be in EDX (linear address). The old start
          address is returned in EAX. Calling this service with edx=0
          makes DPMILD32 load fresh copies of already loaded dlls at
          next program load.

        - Int 21h, AX=4B93h (SetErrorMode): Set error mode flag
          SEM_NOOPENFILEERRORBOX (in EDX). As default the loader displays
          error messages if it cannot load a module for some reason.
          Setting this flag will hide those messages.

        - Int 21h, AX=4B94h: Set new value of variable DPMILDR.
           CX = mask for the bits to change
           DX = new values for these bits
           AX returns old value of the bits.

     These services are available in protected mode only.


5.   Win32 API Emulation

     During the load process some imported dlls will be "replaced"
     by DPMI compatible versions. These are:

     KERNEL32.DLL ->  DKRNL32.DLL
     ADVAPI32.DLL ->  DADVAPI.DLL
     USER32.DLL   ->  DUSER32.DLL

     This feature allows dual-mode applications. Such apps run as normal Win32
     apps in Win32 environments and will run as DPMI clients in non-Win32
     environments. Fore more details about this view DKRNL32.TXT.
     Since DKRNL32.DLL supplies emulation code for CreateProcess, LoadLibrary,
     FreeLibrary and GetProcAddress, there's no need to use the loaders
     Int 21h API described above if DKRNL32.DLL has been loaded.

     Included in this package are modules OLE32.DLL and OLEAUT32.DLL.
     Do NOT copy these dlls into your windows system directory!!!
     These files are required for MS VC++ compilers, which import some
     functions from these dlls (for "#import <typelib>" command). Calling
     these functions will fail, though, so the "#import" command will not
     work when compiling under DOS.

     
6.   PX executables

     If a PE executable uses non-Win32 compatible features (like software
     interrupts 0x21/0x31 or in/out instructions), it should be ensured
     that is is NOT loaded as Win32 app. For this the loader supports besides
     "PE" files "PX" files, which are exactly what you suppose they are.
     To convert a PE executable to PX format, just use tool PATCHPE.EXE
     after linking the PE executable. A small catch is that modules
     modified this way won't be recognized by PE tools anymore.
     

7.   Memory Model

     As in Win32, applications loaded by DPMILD32 execute in a flat, zero
     based environment with a 4 GB 32-bit code selector in CS and a 4 GB
     32-bit data selector in DS,ES and SS. FS points to a memory area which
     will be used by the WIN32 emulation dlls as "thread information block".
     GS is zero and unused.

      Processes in a Win32 environment execute in their own address spaces.
     This by default is not true for DPMI PE applications loaded with
     DPMILD32. If a PE application starts another PE application, both
     applications reside in the same address space. And, what is more
     important, if they reference the same dll this dll is loaded only once
     in memory. The dlls entry routine is called for each process at startup
     with the event "process attaching" and at process termination with event
     "process detaching", like in Win32. But the dll has only one data segment
     for both processes and this is a major difference to Win32 and is
     similiar to Win32s. This limitation may be overcome by several ways.
     First, if you have access to the source code of the app to execute,
     you can make DPMILD32 to behave like Win32 in calling Int 21h,
     AX=4B92h with EDX=0 just before executing another program. Restore
     module list after program has terminated with another call to Int 21h,
     AX=4B92h with EDX containing the value previously returned.
     Second, you may set environment variable DPMILDR=8, which will result
     in a new instance of the loader to be launched.

     DPMILD32 always allocates the stack of an application as committed memory,
     since there exists no "Guard Page" mechanism in DPMI. Thats why you
     should not specify too large amounts of stack space. The same is true
     for the heap, which is not allocated by the loader, but by the WIN32
     emulation dll DKRNL32.DLL. The heap is able to grow dynamically, so
     you won't loose anything by declaring its initial size not too large.


8.   Environment variable DPMILDR

     By setting the environment variable DPMILDR you can control some aspects
     of the loader's behaviour. The variable is a number whose individual bits
     are interpreted as follows:

     - bit 0 (DPMILDR=1): forces a breakpoint at program entry point. If no
       debugger is present, this switch has no effect.

     - bit 1 (DPMILDR=2): prevents the loader from moving itself into extended
       memory. It remains in conventional DOS memory after switching to
       protected mode. 

     - bit 2 (DPMILDR=4): disables support for 16-Bit NE executables.

     - bit 3 (DPMILDR=8): disables loader and tries to disable DPMI server
       if current app launches another PE application. This will result
       in new instances of loader and server to be initialized, so the
       launched app will have its own address space. Setting this option is
       required if the launched app has no relocation infos and address
       space is occupied by the launching app already. Whether this option
       works as expected depends on the DPMI server. With HDPMI this option
       works fine.

     - bit 4 (DPMILDR=16): don't protect read-only sections. Without this
       option set DPMILDR will try to protect all read-only sections. This
       will only succeed if the DPMI host supports DPMI function 0507h.
       There exist some apps written with WDOSX dos extender which write
       to sections marked as readonly, thus causing a GPF with DPMILD32
       if running on a DPMI 1.0 host. To avoid this use this switch.

     - bit 5 (DPMILDR=32): don't use any DPMI 1.0 function. This option
       is automatically set for Windows NT/2k/XP because of this really
       buggy DPMI support (coming from DOSX.EXE) included in these systems.               

     - bit 6 (DPMILDR=64): ignore SEM_NOOPENFILEERRORBOX flag. With this
       setting the loader will display errors during module loads in any
       case.

     - bit 7 (DPMILDR=128): ignore unresolved imports. With this
       setting the loader will continue to load and execute a binary even
       if an unresolved import has been detected. If such an import is 
       called, however, the task will be silently terminated.
       
     So to set bit 0, 4 and 6 enter "SET DPMILDR=81" (81=1+16+64).


9.   Debugging Support

     DPMILD32 supports Int 41h in protected mode. That is, events such as
     loading a dll, starting a task, loading a segment a.s.o are all
     reported through calls to Int 41h with AX set appropriately. To force
     a breakpoint at app start set environment variable "DPMILDR=1".


10.  Returncodes

     Usually DPMILD32 returns the returncode of the (last) program it
     has executed. But there are several internal error conditions, which
     are reported to the caller through the following return codes:

     rc   comment                  possible reasons
     -----------------------------------------------------------------
     FB   loader init error        cannot switch CPU in protected mode
                                   DOS translation services not available
                                   memory shrink error (in real mode)
                                   no selectors available
                                   no extended memory available
                                   no conventional memory available
     FC   client init error        no filename supplied
                                   file not found
                                   no selectors available
                                   no extended memory available
                                   no conventional memory available
     FD   error in exception 0Bh   no more memory
          (NE files only)          error in NE exe file
     FF   fatalexit                application has called FatalExit or
          (NE files only)          FatalAppExit

     DPMILD32 always displays an error text to STDERR in these cases.


11.  Error Messages

     "relocs stripped, cannot load": relocation information is stripped
                      from PE binary. It can only be loaded at prefered
                      load address, which may be used already or DPMI
                      host doesn't support function 0x504.
     "out of memory": allocating memory for PE binary failed. Please note
                      that DPMILD32 cannot commit stack memory dynamically
                      by setting a GUARD page, it has to be fully allocated
                      at load time.
     "cannot create psp": most likely there is no more conventional DOS
                      memory available.
     "invalid PE format": there is a severe error in the PE binary. Try
                      to relink it.
     "cannot resolve imports": not all imports could be resolved. DPMILD32
                      will display the missing imports in detail.
     "dll init failed": dll entry code was called and returned with EAX != 1


12.  History

     The loader was created mainly in 1993 to execute protected mode
     NE executables as 16-bit DPMI clients. This version was able to
     run on 80286 processors as well. Some time later a version for
     32-bit DPMI clients was added, still supporting NE executables
     only and thus unable to provide a true flat memory model. This
     changed in 1995, when the 32-bit version was extended to support
     PE file format. Another major extension was the win32 emulation dll
     DKRNL32.DLL, which allowed to create dual-mode (or bimodal) 
     applications. This happened mainly in 1995 and 1996.

     In 2003 some support for DPMI V1.0 features were added to the DPMI
     server HDPMI. The most remarkable extension was support for function
     0x0504, which allows a client to allocate memory at a specified address.
     This feature allowed the PE loader to load and execute commercial
     (console) programs which don't have relocation infos. So now it
     was possible to run apps like MASM V6.15 in native DOS.

     Regretably I never thought of making this code public and therefore
     never noted the changes made. So this history is pretty short and 
     includes the changes since version 2.5.1 only.

 14.06.2005: version 2.8.9
 14.06.2005: workaround for DPMIONE implemented
 11.06.2005: display correct error if memory reallocation failed
 22.05.2005: version 2.8.8
 21.05.2005: bugfix: version 2.8.7 introduced a bug so a module was
             searched in PATH even if a directory was included in path!
 20.05.2005: version 2.8.7
 19.05.2005: bugfix: loading a dll with a relative path didn't work
 16.05.2005: bugfix: the loader assumed that word at PSP:[7Eh] was
             zero-initialized. For OpenDOS (DR-DOS) this is not true and is
             the reason why the loader may have caused a crash by launching 
             a real-mode app. 
 16.05.2005: 64kB are added to the reserved stack as it is done in win9x
             systems. Wlink by default defines a 64kB reserved stack region
             only, and, on the other hand, the OW stack checking code
             requires a stack of at least 76 kB on win9x systems!
 16.05.2005: stack bottom now set in THREAD_INFORMATION_BLOCK.
             Used by VirtualQuery of DKRNL32.DLL (OW stack check)
 09.01.2005: version 2.8.7 preliminary
 09.01.2005: stack for PE applications is now allocated as a separate
             memory block with unspecified address. Thus it will run
             much better on DOSEMU.
 03.12.2004: version 2.8.6
 03.12.2004: loader moving in extended memory now supported for dosemu.
             termination code simplified.
 01.12.2004: switched to Digital Mars C++ linker to link DPMILDxx.EXE
 29.11.2004: bugfix: on int 21h, ah=4Ch don't modify AL
 31.10.2004: version 2.8.5
 31.10.2004: error messages modified:
             "memory allocation error #2" -> "out of memory"
             "init failed" -> "dll init failed"
 08.10.2004: version 2.8.4
 07.10.2004: always ignore relocation directory entry if relocations are
             stripped (ldw.exe)
 06.10.2004: version 2.8.3
 03.10.2004: bugfix: GetModuleHandle32 didn't find modules if 
             extension ".dll" wasn't specified
 11.09.2004: version 2.8.2
 11.09.2004: bugfix: don't launch HDPMIxx with command line of DPMILDxx
 04.09.2004: loader automatically increases file handles if file open
             fails with error 4
 21.08.2004: version 2.8.1
 26.08.2004: new API int 21h, AX=4B94 (set DPMILDR value)
 26.08.2004: ESI now holds module handle when a PE module starts
 25.08.2004: when receiving an int 41h, AX=F003 (set break), CX:EBX
             may be modified, so it is no longer necessary to set a break
 21.08.2004: version 2.8
 15.08.2004: DPMILDR=128 switch added (ignore unresolved import errors).
 14.08.2004: bugfix: use current PSP to copy environment selector from
             when creating a child PSP. This is for FreeDOS mainly.
 27.07.2004: version 2.7.9
 15.07.2004: DPMILD32, int 21h, ax=4B00h now compatible with DOSEMU 
 02.07.2004: Load dlls at their base address if relocs are stripped
 14.06.2004: version 2.7.8
 12.06.2004: bugfix: setting DTA (12.05.2004) destroyed HIWORD(ebx) in 
             DPMILD32 (EBX is pointer to EXEC parameter block)
 09.06.2004: version 2.7.7 (released in DEBXXF)
 06.06.2004: bugfix: dpmi error display may have caused an exception
 05.06.2004: disable 16bit flag now only valid for apps (and DPMILD32)
 29.05.2004: SetErrorMode(0) called for PE apps just before app starts
 28.05.2004: free 16-bit stack mem only if 16-bit stack selector is valid
 28.05.2004: set NE flag 4000h if libentry has been called for dlls
 27.05.2004: Loader now always sets parent in PSP as selector, since
             this is required not only by NT platforms, but by win31 as well
 26.05.2004: version 2.7.6
 26.05.2004: DPMILD32: NE LibEntry now called with 16-bit stack
 25.05.2004: vector int 31h now saved at startup and used in dpmicall.
             this helps for terminating code when client has left interrupts
             in a bad state
 25.05.2004: bugfix: DPMILD32: before calling LoadlibIntern clear HIWORD(esi)
 25.05.2004: DPMILD32: NE Wep now called with 16-bit stack
 12.05.2004: dta saved and restored for each task
 12.05.2004: bugfix: WritePrivateProfileString works now!?
 11.05.2004: bugfix: user loader psp selector as parent for NT/2K/XP again
             (bugfix from 03.03.2004)
 10.05.2004: DPMILD16.EXE and DPMILD32.EXE now patched as DPMI executable
 01.05.2004: functions ax=4b91h and ax=4b93h for 16 bit loader as well
             4b91h will totally disable/enable loader now
 30.04.2004: use switch DPMILDR=8 for NE files as well (TLINK.EXE!)
 28.04.2004: version 2.7.5
 27.04.2004: if pointer to raw data is zero, dont try to load from file
 26.04.2004: use raw data size to determine if anything to load from
             the file (previous just checked uninitialized data flag)
 25.04.2004: win16api.asm cleaned, OutputDebugString now for 16bit only
 24.04.2004: bugfix: dont check for 0 as dpmi memory handle. this may be valid
 24.04.2004: dont free psp with int 31h, ax=102h if dos kill function worked
 22.04.2004: version 2.7.4 no changes in DPMILDXX, but possibly in DKRNL32
 20.04.2004: version 2.7.3
 18.04.2004: freeing 32-bit segment notifications added
 17.04.2004: DPMILDR=64 switch added (ignore SEM_NOOPENFILEERRORBOX).
             Also added new API ax=4B93h (SetErrorMode)
 10.04.2004: version 2.7.2
 09.04.2004: release memory of discardable sections
 09.04.2004: to clear a section use phys size if virt size is smaller
 08.04.2004: function int 21h, ax=4b83h now returns dpmi handle in edx
 30.03.2004: version 2.7.1 no changes in DPMILDXX, but possibly in DKRNL32
 14.03.2004: version 2.7.0
 11.03.2004: no more special loader version for DOSEMU needed
 08.03.2004: notification int 41h, ax=164h (loading 32bit dll)
 03.03.2004: set parent PSP as segment, not selector
 28.02.2004: loader now DOSEMU compatible (special version)
 02.02.2004: version 2.6.6
 02.02.2004: in FreeModule32 check value of popped fs first (may be invalid)
 31.01.2004: copy env ptr to child psp if it is NULL (FreeDOS)
 24.01.2004: version 2.6.5
 23.01.2004: another bugfix (the last?) in command line preparation 
 23.01.2004: call SetCmdLine for PE apps as well
 19.01.2004: version 2.6.4 no changes in DPMILDXX, but possibly in DKRNL32
 01.01.2004: version 2.6.3
 23.12.2003: write-protect read/only sections (optionally, see DPMILDR=16)
 21.12.2003: heap no longer allocated. is done by dkrnl32 now
 20.12.2003: version 2.6.2
 19.12.2003: 32bit segment load notifications added (int 41h, AX=150)
 19.12.2003: bug fixed in command line parser if DPMILDR was executed
             directly (not as overlay from stub)            
 18.12.2003: loading continues after first import hasnt been found
             so all missing imports will be reported.
 17.12.2003: some changes for CallProc16 (switch to 16-bit stack)
 14.12.2003: Dlls now called with stack and PSP of application
             TIB now located behind MZ (each 40h bytes in size)
 13.12.2003: FS points to a full TIB now, TLS slots now located on stack
 11.12.2003: version 2.5.3
 10.12.2003: import resolves now works with borland PEs (missing ILT)
 09.12.2003: dont try to load PE GUI apps
 07.12.2003: version 2.5.2
 07.12.2003: new switch DPMILDR=8 (disable loader and server)
 30.11.2003: version 2.5.1


13.  Copyright

     DPMILD32.EXE is part of HX DOS extender. All parts of this extender
     are freeware.


 Japheth (http://www.japheth.de)

