#define SCCSID  "@(#)ldrmte.c   6.63 92/03/20"

/***    OS/2 Module Loader Routines
 *
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1991
 *      Copyright (c) Microsoft Corporation 1987, 1991
 *
 *      All Rights Reserved
 *
 *      The module loader routines handle the loading of 16-bit and 32-bit
 *
 *      modules
 *
 *      For each type of module a Module Table Entry (MTE) is allocated in
 *      system memory.
 *
 *      All of the modules loaded in the system are kept in a zero
 *      terminated linked list.  The link is made using a linear address
 *      to each module.
 *
 *      The handle pointer to a module table entry is kept in the
 *      process's PTDA  for the program module the process is executing.
 *
 *      Modification History
 *     Date       Defect #   Person       Comments
 *     ____________________________________________________________________
 *
 *     5/05/95    118730    S. Cottrill   Removed code added for defect 97262.
 *     9/15/95    135552  System should not trap or IPE when attemping to load
 *                        a DLL from a network dirve that has been disconnected.
 *    11/30/95    144009    S. Cottrill   Added check for null BeginLIBPATH and
 *                                        EndLIBPATH.
 *    08/01/96    161596    B. Muschett   Modified lielist to support versions
 *                                        other than 20 and to suport major/minor
 *                                        versions.
 */

#define INCL_ERROR_H            /* for bseerr.h */
#define INCL_TYPES
#include <basemac.h>
#include <basedef.h>
#include <osconst.h>
#include <ostypes.h>

#define FNO_NUM FNO_LDRMTE_C
#include <fnomem.h>
#include <osmac.h>
#include <mi.h>
#include <bseerr.h>
#include <dossymc.h>
#include <sigdef.h>
#include <except.h>
#include <pgexport.h>
#include <vmexport.h>
#include <selxport.h>
#include <tkexport.h>
#include <infoseg.h>
#include <curdir.h>
#include <dirent.h>
#include <find.h>
#include <screen.h>
#include <sem.h>
#include <tkptda.h>
#define FOR_EXEHDR 1
#include <newexe.h>
#include <exe386.h>
#include <kmode.h>                      /* Must be before ldrvars.h */
#include <ldrxport.h>
#include <ldrvars.h>
#include <vmowner.h>
#include <seldesc.h>
#include <sft.h>
#include <mvdmexp.h>
#include <kutil.h>
#include <string.h>
#include <dynxport.h>
#include <harderr.h>
#include <dbgex.h>
#ifdef SECURITY
  #include <secure.h>
  #include <secure2.h>
#endif
#include <exepe.h>


// The 2 characters 'OZ'. The constant EMAGIC is defined as MZ in
// NEWEXE.H/EXE386.H. All executable code, whether DOS or OS/2, has
// the signature 'MZ' as the first 2 bytes of the file ... except,
// that is, for some WLO 1.0 DLLs, which have the signature 'OZ'.
// The 'O' instead of the 'M" signifies an overlay module. It does
// seem that we are capable of running these modules - probably
// bacause the overlay stuff is only used by the DOS stub, not by OS/2.
// OMAGIC is only needed in this source file, so define here and not
// in a header file.
#define OMAGIC 0x5A4F   // 'OZ'


int ldrCheckForWLO(ldrlv_t *lv, struct e32_exe *pe32);

void ldrGetModParams(ldrmte_t *pmte, ldrrei_t *pei);

int ldrFindModule(PCHAR pachModname, USHORT cchModname, USHORT class, ldrmte_t **ppmte);

int ldrOpenNewExe(PCHAR pachModname, USHORT cchModname, ldrlv_t *plv, PUSHORT pfl);

int ldrOpenPath(PCHAR pachModname, USHORT cchModname, ldrlv_t *plv, ULONG *pflmte);

int ldrCreateMte(struct e32_exe *pe32, ldrlv_t *plv);

int ldrGetMte( uchar_t *pachModname, ushort_t cchModname, uchar_t chLdrtype, uchar_t class, ldrmte_t **ppmte );

void ldrExpandSegment(ldrmte_t *pmte, UCHAR type);

int ldrMTEValidatePtrs(ldrsmte_t *psmte, ULONG limit, ULONG constant);

int ldrIsAttached(ldrmte_t *pmte);

int ldrChkLoadType(ULONG lflags, ldrlv_t *plv);

void ldrExpandHeader(struct e32_exe *pe32);

int ldrCopyName(PCHAR pachsrc,   USHORT cchsrc, PCHAR *ppachdst, PUSHORT pcchdst, int fFault);

void ldrPromoteMTE(ldrmte_t *pmte);

int ldrCheckGlobal(PCHAR pachname, USHORT cchname, ldrmte_t **ppmte);

int ldrCheckSpecific(ldrmte_t **ppmte, ldrlv_t *plv);

int ldrCheckInternalName(ldrmte_t *pmte);

int ldrMungeFlags(struct e32_exe *pe32);

int ldrLoadImports( ldrmte_t *pmte );

int ldrProcessImports( ldrmte_t *pmte );

extern unsigned long ldrHashQATName( char * pName );

void ldrRecordMTE( ldrmte_t *pmte );

PCHAR LdrGetModIndx(PCHAR pchname, PULONG plen);


extern UCHAR fTKUsesR2;      /* Set if module has IOPL segment */

extern UCHAR IsMiniIFS;          /* TRUE if IFS */

extern UCHAR DDInitTime;         /* TRUE if at DDInitTime */

extern UCHAR *LDRLibPath;

extern UCHAR *LDRLieList;


#ifdef LDRSTRICT
extern USHORT TaskNumber;
#endif

extern UCHAR ldrMiniFileIOFlag;

void ldrFreeMTEFixups( ldrmte_t *pmte, int flag);  // PERF - defect 98488

/***LD  Handle and Pointer to Doscall1.dll MTE */
// Moved to sdata.asm for System Anchor Block - f122427
extern VMHOB hMteDoscall1;
extern ldrmte_t *pmteDoscall1;

// Set if invoker is w_get_module ... must be in the ldrSem to touch
uchar_t ldrfromLdrRun = 0;

// Cache for the return from the last DosQAppType.
//
// On exit from LDRQAppType, store a hashed name, the time stamp, and
// the flags we are returning. Then, on the next call, check that we are
// within 2 seconds and that the hashed name matches. If so, return the
// flags, otherwise clear the cache.
extern struct
{
    unsigned long QATTimeStamp;
    unsigned long QATHashName;
    unsigned long QATFlags;
} ldrQATCache;


// Pointer to buffer for TransPath - note that you MUST have the ldr semaphore
// to access this buffer. It is not valid outside of the ldrSem.
extern UCHAR *ldrpFileNameBuf;



// Kernel infoseg data, to get access to the current time.
extern struct InfoSegGDT SISData;



#pragma pack(1)

// Special 0.9 WLO .DLL names to watch out for. Note that DOSCALLS and
// PMWIN are not WLO names, but are OS/2 names ... some 0.9 WLO apps
// seem to do an equivalent of a DosLoadModule of USER_B etc, so do not
// have them in their names table. But they (eg DynaComm) do have
// DOSCALLS, PMWIN and PMSHAPI in their names table.

struct WLOTblEnt
{
    unsigned char namlen;
    char         *WLOdll;
}   ldrWLOTbl[] = {
                      { 8, "DOSCALLS"},
                      { 8, "KERNEL_B"},
                      { 5, "PMWIN"   },
                      { 5, "GDI_B"   },
                      { 6, "USER_B"  }
                  };

#define NUMWLOTBLENT 5



// Names of DLLs that are considered as system to speed up loading of
// a new task.
//
// WARNING - the ordering of this table defines the init ordering of
//           system DLLs. DOSCALL1 is the first DLL to init. This MUST
//           be true or the npx emulation and segment packing die ....

SysTblEnt_t  ldrSysTbl[] = {
                              { "PMWP",     NULL, 4 },
                              { "SOM",      NULL, 3 },
                              { "PMVIOP",   NULL, 6 },
                              { "PMCTLS",   NULL, 6 },
                              { "PMDRAG",   NULL, 6 },
                              { "PMSPL",    NULL, 5 },
 //Defect 80218 Need private version of kernel to use old DLL's
#ifdef NODLLMERGE
                              { "PMWIN",    NULL, 5 },
                              { "PMGRE",    NULL, 5 },
                              { "PMSHAPI",  NULL, 7 },
                              { "OS2CHAR",  NULL, 7 },
#else
                              { "PMMERGE",  NULL, 7 },  // Added for merge
#endif
                              { "DOSCALL1", NULL, 8 },
                              { "PMATM",    NULL, 5 },  // just to swapfile
                              { "IBMVGA32", NULL, 8 },  // just to swapfile
                              { "IBMDEV32", NULL, 8 },  // just to swapfile
                              { "DISPLAY",  NULL, 7 },  // just to swapfile
                              { "PMMLE",    NULL, 5 },  // just to swapfile
                              { "DSPRES",   NULL, 6 },  // just to swapfile
                              { "IBMXGA32", NULL, 8 },  // just to swapfile
                              { "IBMS332",  NULL, 7 },  // just to swapfile
                              { "PMGPI",    NULL, 5 }   // just to swapfile
                           };

#define NUMSYSTBLENT_SWAP  (NUMSYSTBLENT+9)

// ldrmtebundle_s A package of MTE's (either saved or remembered)
//                used by ldrProcessImports to initialize DLL's
//                in the rcorrect order


// Before changing these numbers, profile the system to see
// how many we are using....  Do not randomly change them.
#define MAXREMEMBEREDMTES 46
#define MAXSAVEDMTES 16       //MUST BE A MULTIPLE OF 2!!!

struct  ldrmtebundle_s
{
        struct ldrmtebundle_s *lrm_prev;
        struct ldrmtebundle_s *lrm_next;
        ldrmte_t              *lrm_mte[MAXREMEMBEREDMTES];

};

typedef struct  ldrmtebundle_s      ldrmtebundle_t;

struct  ldrmtebundle2_s
{
        struct ldrmtebundle2_s *lrm2_prev;
        struct ldrmtebundle2_s *lrm2_next;
        ldrmte_t               *lrm2_mte[MAXSAVEDMTES];

};

typedef struct  ldrmtebundle2_s      ldrmtebundle2_t;


ldrmtebundle_t    BaseRememberedMTEs;
ldrmtebundle2_t   BaseSavedMTEs;

#pragma pack()




/*
 *  Beginning of swappable code
 */

#pragma code_seg("SWAP32_15","CODE")




/***EP  LDRLoadExe - load a new .EXE file format program.
 *
 *      This routine is called by the w_execpgm function to
 *      load a program module and it's referenced library
 *      modules.
 *
 *      This procedure performs the following steps:
 *
 *      - Compute module name string length and validate it.
 *      - Load the program module and referenced library modules.
 *      - Copy startup information into the exec_info structure.
 *
 *      ENTRY   pachModname             - pointer to ASCII module name
 *              pexec_info              - pointer to return buffer
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 *                                        pexec_info structure set
 */

int LDRLoadExe( PUCHAR    pachModname,  // pointer to ASCII module name
                ldrrei_t *pexec_info )  // pointer to return buffer
{
    USHORT             cchModname;     // module name length
    register int       rc;
    ldrmte_t          *pmte = 0L;      // Pointer to loaded mte
    register ldrmte_t *psysmte = 0L;   // Pointer to  mte of system DLL

    if ( rc = LDRGetSem() )
        return(rc);

    // Initialize to not use ring 2.
    fTKUsesR2 = 0;

    // Compute module name string length and validate it.
    if (rc = TKFuBuffLenz( SSToDS(&cchModname), pachModname, MAXPATHLEN, 0 ) )
    {
        goto LoadNewExe_exit;
    }

    // If we are loading an EXE file, then we need to make sure that we
    // have attached DOSCALL1.DLL to the EXE.  This is because there are
    // routines within DOSCALL1 (such as Dos32R3ExceptionDispatcher or
    // Dos32R3ExitAddr) that are not APIs but which may be inadvertently
    // invoked by the EXE (such as when an exception occurs or a thread
    // does a ret instead of a DosExit).  These routines must be available
    // to the EXE, since we will call them under the EXE's context, or a
    // fatal trap will occur.
    //
    // Note - pmte initialized to 0 above.
    //
    if ( rc = ldrGetMte( DOSCALL1,
                         (ushort_t)SIZEDOSCALL1STR,
                         EXT_LIBRARY,
                         CLASS_GLOBAL,
                         SSToDS(&pmte) ) )
    {
        goto LoadNewExe_exit;
    }

    pmte = 0;
    rc = ldrGetModule( pachModname,
                       ( ((ulong_t)cchModname)<<16 ) | (EXT_PROGRAM<<8) | CLASS_PROGRAM,
                       SSToDS(&pmte) );

    // Force system dll init as a special case
    for ( cchModname = 0; cchModname < NUMSYSTBLENT; cchModname++)
    {
         psysmte = ldrSysTbl[cchModname].pMTE;
        // Only compare names if pointer exists and length of names match
        if ( psysmte && (psysmte->mte_flags2 & MTELOADORATTACH) )
        {

#ifdef LDRSTRICT
            if ( ldrDebugFlags & LDR_PRINT_IMPORTS )
               {
                printf( "lpi, Recording init hMTE=%04x, flags1=%08x, name=%s\n",
                        psysmte->mte_handle,
                        psysmte->mte_flags1,
                        psysmte->mte_swapmte->smte_path );
               }
            ldrAssert( !( TKLibiRecordMTE( (psysmte)->mte_handle,
                                           1 ) ) );
#else
            TKLibiRecordMTE( psysmte->mte_handle,
                             1 );
#endif

        }
#ifdef LDRSTRICT
        else
        {
           if ( ldrDebugFlags & LDR_PRINT_IMPORTS )
              {
               printf( "lpi, Skipping init hMTE=%04x, flags1=%08x, name=%s\n",
                       psysmte->mte_handle,
                       psysmte->mte_flags1,
                       psysmte->mte_swapmte->smte_path );
              }
        }
#endif
    }

    ldrSetLoaded();

    if (rc)
    {
        ldrDoGCollect = 1;
        pTCBCur->TCBCheckedSig = 0;
    }
    else
    {
        // get program module startup parameters
        ldrGetModParams( pmte, pexec_info );
        TKContext()->ptda_pszproc = (PCHAR) pmte->mte_swapmte->smte_path;

    }

LoadNewExe_exit:

    LDRClearSem();
    return( rc );

} // end of ldrLoadExe



#pragma code_seg()
#pragma code_seg("SWAP32_13","CODE")

/***LP  ldrSetLoaded
 *
 *      WARNING - this code does a ldrClearGraph without calling it. If changing
 *                this code, you also need to change ldrClearGraph.
 */

void ldrSetLoaded()
{
    register ldrmte_t *pmte;

    // Blast the flags into the correct state for every MTE in the system
    for ( pmte = mte_h; pmte != NULL; pmte = pmte->mte_link )
    {
        pmte->mte_flags1 &= ~( USED | INGRAPH | MTENEWMOD );
        pmte->mte_flags1 |= MTEPROCESSED;

        pmte->mte_flags2 &= ~( MTEPRELOADED | MTEGETMTEDONE | MTELOADORATTACH |
                              MTECIRCLEREF | MTESYSPROCESSED );
    }
}




/***LP  ldrGetModule - Get module handle, load if required
 */

int ldrGetModule( uchar_t           *pachModname, // pointer to ASCII module name
                  register ulong_t   ParmsStruc,  // packed parameters
                  ldrmte_t         **ppmte )      // place to return pointer to mte
{
    register ldrmte_t *pmte;
    register int       rc;

    #ifdef SECURITY
    // @SEC see if security is interested in this call
    if (fSec && (rc = Sec32GetModule(pachModname)))                      //@SEC
    {                                                                    //@SEC
       return( rc);                                                      //@SEC
    }                                                                    //@SEC
    #endif

    // Setup swapping flag -
    // MVDM or FSD loads must have swapping turned off.
    swapping = PGSwapEnabled;
    if (    ((uchar_t)(ParmsStruc>>8)) == EXT_VDD
         || ((uchar_t)(ParmsStruc>>8)) == EXT_FSD )
    {
        swapping = FALSE;
    }

    // Get the mte for the requested module - maybe force the load.
    // ldrGetMte needs to be changed to take the packed ParmsStruc.
    *ppmte = 0;
    if (rc = ldrGetMte( pachModname,
                        (ushort_t)(ParmsStruc>>16),
                        (uchar_t)(ParmsStruc>>8),
                        (uchar_t)ParmsStruc,
                        ppmte ) )
    {
        return( rc );
    }
    pmte = *ppmte;

    // If already loaded for this process, but not during this particular
    // load, then no need to continue as init has already been run.
    if (!( pmte->mte_flags2 & MTELOADORATTACH ))
    {
        return( rc );
    }

    // Scan linked list of mtes and load or attach to objects.
    // Since we know that all implicitly referenced modules are in the
    // global list, we only need look at the current MTE and the entire
    // global list.
    while (pmte)
    {
        // If not already processed and is a new load/attach
        if (    !(pmte->mte_flags1 & (MTEPROCESSED|DOSMOD|MVDMMOD|FSHMOD) )
             && (pmte->mte_flags2 & MTELOADORATTACH) )
        {
            if ( rc = ldrLoadImports( pmte ) )
                {
//118730                //Only free from DosLoadModule, not DosExecPgm/DosStartSession
//118730                if (ldrfromLdrRun)
//118730                   {
//118730                   ldrUnlinkMTE(pmte);         // D97262
//118730                   ldrFreeMTE(pmte);           // D97262
//118730                   }
                return( rc );
                }

            pmte->mte_flags1 |= MTEPROCESSED;

            pmte = global_h;  // Start at head of list again. Note that all
                              // imports must be class global.
        }
        else
        {
           pmte = pmte->mte_link;
        }

        // If all class global done, then we are done with loading.
        if (    (pmte)
             && ( (pmte->mte_flags1 & CLASS_MASK) != CLASS_GLOBAL ) )
        {
            pmte = NULL;
        }
    }

    // Walk the list of mte's a second time, processing newly added
    // modules.  This processing was deferred because forwarders
    // require all directly and indirectly referenced modules to
    // be loaded before processing. (No, not really true ! - Paul.)
    pmte = mte_h;
    while (pmte)
    {
        if (    ( pmte->mte_flags2 & MTELOADORATTACH )
             && !(pmte->mte_flags2 & MTEPRELOADED) )
        {
            if ( ldrIsLX(pmte) )
            {
                // Preload, and do some other stuff to complete the load
                if ( rc = ldrProcessObjects( pmte ) )
                {
                    return( rc );
                }
            }
            else
            {
                // Preload LDT, for 16-bit code :
                //   1. if it is coming off removeable media,
                //   2. if it is something like uhpfs.dll which funny
                // The decision on what has to be preloaded is made in ldrAllocSegment.
                // Note that this used to be invoked by tasking - moved to here due to
                // problems with the sel --> mte lookups.
                if ( rc = ldrPreloadLDT( pmte ) )
                {
                    return( rc );
                }
            }
            pmte->mte_flags2 |= MTEPRELOADED;
        }
        pmte = pmte->mte_link;
    }

    //****New comments start *** */
    // Walk the module tree, working out which DLLs need initialisation,
    // and which order to do them in. The code above has loaded all of
    // the DLLs we need, so the tree walk routine merely has to walk
    // the tree of resident MTEs - there will be no file open or file
    // reads, so we stay out of the file system, so we will be ok on
    // stack space. Note that TKLibiRecordMte may page fault, but that
    // will be ok - the problem scenario would be the recursive code doing
    // a disk read, and taking a page fault in the file system. Trap 8.
    // Also note that the allocation of memory may cause a write, but
    // we are protected by the loader semaphore here, and the VMAllocKHB
    // shouldn't need to come into the loader.
    //
    //****Old comments start **** (no longer recursive)
    // Walk the module tree, working out which DLLs need initialisation,
    // and which order to do them in. Note that this is a recursive routine,
    // and will therefore use a lot of stack space. The code above has
    // loaded all of the DLLs we need, so the tree walk routine merely has
    // to walk the tree of resident MTEs - there will be no file open or
    // file reads, so we stay out of the file system, so we will be ok on
    // stack space. Note that TKLibiRecordMte may page fault, but that
    // will be ok - the problem scenario would be the recursive code doing
    // a disk read, and taking a page fault in the file system. Trap 8.
    //****Old comments end **** */
    //
    rc = ldrProcessImports( *ppmte );
    return( rc );

} // end of ldrGetModule



#pragma code_seg()
#pragma code_seg("SWAP32_14","CODE")

/***LP  ldrLoadImports - Loads the imports from the given module
 */

int ldrLoadImports( ldrmte_t *pmte )
{
    register ldrmte_t  **ppmte;
    register uchar_t    *pach_modname;    // pointer to module name
    register ulong_t     lindex;
    ldrmte_t            *ptemp;
    ulong_t              i;
    int                  rc;

    // For each module in import module name table attach to or load

    // point to modptrs table to place mtes in
    ppmte = (ldrmte_t **)( ((char *)pmte) + sizeof(ldrmte_t) );

    // For 32-bit modules load the referenced module in order for now.
    // But for 16-bit modules they must be loaded in reverse order
    // found in the module reference table.

    pach_modname = (uchar_t *) pmte->mte_swapmte->smte_impmod;  // assume LX

    for ( i = 0; i < pmte->mte_impmodcnt; i++ )
    {
        // Since it is required for 16-bit modules to load the
        // referenced module in reverse order and not for 32-bit,
        // lindex will be the index for the array of mte pointers
        // for both 16-bit and 32-bit modules

        lindex = i;
        if ( ldrIsNE(pmte) )
        {
            lindex = pmte->mte_impmodcnt-i-1;

            // point to mod name in table for 16-bit modules,
            // load in reverse order

            pach_modname = (uchar_t *) (pmte->mte_swapmte->smte_impproc +
                       ((ushort_t *) (pmte->mte_swapmte->smte_impmod))[lindex]);
        }

        ptemp = ppmte[lindex];

        if ( pmte->mte_flags1 & VDDMOD )
        {

            // The parent module is a VDD module, so we need to check:
            // 1.) A VDD can only import a VDD which is present in the linked
            //     list of mte's already.
            // 2.) A VDD can only have fixups to another VDD.
            // To verify these do a ldrFindModule on the referenced module if
            // it is present verify that it is a VDD module or the kernel VDD.
            // Otherwise error out.

            ldrUCaseString( (uchar_t *)pach_modname+1,
                            (ulong_t)*( (uchar_t *)pach_modname ) );

            // Warning - ldrFindModule returns NO_ERROR but *pmte=0 for
            //           not found.
            if ( ldrFindModule( (uchar_t *)pach_modname+1,
                                (ushort_t)*((uchar_t *) pach_modname),
                                CLASS_GLOBAL,
                                SSToDS(&ptemp) )
                 || (ptemp == NULL)
                 || !(ptemp->mte_flags1 & (VDDMOD | MVDMMOD)) )
            {
                ldrStop( 643, ptemp );
                return( 643 );               // ERROR_VDD_NOT_FOUND
            }
            ppmte[lindex] = ptemp;
        }

        if (    ( !ptemp )
             || ( !(ptemp->mte_flags2 & MTEGETMTEDONE) ) )
        {
            if ( rc = ldrGetMte( (char *)pach_modname+1,
                                 (ushort_t)*( (char *)pach_modname ),
                                 EXT_LIBRARY,
                                 CLASS_GLOBAL,
                                 SSToDS(&ptemp) ) )
            {
                ldrStop( 1, ptemp );
                return( rc );
            }
            ptemp->mte_flags2 |= MTEGETMTEDONE;
            ppmte[lindex] = ptemp;
        }

        /*
         * If module referenced is DOSCALLS make sure we attach to
         * DOSCALL1.
         * The test for ldrMiniFileIOFlag is used for the loading of
         * base device drivers.  If we are loading a base DD do not
         * load DOSCALL1, this will fail since we are using the mini
         * file system.  In order to allow base DD's to import from
         * DOSCALLS do not attempt to load DOSCALL1.
         */
        if ( ptemp->mte_flags1 & DOSMOD && !ldrMiniFileIOFlag )
        {
            ppmte[lindex] = pmteDoscall1;
        }

        // Assume LX. For 32-bit modules point to next module to load
        pach_modname += ((ulong_t) pach_modname[0]) + 1;

    }

    return( NO_ERROR );

} // end of ldrLoadImports



#pragma code_seg()
#pragma code_seg("HIGH32CODE","CODE")


/***LP  ldrProcessImports - Recursive loop
 *
 * ERRORS - An error will be returned if out of memory.
 *
 *
 * Assume MAXSAVEDMTES will be a power of 2
 */

int ldrProcessImports( ldrmte_t *pmte )       // pointer to MTE
{
 register ldrmte_t  *mypmte;
 register ldrmte_t **ppmte;        /* pointer to list of mtes */
 register ldrmte_t  *nextunmarkedchild;
 ulong_t             i;
 ulong_t             cRememberedMTEs;
 ulong_t             cSavedMTEs;
 ldrmtebundle_t   *CurrRememberedMTEs;
 ldrmtebundle2_t  *CurrSavedMTEs;
 ldrmtebundle_t   *Temp;
 int                 rc;
#ifdef PMDEBUG
 ulong_t             cTotalRememberedMTEs;
 ulong_t             cTotalSavedMTEs;
 ulong_t             cMaxRememberedMTEs;
 ulong_t             cMaxSavedMTEs;
#endif

   mypmte = pmte;                          // p = root;
   pmte->mte_flags2 |= MTECIRCLEREF;       // mark(p)

   CurrRememberedMTEs   = &BaseRememberedMTEs;
   CurrSavedMTEs        = &BaseSavedMTEs;

   memset( CurrRememberedMTEs,
           0,
          (ULONG) sizeof(ldrmtebundle_t));

   memset( CurrSavedMTEs,
           0,
          (ULONG) sizeof(ldrmtebundle2_t));

   cRememberedMTEs = 0;
   cSavedMTEs = 0;
   i = 0;
   rc = 0;
   CurrSavedMTEs->lrm2_mte[cSavedMTEs++] = NULL;  //Save i
   CurrSavedMTEs->lrm2_mte[cSavedMTEs++] = NULL;  //Flag for last pMTE

#ifdef PMDEBUG
   cTotalRememberedMTEs = 0;   // For profiling
   cMaxRememberedMTEs   = 0;   //
   cTotalSavedMTEs      = 2;   //
   cMaxSavedMTEs        = 2;   //
#endif

#ifdef LDRSTRICT
       if ( ldrDebugFlags & (LDR_PRINT_TREE_WALKING | LDR_PRINT_IMPORTS))
          {
           printf( "lpi Processing imports slot=%04x, module=%s\n",
                   TaskNumber,
                   mypmte->mte_swapmte->smte_path );
          }
#endif

   while (mypmte)
      {
#ifdef LDRSTRICT
       if ( ldrDebugFlags & LDR_PRINT_TREE_WALKING )
          {
           printf( "ldr walking tree hMTE=%04x, name=%s\n",
                   mypmte->mte_handle,
                   mypmte->mte_swapmte->smte_path );
          }
#endif

      nextunmarkedchild = NULL;

      ppmte = (ldrmte_t **)( ((char *)mypmte) + sizeof(ldrmte_t) );
      while((i < mypmte->mte_impmodcnt) && !(nextunmarkedchild))
         {
           if (!((ppmte[i])->mte_flags1 & (DOSMOD | DOSLIB |
                        DEVDRVMOD | VDDMOD | FSDMOD | MVDMMOD | FSHMOD)))
             {
             if (!(ppmte[i]->mte_flags2 & MTECIRCLEREF))
                {
                nextunmarkedchild=ppmte[i];
                }
             }
             i++;
         }

      if (nextunmarkedchild)
         {
#ifdef LDRSTRICT
       if ( ldrDebugFlags & LDR_PRINT_TREE_WALKING )
          {
           printf( "ldr walking tree going down\n");
          }
#endif
         if(cSavedMTEs >= MAXSAVEDMTES)
            {
            if (CurrSavedMTEs->lrm2_next)
               {
               CurrSavedMTEs = CurrSavedMTEs->lrm2_next;
               }
            else
               {
               if ( rc = VMAllocKHB( VM_HKH_PUB_RESRW,
                                     (ULONG) sizeof(ldrmtebundle_t),
                                     (VMHOB) LDRMTEPATHOWNER,
                                     NA,
                                     &CurrSavedMTEs->lrm2_next))
                  {
                  CurrSavedMTEs->lrm2_next = NULL;   //defect 80157
                  goto ldrProcessImportsExit;
                  }
                memset( CurrSavedMTEs->lrm2_next,
                        0,
                        (ULONG) sizeof(ldrmtebundle2_t));
               // VMAllocKHB set's old record's next pointer
               // Set new record's prev pointer
               CurrSavedMTEs->lrm2_next->lrm2_prev = CurrSavedMTEs;
               CurrSavedMTEs = CurrSavedMTEs->lrm2_next;
               // Set new record's next pointer
               CurrSavedMTEs->lrm2_next = NULL;
               }
            cSavedMTEs = 0;
            }

         //OK, I know "i" isn't a pointer, but this is a convenient
         //place to save it.
         CurrSavedMTEs->lrm2_mte[cSavedMTEs++] = (ldrmte_t *)i;
         CurrSavedMTEs->lrm2_mte[cSavedMTEs++] = mypmte;
#ifdef PMDEBUG
         // Figure out total saved
         cTotalSavedMTEs +=2;
         if(cTotalSavedMTEs > cMaxSavedMTEs)
            {
            cMaxSavedMTEs = cTotalSavedMTEs;
            }
#endif
         mypmte = nextunmarkedchild;
         i = 0;

         mypmte->mte_flags2 |= MTECIRCLEREF;       // mark(p)
         }
      else
         {
#ifdef LDRSTRICT
         if ( ldrDebugFlags & LDR_PRINT_TREE_WALKING )
            {
             printf( "ldr walking tree going up\n");
            }
#endif

         if ( !(mypmte->mte_flags1 & (DOSMOD|MVDMMOD|FSHMOD)) )
            {
            if(cRememberedMTEs >= MAXREMEMBEREDMTES)
               {
               if ( rc = VMAllocKHB( VM_HKH_PUB_RESRW,
                                     (ULONG) sizeof(ldrmtebundle_t),
                                     (VMHOB) LDRMTEPATHOWNER,
                                     NA,
                                     &CurrRememberedMTEs->lrm_next))
                  {
                  CurrRememberedMTEs->lrm_next = NULL;   //defect 80157
                  goto ldrProcessImportsExit;
                  }
                memset( CurrRememberedMTEs->lrm_next,
                        0,
                        (ULONG) sizeof(ldrmtebundle_t));
               // VMAllocKHB set's old record's next pointer
               // Set new record's prev pointer
               CurrRememberedMTEs->lrm_next->lrm_prev = CurrRememberedMTEs;
               CurrRememberedMTEs = CurrRememberedMTEs->lrm_next;
               // Set new record's next pointer
               CurrRememberedMTEs->lrm_next = NULL;
               cRememberedMTEs = 0;
               }
            CurrRememberedMTEs->lrm_mte[cRememberedMTEs++] = mypmte;
#ifdef PMDEBUG
            // Figure out total remembered
            cTotalRememberedMTEs++;
            if(cTotalRememberedMTEs > cMaxRememberedMTEs)
               {
               cMaxRememberedMTEs = cTotalRememberedMTEs;
               }
#endif
            }

         if(cSavedMTEs == 0)
            {
            CurrSavedMTEs = CurrSavedMTEs->lrm2_prev;
            cSavedMTEs = MAXSAVEDMTES;
            }
         mypmte = CurrSavedMTEs->lrm2_mte[--cSavedMTEs];
         i      = (ULONG)CurrSavedMTEs->lrm2_mte[--cSavedMTEs];
#ifdef PMDEBUG
         // adjust total saved
         cTotalSavedMTEs -=2;
#endif
         }
      }

   while (CurrRememberedMTEs)
   {
      cRememberedMTEs = MAXREMEMBEREDMTES;
      while (cRememberedMTEs > 0)
         {
         cRememberedMTEs--;
         mypmte = CurrRememberedMTEs->lrm_mte[cRememberedMTEs];
         if (mypmte)
            {
            ldrRecordMTE(mypmte);
            mypmte->mte_flags2 &= ~MTECIRCLEREF;
            }
         }
      CurrRememberedMTEs = CurrRememberedMTEs->lrm_prev;

    }

ldrProcessImportsExit:
    //Free memory for remembered MTEs
    CurrRememberedMTEs = BaseRememberedMTEs.lrm_next;

    while(CurrRememberedMTEs)
       {
       Temp = CurrRememberedMTEs->lrm_next;
       VMFreeKHB( VM_HKH_PUB_RESRW, CurrRememberedMTEs );
       CurrRememberedMTEs = Temp;
       }

    //Free memory for saved MTEs
    CurrSavedMTEs = BaseSavedMTEs.lrm2_next;

    while(CurrSavedMTEs)
       {
       Temp = (ldrmtebundle_t *)CurrSavedMTEs->lrm2_next;
       VMFreeKHB( VM_HKH_PUB_RESRW, CurrSavedMTEs );
       CurrSavedMTEs = (ldrmtebundle2_t *)Temp;
       }

   return(rc);

} // end of ldrProcessImports


/***LP  ldrRecordMTE - Call TKLibiRecordMTE if needed
 *
 * ERRORS - can not return an error. An error will come out as a trap or
 *          IPE.
 */

void ldrRecordMTE( ldrmte_t *pmte )               // pointer to MTE
{
    // If already loaded for this process, but not during this particular
    // load, then no need to continue as init has already been run. Or,
    // if a system dll and already processed, then init handled later.
    if (    (!( pmte->mte_flags2 & MTELOADORATTACH ))
         || (pmte->mte_flags2 & MTESYSPROCESSED) )
    {
        return;
    }

#ifdef PMDEBUG
    if (    ( pmte->mte_flags2 & MTELOADORATTACH )
         && !( pmte->mte_flags1 & MTENEWMOD )
         && !( IsMiniIFS ) )                   // Do not attempt to load .sym
                                               // files at mini-FSD time.
    {
        DebugLoadSymMTE( pmte,
                         pmte->mte_handle );
    }
#endif


    pmte->mte_flags1 |= MTENEWMOD;

    // We record this MTE iff:
    // a, Debugging is enabled ( pPTDA->ptda_DbgEnabled ) OR
    // b, There is an init routine to dispatch to.
    // Note, the MTE for a forwarder is never recorded.
    //
    // WARNING - system dlls (ie system flag on in mte_flags2) do not get
    //           recorded here unless this is a call from get_module
    //
    if (
            (pmte->mte_flags2 & MTELOADORATTACH)
         && ( (!(pmte->mte_flags2 & MTESYSTEMDLL)) || (ldrfromLdrRun) )
         && (pmte->mte_flags1 & LIBRARYMOD)
         && ( !(pmte->mte_flags1 & (DEVDRVMOD | VDDMOD | FSDMOD) ) )
         && (    ((pmte->mte_flags1 & (INSTLIBINIT | GINIDONE)) != GINIDONE)
              || ( TKContext() )->ptda_DbgEnabled )
         && (pmte->mte_swapmte->smte_objcnt) )
    {
#ifdef LDRSTRICT
       if ( ldrDebugFlags & LDR_PRINT_IMPORTS )
          {
           printf( "lrm, Recording init hMTE=%04x, flags1=%08x, name=%s\n",
                   pmte->mte_handle,
                   pmte->mte_flags1,
                   pmte->mte_swapmte->smte_path );
          }
#endif
        TKLibiRecordMTE( pmte->mte_handle,
                         (pmte->mte_flags1 & (INSTLIBINIT | GINIDONE)) != GINIDONE);
    }
#ifdef LDRSTRICT
    else
    {
       if ( ldrDebugFlags & LDR_PRINT_IMPORTS )
          {
           printf( "lrm, Skipping init hMTE=%04x, flags1=%08x, name=%s\n",
                   pmte->mte_handle,
                   pmte->mte_flags1,
                   pmte->mte_swapmte->smte_path );
          }
    }
#endif

    if ( !(pmte->mte_flags1 & (DOSMOD|MVDMMOD|FSHMOD)) )
    {
        // Set system processed flag on for system DLLs - BUGBUG, this is
        // dependant on positional knowledge of the flags ...
        pmte->mte_flags2 |= ( (pmte->mte_flags2 & MTESYSTEMDLL) << 8 );
    }

    return;

} // end of ldrRecordMTE

#pragma code_seg()
#pragma code_seg("LOAD_FTUNE1_SWP","CODE")


/***LP  ldrPreloadLDT - Preload LDT NE modules
 *
 *      ENTRY   pmte                    - pointer to MTE to be preloaded
 *
 *      EXIT    none                    - goto load_error on error
 *
 *      COMMENT: Only preloads for 16-bit modules. 32-bit handled by
 *               ldrProcessObject. Only loads LDTs.
 */

int ldrPreloadLDT( register ldrmte_t *pmte )
{
    register ldrsmte_t    *psmte;
    register ldrste_t     *pste;
    ldrlf_t                lf;
    ldrlv_t                lv;
    int                    rc;
    unsigned int           i;

    // Loop thru each segment in the MTE to do any preloads.
    // This needs to be done for some 16-bit code, eg :
    //   1. if it is coming off removeable media,
    //   2. if it is something like uhpfs.dll which funny
    // The decision on what has to be preloaded is made in ldrAllocSegment.
    // Note that this used to be invoked by tasking - moved to here due to
    // problems with the sel --> mte lookups ... segment packing.

    psmte = pmte->mte_swapmte;

    // For each segment
    for ( i = 1, pste = (ldrste_t *)psmte->smte_objtab;
          i <= psmte->smte_objcnt;
          i++, pste++ )
    {

        // if  ldrAllocSegment set the preload bit
        // AND it is not GDT (GDT preload for DDs etc handled elsewhere).
        if (    (pste->ste_flags & STE_PRELOAD)
             && !(pste->ste_flags & STE_GDTSEG) )
        {
            // We are going to call ldrSegment to preload this segment.
            // Initialise the lv and lf structures needed to make the call.

            memset( SSToDS(&lv), 0, (ulong_t)(sizeof(ldrlv_t)) );

            lv.lv_pmte = pmte;
            lv.lv_sfn = pmte->mte_sfn;
            lv.lv_hobmte = pmte->mte_handle;

            lf.lf_pallocbuf = lf.lf_objaddr = SELSelToLa( pste->ste_selector,
                                                          TKContext() );

            // Set the fault address to -1 so that the ldrSegment code
            // knows that it is not responding to a fault.
            lf.lf_faultaddr = -1;

            lf.lf_sallocbuf = pste->ste_selector;
            lf.lf_bno = (i << 4);
            lf.lf_hMTE = pmte->mte_handle;

            // Do the preload
            if ( rc = ldrSegment( pste,
                                  SSToDS(&lf),
                                  SSToDS(&lv) ) )
            {
                load_error( rc, pmte );
                return( rc );
            }

            // Ensure that we only preload shared segments once
            pste->ste_flags &= ~STE_PRELOAD;

        } // end of if preload needed

    } // end of for each segment

    return(NO_ERROR);

} // end of ldrPreloadLDT


#pragma code_seg()
#pragma code_seg("SWAP32_13","CODE")


/***LP  ldrGetMte - Get module handle
 *
 *      Get the module table entry (mte) handle for this module. If
 *      the module's mte is not in the linked list of mte's, open and
 *      read the file EXE header, verify the header is for a segmented
 *      or linear EXE file format, create an mte for the module and initialize
 *      it.  If this is a program module, save the mte handle in the user's
 *      PTDA. If the module's mte is found in the linked list of mte's,
 *      attach to the module if not already attached and allocate non_shared
 *      objects.
 *
 *      ENTRY   pachModname             - pointer to module name
 *              cchModname              - module name length
 *              chLdrtype               - load type being requested
 *                                        (program,library,device)
 *              class                   - program,global or specific
 *              ppmte                   - pointer to a pmte if exist else 0
 *
 *      EXIT    rc                      - return code
 *
 *      NOTE - needs to be changed to take a packed ParmStruc. Sometime ...
 */

int ldrGetMte( PUCHAR     pachModname,  /* pointer to ASCII module name */
               USHORT     cchModname,   /* module name length */
               UCHAR      chLdrtype,    /* type of module to load */
               UCHAR      class,        /* class to which module belongs */
               ldrmte_t **ppmte )       /* pointer to a mte pointer */
{
    ldrlv_t             lv;               /* loader variable */
    register ldrmte_t  *pmte;             /* pointer to mte */
    register ldrsmte_t *psmte;            /* pointer to swappable mte */
    register ldrste_t  *pste;             /* pointer to ste */
    struct e32_exe     *pe32;             /* pointer to link exe format image */
    int                 rc;
    ulong_t             ldrSizePackSeg;   /* running size of packed segs */
    uchar_t             redoPackSeg,      /* redo packed segment flag */
                        redoDLLInit,      /* redo DLL Initialization flag*/
                        mte_already_loaded;

    // Init local variables
    memset( (PCHAR)SSToDS(&lv), 0, sizeof(ldrlv_t) );
    lv.lv_type = chLdrtype;
    lv.lv_sfn = SFN_NULL;
    lv.lv_class = class;

    // setup source global error message txt
    ldrSetupSrcErrTxt( pachModname, cchModname );

    // Do not search for device drivers. Allow multiple loads of a PDD
    // by treating each repeated load as a new load.
    if ((*ppmte == NULL) && (lv.lv_type != EXT_DEVICE))
    {
        // Upper case module name
        ldrUCaseString( pachModname, (ulong_t)cchModname );

        // Search to see if the module is already loaded so that
        // it may be shared. ppmte is set to zero if the module
        // is not found
        if ( rc = ldrFindModule( pachModname,
                                 cchModname,
                                 class,
                                 ppmte) )
        {
            load_error( rc, NULL );
            return( rc );
        }

        // We can not load VDDs twice, so if FindModule found it
        // return an error.
        if (    (chLdrtype == EXT_VDD)
             && (*ppmte != NULL) )
        {
            load_error( ERROR_NOT_SAME_DEVICE, NULL );
            return( ERROR_NOT_SAME_DEVICE );
        }

    }

again:
    pmte = lv.lv_pmte = *ppmte;

    // Module found ?
    if (pmte)
    {
        mte_already_loaded = TRUE;

        pmte->mte_flags2 |= MTEGETMTEDONE;

        if ( rc = ldrChkLoadType( pmte->mte_flags1, SSToDS(&lv) ) )
            return(rc);

        ldrInvTgtErrTxt();

        /*
         *  Don't load DOSCALLS, FSH, VDDMOD, MVDMMOD, or modules
         *  that are attached already.
         */
        if (    ( pmte->mte_flags1 & (DOSMOD | FSHMOD | VDDMOD | MVDMMOD) )
             || ldrIsAttached(pmte) )
        {
            if ( IsMiniIFS && !(pmte->mte_flags1 & FSHMOD) )
            {
                return( ERROR_INVALID_NAME );
            }
            return( NO_ERROR );
        }
        else
        {
            // setup existing SFN to load non-shared objects
            lv.lv_sfn = pmte->mte_sfn;

            // Indicate that shared objects should not have the page tables
            // scanned. We will use the USED bit to indicate this.
            pmte->mte_flags1 |= USED;
        }

        // Flag to say that this mte was either loaded or attached to.
        // Therefore, DLL init, etc are required.
        pmte->mte_flags2 |= MTELOADORATTACH;

        // Check to see if packed segments need to be re-processed or
        // global DLL Initialization has to be re-executed on the mte
        // we have just found. This occurs when a dying process has
        // already free'd all the objects in this mte but has blocked
        // and still contains this mte in it's dld records; therefore,
        // garbage collection cannot delete it.

        psmte = pmte->mte_swapmte;
        if ((chLdrtype == EXT_PROGRAM) || (chLdrtype == EXT_LIBRARY))
        {
           redoPackSeg = (uchar_t)(    VMPackedMem
                                    && ldrIsNE(pmte)
                                    && (psmte->smte_csegpack != 0) );
           redoDLLInit = (uchar_t)(chLdrtype == EXT_LIBRARY);

           if (redoPackSeg || redoDLLInit)
           {
              for ( rc = ldrSizePackSeg = 0, pste = (ldrste_t *)psmte->smte_objtab;
                    (unsigned long)rc < psmte->smte_objcnt;
                    pste++, rc++ )
              {
                 if (pste->ste_seghdl != HOBNULL)
                 {
                    redoPackSeg = redoDLLInit = 0;
                    break;
                 }
                 if ((redoPackSeg) && (pste->ste_flags & STE_PACKED))
                 {
                    pste->ste_flags &= ~STE_PRESENT;
                    ldrSizePackSeg += (ulong_t) pste->ste_minsiz;
                 }
              }

              if (redoPackSeg)
              {
                 /* Reset these values so ldrAllocSegments can
                  * reprocess the packed segments of this mte.
                  */
                 pmte->mte_flags2 &= ~MTEPACKSEGDONE;
                 psmte->smte_ssegpack = ldrSizePackSeg;
              }
              if (redoDLLInit)
              {
                 if ((psmte->smte_startobj != 0) &&
                    (!(pmte->mte_flags1 & INSTLIBINIT)))
                       pmte->mte_flags1 &= ~(GINISETUP | GINIDONE);
              }
           }
        }

    }
    else
    {
        /*
         * We come here after ldrFindModule has failed to find this module
         * in the class specified. We can have the following situation:
         *
         *  a, We are trying to load a CLASS_SPECIFIC module. In this case
         *     we need to check if this module is loaded as a CLASS_GLOBAL
         *     module. If it is we use that MTE. The MTE is maintained as
         *     CLASS_GLOBAL.
         *
         *  b, We are trying to load a CLASS_GLOBAL module. In this case
         *     we need to check if this module is loaded as a CLASS_SPECIFIC
         *     module. If it is we use that MTE. The MTE is then promoted
         *     to CLASS_GLOBAL.
         */
        if (    (class == CLASS_SPECIFIC)
             && ldrCheckGlobal(pachModname, cchModname, ppmte))
        {
            goto again;
        }

        mte_already_loaded = FALSE;

        if ( rc = ldrOpenNewExe( pachModname, cchModname, SSToDS(&lv), NULL ) )
        {
            ldrClose(lv.lv_sfn);
            lv.lv_sfn = SFN_NULL;
            load_error( rc, NULL );
            return(rc);
        }

        if (    (class == CLASS_GLOBAL)
             && ldrCheckSpecific(ppmte, SSToDS(&lv)) )
        {
            ldrClose(lv.lv_sfn);
            lv.lv_sfn = (*ppmte)->mte_sfn;
            goto again;
        }

        pe32 = (struct e32_exe *) pheaderbuf;

        if ( rc = ldrChkLoadType( pe32->e32_mflags, SSToDS(&lv) ) )
            return(rc);

        if ( rc = ldrCreateMte( pe32, SSToDS(&lv) ) )
        {
            return(rc);
        }

        pmte = lv.lv_pmte;
        psmte = pmte->mte_swapmte;
        pmte->mte_flags2 |= ( MTELOADORATTACH | MTEGETMTEDONE );
        ldrInvTgtErrTxt();

        // If this is DD or IFS init time, then hack the PDD or IFS
        // handle into the PTDA so that w_get_version can find the
        // module. SysInit does load(x) then init(x) then load(y)
        // then init(y) etc - so, if we hack in the module handle
        // for x into the PTDA at load time, then if init(x) calls
        // w_get_version, we have the correct parent.
        if (DDInitTime)
        {
            if (    ( pmte->mte_flags1 & DEVDRVMOD )
                 || ( pmte->mte_flags1 & FSDMOD ) )
            {
                pPTDACur->ptda_module = pmte->mte_handle;
            }
        }

        // If this is the DOSCALL1 module the DOSLIB indicator is set.
        // This indicator is used during dynamic linking to ensure that
        // the DOSCALLS module is searched before DOSCALL1.
        if ( strncmp( DOSCALL1,
                      ( (PCHAR)&pmte->mte_modname ),
                      SIZEDOSCALL1STR ) == 0 )
        {
            pmte->mte_flags1 |= DOSLIB;
            pmteDoscall1 = pmte;
            hMteDoscall1 = pmte->mte_handle;
        }

    } // end of else mte not found

    // If this is a VDD
    // Notify VDMM that we are going to assign address for a VDD
    if (pmte->mte_flags1 & VDDMOD)
    {
        VDMMLoadingVDD( pmte->mte_handle );
    }

    // allocate object for this module by calling ldrAllocSegments for 16-bit
    // modules or ldrAllocObjects for 32-bit modules
    if ( ldrIsNE(pmte) )
    {
        rc = ldrAllocSegments(SSToDS(&lv));
    }
    else
    {
        rc = ldrAllocObjects(SSToDS(&lv));
    }
    if (rc)
    {
        return(rc);
    }

    // is this a program exec
    if (lv.lv_type == EXT_PROGRAM)
    {

#ifdef LDRSTRICT
        // if it is a program, we MUST be execing a child process
        ldrAssert( pPTDACur->pPTDAExecChild );
#endif

        // set program module handle in ptda
        pPTDACur->pPTDAExecChild->ptda_module = pmte->mte_handle;
        if (pmte->mte_flags1 & MTELONGNAMES)
        {
            pPTDACur->pPTDAExecChild->ptda_NewFiles = TCBNEWFILES | TCBKNOWEAS;
        }

        // track usage of module for cleanup
        pmte->mte_usecnt++;
    }

    else if (    lv.lv_type == EXT_LIBRARY
              && ! mte_already_loaded
              && pmte->mte_flags1 & NOINTERNFIXUPS )
    {
        // Then, this is the first time a 'BASE'd DLL was loaded and we loaded
        // it at the pre-relocated address the linker was told to use.  This
        // all means that we can get rid of all the internal fixup information
        // that was stored in the swappable heap (!)

        PBYTE      pfrend, pfrnew, pfrold;   // -> fixup info
        ULONG      frlen;                    // fixup record length
        ULONG      cbsave;                   // number of bytes saved
        PULONG     pfpte;                    // -> fixup page table entry
        ULONG      frtab;                    // offset of fixup record table

        psmte = pmte->mte_swapmte;
        pfpte = (PULONG)psmte->smte_fpagetab;
        frtab = psmte->smte_frectab;

        // Compact the fixup record table within the swappable MTE by
        // eliminating all internal fixup records
        pfrold = pfrnew = (PBYTE)(frtab + *pfpte);
        pfrend = (PBYTE)(frtab + *(pfpte+1));
        for ( rc = psmte->smte_mpages; rc > 0; rc-- )
        {
            while (pfrold < pfrend)
            {
                if (ldrIsInternalFixup(pfrold, SSToDS(&frlen)))
                    pfrold += frlen;
                else while (frlen--)
                    *pfrnew++ = *pfrold++;
            }
            pfrend = (PBYTE)(frtab + *(++pfpte + 1));
            *pfpte = (ULONG)(pfrnew - frtab);
        }

        // If anything was saved then shift the remainder of the swappable
        // MTE up in memory and resize the entire swappable MTE
        if ( cbsave = (ULONG)pfrold - (ULONG)pfrnew )
        {
            // Shift the import module names table and the import
            // procedure names table up within the swappable MTE
            // while ( (ULONG)pfrold <= (ULONG)psmte + (ULONG)lv.lv_csmte )
            // ???? BUGBUG also subtrace cbsave first ????
            while ( (ULONG)pfrold < (ULONG)psmte + (ULONG)lv.lv_csmte )
                *pfrnew++ = *pfrold++;
            psmte->smte_impmod -= cbsave;
            psmte->smte_impproc -= cbsave;
            psmte->smte_fixupsize -= cbsave;

            // Resize the swappable MTE
#ifdef LDRSTRICT
            if ( VMReallocKHB( VM_HKH_PUB_SWAPRW,
                               &(pmte->mte_swapmte),
                               lv.lv_csmte - cbsave,
                               VMAC_ALIGNPAGE ) )
            {
                panic("fixups - Realloc");
            }
#else
            VMReallocKHB( VM_HKH_PUB_SWAPRW,
                          &(pmte->mte_swapmte),
                          lv.lv_csmte - cbsave,
                          VMAC_ALIGNPAGE );
#endif
        }
    }

    pmte->mte_flags1 &= ~MTEPROCESSED;  // indicate segment are not loaded

    *ppmte = pmte;                      // return pmte

    return(NO_ERROR);

} // end of ldrGetMte


#pragma code_seg()
#pragma code_seg("SWAP32_13","CODE")

/***LP  ldrFindModule - Check if module is already loaded.
 *
 *      Scans the class-list (CLASS_PROGRAM, CLASS_GLOBAL or CLASS_SPECIFIC)
 *      searching for a matching pathname.  If the request is for a global
 *      dynlink library module, the module name, which is the first entry in
 *      the resident name table, is used for comparison. Otherwise the pathname
 *      is fully expanded and compared with expanded pathname in the mte.
 *
 *      The MTE list is organised like this:
 *
 *     program_h             global_h              specific_h
 *       |                      |                     |
 * mte_h  +------+    +-----+    +-----+    +-----+    +-----+    +-----+
 *  ----> |      |    |     |--->|     |    |     |--->|     |    |     |---+
 *        +------+ .. +-----+    +-----+ .. +-----+    +-----+ .. +-----+   |
 *                   |                     |                     |         ___
 *               program_l             global_l              specific_l     -
 *
 *      mte_h is the head of the linked list. Both program_h and specific_h
 *      could be NULL. If program_h is NULL, then mte_h and global_h point to
 *      the same MTE. global_l can never be NULL. program_l and specific_l
 *      can be.
 *
 *      ENTRY   pachModname             - pointer to module name
 *              cchModname              - module name length
 *              class                   - CLASS_GLOBAL, CLASS_SPECIFIC or
 *                                        CLASS_PROGRAM
 *              ppmte                   - pointer to a pmte to return
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 *                                      - pointer to reqested mte returned in
 *                                        ppmte
 *
 * WARNING - returns NO_ERROR if valid call, but not found - *pmte = 0 for
 *           this case.
 *
 */

int ldrFindModule( register PCHAR   pachModname, /* pointer to ASCII module name */
                   USHORT           cchModname,  /* module name length */
                   USHORT           class,       /* class of module */
                   ldrmte_t       **ppmte )      /* place to return pointer */
{
    register ldrmte_t  *pmte;   /* pointer to module table entry */
    ldrsmte_t          *psmte;  /* pointer to swappable MTE */
    USHORT              fpath;  /* If TRUE then search by path */
    int                 rc;
    PCHAR               pFileName;  //Pointer into pachModName to filename (no path)
    ULONG               cbFileName; //length of filename part (no extension)
    register ldrmte_t  *pmte_end;  /* pointer to MTE to stop on       */


    // Error if null modulename string
    if (cchModname == 0)
        return(ERROR_FILE_NOT_FOUND);

    fpath = (USHORT) (class & SEARCH_BY_PATH);
    class &= ~SEARCH_BY_PATH;

    switch (class)
    {
      case CLASS_ALL:
            pmte = mte_h;
            pmte_end = NULL;     /* Stop after searching all        */
            break;
      case CLASS_GLOBAL:
            pmte = global_h;
            pmte_end = global_l;                         /*         */
            break;
      case CLASS_SPECIFIC:
            pmte = specific_h;
            pmte_end = specific_l;                       /*         */
            break;
      case CLASS_PROGRAM:
            pmte = program_h;
            pmte_end = program_l;                        /*         */
            break;

#ifdef LDRSTRICT
          default:
                panic("ldrFindModule: Invalid class");
#endif

    }

    /* We need to search the mte pointed to by pmte_end, so          */
    /* if pmte_end isn't NULL, set pmte_end to the next mte          */
    if (pmte_end)                                          /*        */
    {                                                      /*        */
         pmte_end = pmte_end->mte_link;                    /*        */
    }                                                      /*        */

    if (    ( (class != CLASS_GLOBAL) || (fpath == SEARCH_BY_PATH) )
         && ( pachModname != ldrpFileNameBuf ) )
    {
        /*
         * If search is for a program, a specific library or a device
         * driver, expand the given pathname to a full pathname and put
         * this in ldrFileNameBuf. Then search for an mte with this pathname.
         */
        if ( rc = ldrTransPath(pachModname) )
            return(rc);

        pachModname = ldrpFileNameBuf;
        cchModname = (USHORT)( strlen(pachModname)+1 );
        ldrUCaseString( pachModname,
                        (ulong_t)cchModname );
    }

    // Defect 80371 4meg perf
    // If we are going to search for a DLL by path, we first want to check
    // the resident name (avoid touching the swappable MTE if possible)
    if ((class == CLASS_SPECIFIC) ||
        ((class == CLASS_GLOBAL) && (fpath == SEARCH_BY_PATH)))
       {
        pFileName = LdrGetModIndx(pachModname, SSToDS(&cbFileName));
        if(cbFileName >8)
          {
          cbFileName = 8;
          }
       }
    else
       {
        /* We may have gotten the name with a .DLL extension.
         * If so zap it. Defect 83898.
         */
        if (strncmp(&pachModname[cchModname - 4], ".DLL", 4) == 0) {
                pachModname[cchModname - 4] = '\000';
                cchModname -= 4;
        }
        pFileName = pachModname;
        cbFileName = 0;
       }

    /*
     * Search the mte list for module name in TempBuf
     */
    /*        while (pmte) */
    while (pmte != pmte_end)        /*        */
    {

        /*
         * If not searching CLASS_ALL, terminate the scan when the
         * class value in the MTE is not what we want.
         */
        if (    (class != CLASS_ALL)
             && (pmte->mte_flags1 & CLASS_MASK) != class )
        {
            pmte = NULL;
            break;
        }

        if (    (class == CLASS_GLOBAL)
             && (fpath != SEARCH_BY_PATH) )
        {
            //Strings match, and either
            //  a. All 8 bytes match
            //  b. Next byte in mte_modname is a NULL
            if ( ldrMatchStringsN( (PCHAR)&pmte->mte_modname,
                                      pachModname,
                                      cchModname )
                && ((cchModname == 8) ||
                    (pmte->mte_modname[cchModname] == 0)))
                break;
        }

        /*
         * If either program or specific module, just compare name.
         * First compare filename portion (no path or extension)
         * to resident portion of MTE name (if possible).
         * Then compare length of strings to fix the problem of
         * the string matching the first n characters of the pathname
         * but what if the pathname has n+1 characters in name.
         */
        else
        {
            // Defect 80371 4meg perf
            //Check swappable name only if we don't have the
            //filename portion, or if the resident name matches
            //the filename portion.
            if ( !cbFileName ||
                (ldrMatchStringsN( (PCHAR)&pmte->mte_modname,
                                      pFileName,
                                      cbFileName )))
               {
               psmte = pmte->mte_swapmte;
               if (psmte->smte_pathlen == cchModname - (ushort_t) 1)
                  {
                   if ( ldrMatchStringsN( (PCHAR)psmte->smte_path,
                                          pachModname,
                                          cchModname ) )
                       {
                       break;
                       }
                  }
               }
        }

        pmte = pmte->mte_link;
    }

    if(pmte == pmte_end)         /* Didn't find it?                 */
    {
       *ppmte = NULL;            /* return NULL                     */
    }
    else
    {
       *ppmte = pmte;            /* otherwise return pMTE           */
    }
    return(NO_ERROR);               /* did not find the module */

} // end of ldrFindModule


#pragma code_seg()
#pragma code_seg("SWAP32_14","CODE")


/***LP  ldrLinkMTE - Link MTE in the list at the appropriate place
 *
 *      MTEs are linked according to the class they belong to. The possible
 *      classes are CLASS_PROGRAM, CLASS_GLOBAL and CLASS_SPECIFIC. The corres.
 *      list heads are program_h, global_h and specific_h. We also have the
 *      tail of CLASS_PROGRAM, CLASS_GLOBAL and CLASS_SPECIFIC lists which are
 *      program_l, global_l and specific_l. These aid us in linking and
 *      unlinking the list and not having to go through the chain every time.
 *
 *      To begin with mte_h and global_h point to the same MTE, program_h,
 *      program_l, specific_h and specific_l are NULL. global_l is also
 *      initialised.
 *      See ldrFindModule for organisation of the MTE List.
 *
 *      The possible cases are:
 *      1. Link a CLASS_PROGRAM MTE.
 *        if (program_h == NULL) {
 *              pmte->mte_link = global_h;
 *              program_l = pmte;
 *        }
 *        else  pmte->mte_link = program_h;
 *        mte_h = program_h = pmte;
 *
 *      2. Link a CLASS_GLOBAL MTE.
 *        pmte->mte_link = global_h;    // global_h can never be NULL
 *        global_h = pmte;
 *        if (program_l == NULL)        // and hence program_h is NULL
 *              mte_h = global_h;
 *        else  program_l->mte_link = global_h;
 *
 *      3. Link a CLASS_SPECIFIC MTE.
 *        if (specific_l == NULL)
 *              specific_l = pmte;
 *        pmte->mte_link = specific_h;
 *        global_l->mte_link = pmte;
 *
 *      ENTRY
 *              pmte    MTE to link in the list
 *
 *      EXIT    NONE
 *              MTE is linked at the right place
 */
void ldrLinkMTE( register ldrmte_t *pmte )
{

    switch (pmte->mte_flags1 & CLASS_MASK)
    {
      case CLASS_PROGRAM:
        if (program_h == NULL)
        {
            pmte->mte_link = global_h;
            program_l = pmte;
        }
        else
            pmte->mte_link = program_h;
        mte_h = program_h = pmte;
        break;

      case CLASS_GLOBAL:
        pmte->mte_link = global_h;  /* global_h can never be NULL */
        global_h = pmte;
        if (program_l == NULL)      /* and hence program_h is NULL */
            mte_h = pmte;
        else
            program_l->mte_link = pmte;
        break;

      case CLASS_SPECIFIC:
        if (specific_l == NULL)
            specific_l = pmte;
        pmte->mte_link = specific_h;
        specific_h = pmte;
        global_l->mte_link = pmte;  /* global_l can never be NULL */
        break;

#ifdef LDRSTRICT
      default:
        panic("ldrLinkMTE: Invalid class");
#endif

    }

} // end of ldrLinkMte


#pragma code_seg()
#pragma code_seg("LDRSWAP_TEXT", "CODE")

/***LP  ldrUnlinkMTE - Unlink MTE from the list
 *
 *      Unlink the given MTE from the list. See ldrFindModule and ldrLinkMTE
 *      for details of how the list is organised.
 *
 *      ENTRY
 *              pmte    MTE to unlink from the list
 *
 *      EXIT    NONE
 *              MTE is unlinked
 */
void ldrUnlinkMTE( register ldrmte_t *pmte )
{
    register ldrmte_t *pmtecur;     /* Current mte */
    register ldrmte_t **ppmtepred;  /* Predecessor mte */
    ldrmte_t          **ppmtehead;  /* Pointer to class head */
    ldrmte_t          **ppmtetail;  /* Pointer to class tail */
    USHORT            class;

    switch (class = (USHORT)(pmte->mte_flags1 & CLASS_MASK))
    {
      case CLASS_PROGRAM:
        pmtecur   = program_h;
        ppmtepred = &mte_h;
        ppmtehead = &program_h;
        ppmtetail = &program_l;
        break;

      case CLASS_GLOBAL:
        pmtecur   = global_h;
        ppmtepred = &program_l->mte_link;
        if (program_l == NULL)
            ppmtepred = &mte_h;
        ppmtehead = &global_h;
        ppmtetail = &global_l;
        break;

      case CLASS_SPECIFIC:
        pmtecur   = specific_h;
        ppmtepred = &global_l->mte_link;
        ppmtehead = &specific_h;
        ppmtetail = &specific_l;
        break;

#ifdef LDRSTRICT
    default:
        panic("ldrUnlinkMTE: Invalid list");
#endif
    }

    while (TRUE)
    {

        ldrAssert( (pmtecur != NULL &&
                   (pmtecur->mte_flags1 & CLASS_MASK) == class) );

        if (pmtecur == pmte)
            break;
        ppmtepred = &pmtecur->mte_link;
        pmtecur = pmtecur->mte_link;
    }

    *ppmtepred = pmte->mte_link;    /* Unlink MTE */

    if (pmte == *ppmtehead)         /* If unlinkee is at the head */
    {
        *ppmtehead = pmte->mte_link;
        /* If class disappears, then both head and tail disappear */
        if ((*ppmtehead == NULL) ||
            (((*ppmtehead)->mte_flags1 & CLASS_MASK) != class))
        {
            *ppmtehead = NULL;
            *ppmtetail = NULL;
        }
    }
    /*
     * Since we have a pointer to the mte_link field of the predecessor
     * we need to get back to the mte pointer by subtracting the offset
     * of the link field of MTE from the pointer to predecessor.
     */
    if (pmte == *ppmtetail)         /* If unlinkee is at the tail */
    {
        *ppmtetail = (ldrmte_t *)((ULONG)ppmtepred -
                                           FIELDOFFSET(ldrmte_t, mte_link));
    }

} // end of ldrUnlinkMte


#pragma code_seg()
#pragma code_seg("SWAP32_15","CODE")


/***LP  ldrCheckGlobal - Check if specified module is loaded as global
 *
 *      Called by ldrGetMte. If a module is being loaded as a specific module
 *      and ldrFindModule has not found this in the specific list, we see if
 *      this is loaded as a global module. For this to happen, the following
 *      has to be satisfied.
 *      a, The file has to have a ".DLL" as the last part of its name.
 *      b, It should be somewhere in the libpath
 *
 *      ENTRY
 *              pachModname     file name string
 *              cchModname      string length
 *              ppmte           pointer to where the MTE pointer, if found,
 *                              is to be returned
 *
 *      EXIT
 *              TRUE            if module found as loaded global
 *              FALSE           module not global.
 */
int ldrCheckGlobal( char                *pachModname,
                    USHORT               cchModname,
                    register ldrmte_t  **ppmte )
{
    PCHAR           pchext;         /* Pointer to the extension */
    PCHAR           pchname;        /* pointer to file name */
    int             cchname;        /* length of file name string */
    int             rc;

    cchname = (USHORT) ldrGetFileName( pachModname,
                                       (PCHAR *)SSToDS(&pchname),
                                       (PCHAR *)SSToDS(&pchext) );
    if ( ldrMatchStringsN( pchext, "DLL", 3 ) )
    {
        if ( (rc = ldrFindModule( pachModname,
                                  (USHORT) cchModname,
                                  CLASS_GLOBAL|SEARCH_BY_PATH,
                                  ppmte ) ) )
        {
            return( FALSE );
        }
        return( *ppmte != NULL );
    }
    return( FALSE );

} // end of ldrCheckGlobal


#pragma code_seg()
#pragma code_seg("SWAP32_17","CODE")


/***LP  ldrCheckSpecific - Check if specified module is loaded as specfic
 *
 *      Called by ldrGetMte. If a module is being loaded as a global module
 *      and ldrFindModule has not found this in the global list, we see if
 *      this is loaded as a specific module. If we find this in the specific
 *      list, then we promte it to the global list.
 *
 *      ENTRY   ppmte           pointer to where the MTE pointer, if found,
 *                              is to be returned.
 *              plv             pointer to loader variables structure
 *
 *      EXIT
 *              TRUE            If module found as loaded global
 *              FALSE           Otherwise.
 */
int ldrCheckSpecific( ldrmte_t **ppmte,
                      ldrlv_t   *plv )
{
    USHORT    cchModname;
    int       rc;

    cchModname = (USHORT) (strlen(ldrpFileNameBuf)+1);

    ldrUCaseString( ldrpFileNameBuf, (ULONG)cchModname );

    if ( rc = ldrFindModule( ldrpFileNameBuf,
                             cchModname,
                             CLASS_SPECIFIC,
                             ppmte ) )
    {
        ldrClose( plv->lv_sfn );
        plv->lv_sfn = SFN_NULL;
        return( FALSE );
    }

    if ( *ppmte == NULL )
    {
        return( FALSE );
    }

    ldrPromoteMTE( *ppmte );

    return( TRUE );

} // end of ldrCheckSpecific


#pragma code_seg()
#pragma code_seg("LDRSWAP_TEXT", "CODE")

/***LP  ldrPromoteMTE - Promote MTE from specific class list to global
 *
 *      Unlink an MTE from the specific class list and link into global
 *      class list.
 *
 *      ENTRY   pmte    MTE to promote
 *
 *      EXIT    NONE
 */
void ldrPromoteMTE( ldrmte_t *pmte )
{

#ifdef LDRSTRICT
    if ( (pmte->mte_flags1 & CLASS_MASK) != CLASS_SPECIFIC )
        panic("ldrPromoteMTE");
#endif

    ldrUnlinkMTE( pmte );
    pmte->mte_flags1 &= ~CLASS_SPECIFIC;
    pmte->mte_flags1 |=  CLASS_GLOBAL;
    ldrLinkMTE( pmte );

} // end of ldrPromoteMTE


#pragma code_seg()
#pragma code_seg("SWAP32_8","CODE")

/***LP  ldrOpenNewExe - Open module pathname and verify it's a new EXE format
 *
 *      The specified file is opened and the old header and
 *      New EXE headers are read and verified.  If New EXE header is
 *      a 16-bit module ("NE"), expand the 16-bit header to a 32-bit
 *      header. pheaderbuf is implicitly used.
 *
 *      This procedure performs the following steps:
 *
 *      - Open the file searching LIBPATH if required.
 *      - Read old EXE header into memory buffer size of 32-bit module header.
 *      - Validate it's signature.
 *      - From old EXE header get pointer to new header.
 *      - Read new header into same buffer using size of 32-bit module header.
 *      - Validate that signature is either "NE" or "LE".
 *      - For "NE" modules call expand_header.
 *      - Verify that the header flags indicate the module is valid.
 *
 *      ENTRY   pachModname             - pointer to module name
 *              cchModname              - module name length
 *              plv                     - pointer to local variables on stack
 *              pfl                     - pointer to a flag, set if OLD EXE
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 */

int ldrOpenNewExe( PCHAR    pachModname, // pointer to ASCII module name
                   USHORT   cchModname,  // module name length
                   ldrlv_t *plv,         // pointer to local variables
                   PUSHORT  pfl )        // Pointer to flag
{
    register struct e32_exe *pe32;     // pointer to linker exe format
    int                      rc;
    ULONG                    flmte=0;  // flags

    if ( rc = ldrOpenPath( pachModname,
                           cchModname,
                           plv,
                           SSToDS(&flmte) ) )
    {
        plv->lv_sfn = SFN_NULL;
        return( rc );
    }

    // point to mte header
    pe32 = (struct e32_exe *) pheaderbuf;

    // Set flag to say that we found the file ok - the QAppType can process
    // further. Ie, check if it is a .COM file.
    if (pfl)
    {
        *pfl = 0x0001;
    }

    // read in old header
    if ( rc = ldrRead( plv->lv_sfn,
                       0,
                       (PCHAR) pe32,
                       NULL,
                       sizeof(struct exe_hdr),
                       0 ) )
    {
        return( rc );
    }

    // Allow a new format header without an old header
    if (    ( ((struct exe_hdr *)pe32)->e_magic == LXMAGIC )
         || ( ((struct exe_hdr *)pe32)->e_magic == NEMAGIC ) )
    {
        // Dummy pointer to new header. Forces a re-read, but that will
        // have to be fixed later, when the 2 reads are condensed to 1.
        plv->lv_new_exe_off = 0;
    }
    else
    {
        // validate old signature in header
        if (    ( ((struct exe_hdr *)pe32)->e_magic != EMAGIC )
             && ( ((struct exe_hdr *)pe32)->e_magic != OMAGIC ) )
        {
            return( ERROR_INVALID_EXE_SIGNATURE );
        }

        // It has the DOS header format - it is at least a DOS EXE
        if (pfl)
        {
            *pfl = 0x0002;
        }

        // get pointer to new exe header
        plv->lv_new_exe_off = ((struct exe_hdr *) pe32)->e_lfanew;
    }

    // read in new header to size of 32-bit mte
    if ( rc = ldrRead( plv->lv_sfn,
                       plv->lv_new_exe_off,
                       (PCHAR)pe32,
                       NULL,
                       sizeof(struct e32_exe),
                       0 ) )
    {
        return( rc );
    }

    // validate as 16-bit signature or 32-bit signature
    if (    !(*(short *) (pe32->e32_magic) == LXMAGIC
         || *(short *) (pe32->e32_magic) == NEMAGIC) )
    {
        // Negate QAppType flag if this is an outdated 32-bit format (LE)
        if (    (*(short *)(pe32->e32_magic) == LEMAGIC)
             && (pfl) )
        {
            *pfl = 0;
        }

        // If it is a WIN32 file (PE header) and we have a flag parm,
        // return the right flag.
        if (   (*(long *) (pe32->e32_magic) == PEMAGIC)
            && (pfl) )
        {
            *pfl = 0x0003;
        }

        return( ERROR_INVALID_EXE_SIGNATURE );
    }

    // This is known to be a Protect-mode app at this point.
    if (pfl)
        *pfl = 0;

    // verify some of the exeheader fields for LX modules
    if (    (*(short *)(pe32->e32_magic) == LXMAGIC)  // 'LX' signature
         && (!(    (pe32->e32_border == E32LEBO)      // Byte ordering
                && (pe32->e32_worder == E32LEWO)      // Word ordering
                && (pe32->e32_level == E32LEVEL)      // Exe format level
                && (pe32->e32_os == NE_OS2)           // Op sys type

                   // BUGBUG BUG BUG BUG.
                   // Itermap offset MUST be the same as the datamap offset
                   // or the app loader code will not work. Some may say this
                   // is a bug, some may say this is a restriction. A feature
                   // by any other name. Fix sometime maybe, or maybe not.
                   // Fix is fairly trivial for loading from the hard disk,
                   // but for loading from diskette ldrGetPages needs a
                   // lot of work me thinks.

                && (    (pe32->e32_datapage == pe32->e32_itermap)
                     || (pe32->e32_itermap == 0)
                   )

            ) ) )
    {
        return( ERROR_INVALID_EXE_SIGNATURE );
    }

    // if header in 16-bit format convert to 32-bit format
    if ( *(short *)(pe32->e32_magic) == NEMAGIC )
        ldrExpandHeader( pe32 );
    else
        pe32->e32_mflags |= MTELONGNAMES;

    // check if executable is a valid image
    if (pe32->e32_mflags & LDRINVALID)
        return(ERROR_EXE_MARKED_INVALID);

    // Clear for internal use
    pe32->e32_mflags &= ~(MTE_MEDIAFIXED | MTE_MEDIACONTIG | MTE_MEDIA16M);

    // Set flags set by ldrOpenPath
    pe32->e32_mflags |= flmte;

    return( ldrMungeFlags( pe32 ) );

} // end of ldrOpenNewExe



/***LP  ldrOpenPath - Open module file, possibly searching LIBPATH
 *
 *      ldrOpenPath opens the file for the module, given the module name.
 *
 *      ldrOpenPath performs one of two operations :
 *
 *      if ((plv->lv_class != CLASS_GLOBAL) or IsMiniIFS)
 *              Call ldrOpen directly.
 *
 *      else do {
 *              get next possible file name from LIBPATH
 *              Call ldrOpen
 *      } until (ldrOpen succeeds, or no more names in LIBPATH)
 *
 *      The names passed for CLASS_GLOBAL must not contain a drive,
 *      path, or extension.  For program modules, the name must contain
 *      the desired null terminated relative path.  If IsMiniIFS, the
 *      LIBPATH name is ignored.
 *
 *      See ldrOpen for details regarding IsMiniIFS usage.
 *
 *      ldrOpenPath(pchModname, cchModname, plv, pflmte)
 *
 *      ENTRY   pachModname             - pointer to module name
 *              cchModname              - module name length
 *              plv                     - pointer to local variables
 *              pflmte                  - pointer to mte flags to return
 *
 *      EXIT    none                    - return successful or call load_error
 *
 */

int ldrOpenPath( PCHAR    pchModname,   /* pointer to ASCII module name */
                 USHORT   cchModname,   /* module name length           */
                 ldrlv_t *plv,          /* pointer to local variables   */
                 PULONG   pflmte )      /* pointer to return mte flags in */
{
    #define BEGINLIBPATH  1           /* libpath_level defines*/
    #define SYSLIBPATH    2
    #define ENDLIBPATH    3

    int     rc,                     /* return code          */
            save_hard_error;
    PCHAR   pchBuf;                 /* Temporary buffer.    */
    ULONG   ichLibPath;             /* LIBPATH string index */
    char    libpath_level;          /* flag indicating which*/
                                    /*  level of libpath we */
                                    /*  are processing      */


    /* Save the current value of TCBFailErr             */
    /* We do not want any hard failures searching the   */
    /*  the LIBPATH to be reported to the user.  If we  */
    /*  do not find the DLL, file not found is what the */
    /*  app expects.  This is mainly required in case   */
    /*  an entry in the libpath is removable and not    */
    /*  ready                                           */
    save_hard_error = pTCBCur->TCBFailErr;

    if (IsMiniIFS || (plv->lv_class != CLASS_GLOBAL))
        rc = ldrOpen( &(plv->lv_sfn), pchModname, pflmte );

    else
    {


        /* if there is a user defined BeginLIBPATH */
        if (TKContext()->ptda_pBeginLIBPATH)
        {
            /* set up to search user BeginLIBPATH */
            ichLibPath = (ULONG)TKContext()->ptda_pBeginLIBPATH;
            libpath_level = BEGINLIBPATH;
        }
        else /* start search in system LIBPATH */
        {
            ichLibPath = (ULONG) LDRLibPath;
            libpath_level = SYSLIBPATH;
        } /* endif */
        if ( rc = VMAllocKHB( VM_HKH_PUB_RESRW,
                              (ULONG) MAXPATHLEN,
                              (VMHOB) LDRMTEPATHOWNER,
                              NA,
                              SSToDS(&pchBuf) ) )
        {
            return(rc);
        }
        /* ldrNextLibPath will append a .DLL to our Modname - make sure
         * there isn't one already. If so zap it. Defect 83898.
         */
        if (strncmp(&pchModname[cchModname - 4], ".DLL", 4) == 0) {
                pchModname[cchModname - 4] = '\000';
                cchModname -= 4;
        }
        while ((ichLibPath != -1))
        {
            if ( rc = ldrNextLibPath( SSToDS(&(ichLibPath)),
                                      pchBuf,
                                      MAXPATHLEN,
                                      pchModname,
                                      cchModname ) )
            {

                /* If ldrNextLibpath returns an error, we need to check
                 * ichLibPath to see if we've finished traversing the
                 * LibPath.  If we have, we need to see if there is
                 * another libpath to check.  If there is still another
                 * libpath, we reset ichLibPath to that libpath and
                 * continue in the loop so we will call ldrNextLibPath
                 * from there.
                 */

                if (ichLibPath == -1)
                {
                  if (libpath_level == SYSLIBPATH)
                  {
                      /* if there is a user defined EndLIBPATH */
                      if (TKContext()->ptda_pEndLIBPATH)
                      {
                          /* set up to search user EndLIBPATH */
                          ichLibPath = (ULONG)TKContext()->ptda_pEndLIBPATH;
                          libpath_level = ENDLIBPATH;
                      }
                  } /* end if just finished with the system libpath */
                  else if (libpath_level == BEGINLIBPATH)
                  {
                      ichLibPath = (ULONG) LDRLibPath;
                      libpath_level = SYSLIBPATH;
                  } /* end if just finished with the BeginLIBPATH */
                  /* else if libpath_level == ENDLIBPATH, leave
                   * at ichLibPath==-1 so we will drop out of
                   * while loop
                   */
                }

                /* Continue in the while loop.  If we had exhausted
                 * the ichLibPath and there were more paths to check
                 * out we have now reset ichLibPath for the new path.
                 * If we had exhausted the ichLibPath and there were
                 * no more paths, ichLibPath is set to -1 and we will
                 * drop out of the while loop.  If the error return
                 * code was simply because the file could not be
                 * found, ichLibPath points to the next directory
                 * to search.
                 */
                continue;
            }

            /*
             * ldrNextLibPath succeeded - attempt to open the file.
             */

            rc = ldrOpen( &(plv->lv_sfn), pchBuf, pflmte );

            if (!rc)     // Avoid switch in the NO_ERROR case.
              break;

            /* Retry only if the return code from ldrOpen is one
             * of the following list.  Otherwise, exit the loop
             * and fail the call.
             */

            switch (rc)
            {
              case ERROR_FILE_NOT_FOUND :
              case ERROR_PATH_NOT_FOUND :
              case ERROR_INVALID_DRIVE :
              case ERROR_OPEN_FAILED :
              case ERROR_FILENAME_EXCED_RANGE :
              case ERROR_INVALID_NAME :
              case ERROR_ACCESS_DENIED :
              case ERROR_INVALID_ACCESS :
              case ERROR_NETWORK_BUSY :
              case ERROR_TOO_MANY_CMDS :
              case ERROR_ADAP_HDW_ERR :
              case ERROR_BAD_NET_RESP :
              case ERROR_UNEXP_NET_ERR :
              case ERROR_BAD_REM_ADAP :
              case ERROR_NETNAME_DELETED :
              case ERROR_BAD_DEV_TYPE :
              case ERROR_TOO_MANY_SESS :
              case ERROR_INVALID_PASSWORD :
              case ERROR_VC_DISCONNECTED :
              case ERROR_NOT_DOS_DISK :
              case ERROR_REM_NOT_LIST :
              case ERROR_BAD_NETPATH :
              case ERROR_DEV_NOT_EXIST :
              case ERROR_BAD_NET_NAME :
              case ERROR_REQ_NOT_ACCEP :
              case ERROR_NETWORK_ACCESS_DENIED :
                rc = ERROR_FILE_NOT_FOUND;  // Assume end of LibPath
                continue;           // Retry until ichLibPath == -1.
            }
            break;                  // Fatal error - Exit Loop
        }
        VMFreeKHB( VM_HKH_PUB_RESRW, pchBuf );
    }
    /* Restore any hard error values that were saved before traversing */
    /*  the LIBPATHs                                                   */
    pTCBCur->TCBFailErr = save_hard_error;
    return (rc);

} // end of ldrOpenPath



#pragma code_seg()
#pragma code_seg("LDRSWAP_TEXT", "CODE")


/***LP  ldrCheckForWLO - check for Windows Appls that run as PM appls.
 *      This is called from LDRQAppType().
 *
 *      ENTRY   lv   - pointer to load variables frame
 *              pe32 - pointer to exe header from file
 *
 *      EXIT    int  - -1 if WLO application,
 *                     0  if not WLO application
 *                     rc if could not determine
 *
 *      NOTES   This routine uses ldrWLOTbl when searching through the
 *              import module names table for a 0.9 WLO module.  This
 *              array of structures is defined immediately above this
 *              routine.
 */
int ldrCheckForWLO( ldrlv_t *lv,
                    struct e32_exe *pe32 )
{
    USHORT         cbmodtab;    // Count of bytes in module reference table
    unsigned char *modtab;      // Linear addr of module reference table buffer
    ulong_t        smodtab;     // Segmented addr of module reference table buffer
    USHORT         endmod;      // Ending offset of module reference table buffer
    USHORT         cbnamtab;    // Count of bytes in imported names table
    unsigned char *namtab;      // Linear addr of imported names table buffer
    ulong_t        snamtab;     // Segmented addr of imported names table buffer
    USHORT         endnam;      // Ending offset of imported names table buffer
    USHORT         namlen;      // Length of name in imported names table
    PCHAR          strptr;      // Pointer to name in imported names table
    int            i;           // Loop variable
    int            rc;          // Save any bad return codes
    USHORT         modoff = 0;  // Offset in module reference table buffer
    USHORT         modbas = 0;  // Base offset of module reference table buffer
    USHORT         namoff = 0;  // Offset in imported names table buffer
    USHORT         nambas = 0;  // Base offset of imported names table buffer

    // Return WLO if 1.0 WLO bit is already set.
    if (pe32->e32_res3[3] & NEWLOAPPL)
    {
        return -1;
    }

    // Otherwise check to see if 0.9 WLO by scanning import module names
    // table for a match with one of names in ldrWLOTbl

    // Return not WLO if there are no entries in module reference table or
    // imported names table
    if (   ((cbmodtab = (USHORT)(pe32->e32_impmodcnt * sizeof(SHORT))) == 0 )
        || (cbnamtab = (USHORT)(pe32->e32_enttab - pe32->e32_impproc)) == 1 )
    {
        return 0;
    }

    // Allocate storage buffer for module reference table and import names
    // table. Note, that if no error occurs, we have just initialised rc to
    // the not WLO return value of 0.
    if ( rc = PGAllocBuf( 2,                  // 2 pages
                          PG_16M | PG_CONTIG | PG_DISCARDABLE,
                                              // below 16 meg line and contig
                          -1,                 // need physical memory
                          SSToDS(&modtab),
                          SSToDS(&smodtab),
                          0 ) )
    {
        // Could not get any memory.
        return(rc);
    }

    // We use the first page for the module reference table, and the
    // second page for the import names table.
    namtab = modtab + 0x1000;
    snamtab = smodtab + 0x1000;

    // Read initial portion of imported names table into buffer.
    if (rc = ldrRead( lv->lv_sfn,
                      lv->lv_new_exe_off + pe32->e32_impproc,
                      namtab,
                      snamtab,
                      endnam = min(cbnamtab, (USHORT)PAGESIZE),
                      0 ) )
    {
        goto WLO_error;
    }

    // While the module reference table has not been completely processed
    while (cbmodtab > 0)
    {
        // Read portion of module reference table into buffer
        if (rc = ldrRead( lv->lv_sfn,
                          lv->lv_new_exe_off + pe32->e32_impmod + modbas,
                          modtab,
                          smodtab,
                          endmod = min( cbmodtab, (USHORT)PAGESIZE ),
                          0 ) )
        {
            goto WLO_error;
        }

        // Adjust amount of information remaining in module reference table
        // and where to begin reading in next portion of table
        cbmodtab -= endmod;
        modbas += endmod;
        modoff = 0;

        // While contents of module reference table within buffer have not
        // been completely processed
        while (modoff < endmod)
        {
            namoff = *(USHORT *)(modtab + modoff);

            // crude check for a garbage module
            if ( namoff > cbnamtab )
            {
                rc = ERROR_BAD_EXE_FORMAT;
                goto WLO_error;
            }

            // Module name does not fit completely within the current buffer
            if (    (namoff >= (nambas + (USHORT)PAGESIZE))
                 || (    (namlen = *(CHAR *)(namtab + namoff - nambas) )
                      && (namoff + namlen - nambas + (USHORT)1 > endnam)
                    ) )
            {
                nambas = namoff;

                // Read next portion of imported names table into buffer.
                if (rc = ldrRead( lv->lv_sfn,
                                  lv->lv_new_exe_off + pe32->e32_impproc + nambas,
                                  namtab,
                                  snamtab,
                                  endnam = min( cbnamtab - nambas, (USHORT)PAGESIZE ),
                                  0 ) )
                {
                    goto WLO_error;
                }

                namlen = *(BYTE *)(namtab + namoff - nambas);
            }

            // Compare the module name to each name within ldrWLOTbl
            for (i = 0; i < NUMWLOTBLENT; i++)
            {
                // Only compare names if length of names match
                if (namlen == ldrWLOTbl[i].namlen)
                {
                    // Upper case before comparing
                    strptr = (PCHAR)(namtab + namoff - nambas + 1);
                    ldrUCaseString(strptr, namlen);

                    // Are they the same
                    if ( memcmp( strptr, ldrWLOTbl[i].WLOdll, namlen ) == 0 )
                    {
                        // Match found - it is a WLO app.
                        rc = -1;
                        goto WLO_exit;
                    }
                }
            }
            modoff += sizeof(SHORT);
        }
    }

    // Dropped out of the loop - ie, not found, so assume it is not a WLO app.
    // rc will already be 0 if we got to here, so just return rc.

WLO_error:
WLO_exit:
    PGFreeBuf((laddr_t)modtab);
    return(rc);

} // end of CheckForWLO()



#pragma code_seg()
#pragma code_seg("SWAP32_9","CODE")


/***EP  LDRQAppType - return file's exe type
 *
 *      This is called for the DOSIQAPPTYPE API.
 *
 *      ENTRY   pszModName - pointer to ASCIIz module name
 *              pulType    - pointer to put type
 *
 *      EXIT    int        - return code (NO_ERROR if successful)
 */

int LDRQAppType( PCHAR           pszModName, // the filename of the module
                 register PULONG pulType )   // pointer to result
{
    ldrlv_t         lv;                // loader variable
    register struct e32_exe *pe32;     // pointer to linker exe image
    int             rc;
    unsigned long   NameHash;          // Save the hash of the filename
    USHORT          fl = FALSE;        // Returned by ldrOpenNewExe

    if ( rc = TKTakeKernelSem() )
    {
        return(rc);
    }

    if( rc = LDRGetSem() )
    {
        TKClearKernelSem();
        return(rc);
    }

    // Do we already know the type ? We cache the result of the last QAT
    // call that was made. Cache is invalid after 4 seconds.
    //
    // BUGBUG - it is theoretically possible to make 2 consecutive calls
    //          to QAT with different file names and get a cache hit - this
    //          might happen on a DosFindFirst/FindNext scenario, and would
    //          require the filenames to generate the same hash. This could
    //          happen in theory, but seems very very unlikely in practice.
    //          Ver 2.1 has a modified hash algorithm to try to reduce the
    //          risk of this happening.
    //
    NameHash = ldrHashQATName( pszModName );
    if (    ( SISData.SIS_BigTime < ldrQATCache.QATTimeStamp+4 )
         && (NameHash == ldrQATCache.QATHashName ) )
    {
        // We already know the type of this one - just give the answer back.
        *pulType = ldrQATCache.QATFlags;
        goto ldrQAT_cache_exit;
    }

    // The type was not in the cache, so we have to do this the
    // hard way ...

    *pulType = 0L;

    /* Init local variables */
    memset( (PCHAR)SSToDS(&lv), 0, sizeof(ldrlv_t) );
    lv.lv_type  = EXT_PROGRAM;
    lv.lv_sfn   = SFN_NULL;
    lv.lv_class = CLASS_PROGRAM;

    /* No error buffer */
    pTCBCur->TCB_ObjNameBuf = NULL;
    pTCBCur->TCB_ObjNameBufL = 0;

    pe32 = (struct e32_exe *) pheaderbuf;

    // Read exe header.
    if ( rc = ldrOpenNewExe( pszModName,
                             (USHORT)strlen(pszModName),
                             SSToDS(&lv),
                             SSToDS(&fl) ) )
    {
        if (fl==0x0003)  // file has WIN32 (PE) EXE signature
        {
            *pulType = QAT_WINDOWS_PROT31 | QAT_32BIT;

            if (((PPE_NT_HEADERS)pe32)->FileHeader.Characteristics & PE_DLL)
            {
               *pulType |= QAT_DYNLINK;
            }

            rc = NO_ERROR;
        }
        else if (fl==0x0002)  // file has DOS EXE signature
        {
            *pulType = QAT_DOSEXE;
            rc = NO_ERROR;
        }
        else if (fl==0x0001) // file found but no DOS EXE signature
        {
            // is it a .COM file ?

            while (*pszModName++);
            pszModName -= 5;
            if (    ( (*((ulong_t *)pszModName)) == 0x4d4f432e )    // .COM
                 || ( (*((ulong_t *)pszModName)) == 0x6d6f632e ) )  // .com
            {
               *pulType = QAT_DOSEXE;
               rc = NO_ERROR;
            }
        }
        goto ldrQAT_exit;
    }

    *pulType = (pe32->e32_mflags & QAT_MASK) >> 8;

    // Do not do any of the windows etc code for LX format
    if (*(short *) (pe32->e32_magic) != LXMAGIC)
    {

        // determine if windows app and if so, real or protect mode
        // res[2] is the NE_EXETYPE field from 16 bit header
        if (    (pe32->e32_res3[2] == NE_WINDOWS)
             || (pe32->e32_res3[2] == NE_DEV386) )
        {
            // if the expected windows version field is 3.0 or greater
            // or this app runs in prot mode (NE_FLAGSOTHERS bit 1 set)
            // then say it is protect mode app else real mode app
            // res[1] is the NE_EXPVER field from 16 bit header
            // res[3] is the NE_FLAGSOTHERS field from 16 bit header
            // NOTE: keep low order bits of pulType zeroed, because
            // old apps which use QAppType may not check to see if
            // the Windows bits are set before using lower order bits

            // Start WLO apps as PM applications - check to see if this
            // is or is not a WLO app.

            // -1 means it is a WLO app
            if ( -1 == (rc = ldrCheckForWLO( SSToDS(&lv), pe32 )) )
            {
                *pulType |= QAT_EXETYPE_PMAPI;
                rc = 0;
            }
            else if (rc)
            {
                goto ldrQAT_exit;
            }

            // rc == 0, so it is not a WLO app, so what type of
            // windows app is it ?

            // windows 3.1
            else if (    (pe32->e32_res3[1] == 0x03)
                      && (pe32->e32_res3[0] == 0x0A) )
            {
                *pulType = QAT_WINDOWS_PROT31;
            }

            // windows 3.0
            else if (    (    (pe32->e32_res3[1] == 0x03)
                           && (pe32->e32_res3[0] == 0x00) )
                      || (pe32->e32_res3[3] & 0x02)
                      || (pe32->e32_res3[3] & 0x04) )
            {
                *pulType = QAT_WINDOWS_PROT;
            }

            // windows 3.2 ???? - give it to 3.1 to handle ????
            else if (pe32->e32_res3[1] >= 0x03)
            {
                *pulType = QAT_WINDOWS_PROT31;
            }

            else
            {
                *pulType = QAT_WINDOWS_REAL;
            }

        }

    }  // end of if not LX format

    // Do not set additional bits if windows apps or DLLs. Windows
    // DLLs can have stubs that start windows.
    if (!(*pulType & (QAT_WINDOWS_PROT31|QAT_WINDOWS_REAL|QAT_WINDOWS_PROT)))
    {
        if (pe32->e32_mflags & LIBRARYMOD)
            *pulType |= QAT_DYNLINK;
        if (pe32->e32_mflags & DEVDRVMOD)
            *pulType |= QAT_PDD;
        if (pe32->e32_mflags & VDDMOD)
            *pulType |= QAT_VDD;

        // Set the 32-bit flag if linked with LINK386.
        if (*(short *) (pe32->e32_magic) == LXMAGIC)
            *pulType |= QAT_32BIT;

        // Removed code which changed pulType if LX & DEVDRVMOD - defect 87356
    }

ldrQAT_exit:

    // Close exe file
    ldrClose(lv.lv_sfn);

    // If no error, then cache the result for next time through.
    if (!rc)
    {
        ldrQATCache.QATTimeStamp = SISData.SIS_BigTime;
        ldrQATCache.QATHashName = NameHash;
        ldrQATCache.QATFlags = *pulType;
    }

ldrQAT_cache_exit:
    // free loader and ptda semaphores
    LDRClearSem();
    TKClearKernelSem();

    return(rc);

} // end of LDRQAppType


#pragma code_seg()
#pragma code_seg("SWAP32_14","CODE")


/****LP ldrCreateMte - allocate and load module table entry
 *
 *      This routine loads and initializes a module table entry. Memory
 *      is allocated for the file module data with additional
 *      space for module handles and the module
 *      pathname. Segment handle and selector fields are added to the
 *      end of each segment table entry for 16-bit modules.  The module
 *      reference strings are checked to make sure there are no wild pointers
 *      that would cause a gp fault. The resident name table is checked to
 *      make sure that it can be scanned without causing a gp fault.
 *
 *      If the module that is being loaded is a 16-bit module the 16-bit
 *      exe header was expanded into a 32-bit exe header when file
 *      was opened.
 *
 *      This procedure performs the following steps:
 *
 *      - Check how many preload pages were specified, reduce if needed.
 *      - For 32-bit module, compute adjustment size to force the preload
 *        pages onto a page boundary.
 *      - Compute the size of the mte.
 *      - Alloc a memory object to read in memory resident tables.
 *      - Read in memory resident tables.
 *              * For 16-bit modules, beginning of segment table through end of
 *                entry table
 *              * For 32-bit modules, beginning of object table through end of
 *                fixup tables plus any preload pages.
 *      - Compute size of heap memory to create MTE in.
 *      - Copy exe header into heap memory.
 *      - Copy pathname into heap memory.
 *      - Copy load resident tables into heap memory.
 *      - Validate MTE pointers
 *      - Determine if fixup section will stay resident.
 *      - ReAlloc preload pages as discardable.
 *      - Zero initialize the module pointer table.
 *      - Zero initialize the call gate selector value in the entry table.
 *      - For 16-bit modules expand segment table entries and setup consistent
 *        segment flags.
 *      - Adjust mte offsets.
 *              * For 16-bit, account for extended ste size plus
 *                constant will also be added for the difference
 *                between the 32-bit exe header and the 16-bit exe header.
 *      - Verify access to each name in the module reference table.
 *      - Convert the module name to upper case since module name
 *        compares are not case sensitive.
 *      - If library module, verify file name matches the internal name.
 *      - Verify access to each name in the resident name table.
 *      - Setup for global library initialization if required.
 *
 *      ENTRY   pe32                    - pointer to link exe image
 *              plv                     - pointer to local variables
 *                                      - ptda and search buff are setup
 *
 *      EXIT    none                    - return successful or call load_error
 */

int ldrCreateMte( register struct e32_exe *pe32,  /* pointer to linker exe image */
                  ldrlv_t                 *plv )  /* pointer to local variables */
{
    register ldrmte_t *pmte = NULL;   /* pointer to loader MTE */
    register ldrsmte_t *psmte = NULL; /* pointer to swappable loader MTE */

    PCHAR     psmteheap = NULL;   /* pointer to swappable heap for MTE*/
    PCHAR     psheap = NULL;      /* pointer to swappable heap */
    PCHAR     psheap2 = NULL;     /* pointer to swappable heap */
    PCHAR     prheap = NULL;      /* pointer to resident heap */
    VMHOB     hobmte = HOBNULL;   /* MTE pseudo handle */

    int       rc;
    ULONG     cbuf;               /* size of loader table info */
    ULONG     crmte;              /* size of resident MTE */
    ULONG     csmte;              /* size of swappable MTE */
    PCHAR     psrc;               /* source pointer */
    ULONG     mte_16_32_constant; /* adjustment constant for ptrs */
    ULONG     cbpathlen;          /* length of pathname in TempBuf */
    int       count;
    ULONG     objtaboff;          /* Offset into EXE of object table */
    VMAC      vmac;               /* return info from VM */
    ULONG     lobjnum;            /* object number count */
    ldrste_t  *pste;              /* pointer to segment table entry */
    ldrote_t  *pote;              /* pointer to object table entry */
    ULONG     cbfile;             /* Amount of data for segment in file */
    ULONG     cbseg;              /* Size of segment */

    uchar_t  *pLie;
    uchar_t  *pName;
    uchar_t  *pLieHold;
    uchar_t  *pNameHold;

    // Allow detection of whether or not we have allocated memory.
    vmac.ac_va = 0;

/***ET+ mte_alloc - memory allocation for resident MTE section
 *
 * pe32 - Pointer to memory for resident MTE.
 *
 * The MTE consists of two parts the MTE pointers section and the table
 * section.  The pointer section is allocated in two parts the resident
 * section and the swappable section.  The table section of the mte is
 * also allocated from the swappable heap.
 *
 * The first section of the pointer section allocated from the resident
 * heap will also contain the module pointer table and the module name.
 * The second section of the pointer section is allocated from the swappable
 * heap, also attached to this heap object will be the loader's table sections.
 * The loader's table section will contain space for the pathname, object table,
 * loader info and the fixup table.
 *
 *
 *        Memory resident object              Resident heap object
 *  pe32->+----------------+ -+    Copy    ->+----------------+<-pmte
 *        | Linker EXE info|  |------------| | MTE pointers   |
 *        +----------------+ -+    |       ->|----------------|
 *                                 |         |Space for Modptr|
 *                                 |         +----------------+
 *                                 |         | Module name    |
 *                                 |         +----------------+
 *                                 |
 *                                 |          Swappable heap object
 *                                 |         +----------------+
 *                                 +-------->| MTE pointers   |
 *                                           +----------------+
 *                                           |Space for pathnm|
 *                                           +----------------+
 *                                           | Object table   |
 *                                           |or segment table|
 *                                           +----------------+
 *                                           | Loader tables  |
 *                                           +----------------+
 *                                           | Fixup info     |
 *                                           +----------------+
 */

/*end*/
    /*
     * allocate memory for MTE from resident heap
     */

    /*
     * Compute size of resident heap object to hold resident loader MTE
     * and module ptr table.  Since we have not read the loader section of
     * the exe image we do not have the size of the module name.  So
     * allocate only memory for the loader MTE and modptrs and
     * allocate the space for the module name later.
     *
     * NOTE: Here we must allocate an extra DWORD for modptrs for EXEs only
     * in case the EXE does not import either DOSCALL1 or DOSCALLS.  If it
     * doesn't, we must attach DOSCALL1.DLL to the EXE anyways, and thus we
     * need the extra DWORD for the additional MTE pointer.
     */
    crmte = sizeof(ldrmte_t) + (pe32->e32_impmodcnt << 2);

    if ( rc = VMAllocKHB( VM_HKH_PUB_RESRW,
                          crmte,
                          (VMHOB) LDRMTEOWNER,
                          NA,
                          SSToDS(&prheap) ) )
    {
        ldrStop(1, 0);
        goto createerror;
    }

    /*
     * Copy fields from the Linker EXE image to the loader MTE allocated
     * from the resident heap.
     */
    pmte = (ldrmte_t *) prheap;
    memset( prheap, '\0', crmte );
    pmte->mte_flags2 = MTEFORMATNE;
    if ( *((ushort_t *)pe32) == E32MAGIC )
        pmte->mte_flags2 = MTEFORMATLX;
    pmte->mte_flags1 = pe32->e32_mflags;
    pmte->mte_impmodcnt = pe32->e32_impmodcnt;

    /*
     * allocate memory for swappable heap object for loader tables
     */

    /*
     * compute length of pathname in ldrFileNameBuf
     */
    cbpathlen = strlen( ldrpFileNameBuf ) + 1;

    /*
     * compute size of swappable heap object which is made up of the
     * swappable MTE pointers, the pathname and fixup table and
     * other loader tables found in the loader section.
     */

    csmte = sizeof(ldrsmte_t) + cbpathlen + pe32->e32_fixupsize + pe32->e32_ldrsize;

    /*
     * add space for segment pointers and segment status flags
     * for 16-bit modules
     */
    if ( ldrIsNE(pmte) )
    {
        csmte += pe32->e32_objcnt * ( sizeof(ldrste_t) - sizeof(struct new_seg) );
    }

    /*
     *      Expanding segment table for 16-bit modules
     *
     * 16-bit modules reserve       |       32-bit module
     * space for each STE           |
     *                              |
     *      +----------------+      |       +----------------+
     *      |Space for pathnm|      |       |Space for pathnm|
     *      |----------------|      |       |----------------|
     *      |4*mte_segtbl_cnt|      |       | Object Table   |
     *      |----------------|      |       +----------------+
     *      |segtbl from file|      |
     *      +----------------+      |
     *                              |
     */

    /*
     * allocate memory for swappable MTE from swappable heap
     */
    if ( rc = VMAllocKHB( VM_HKH_PUB_SWAPRW,
                          plv->lv_csmte = csmte,
                          (VMHOB) LDRMTEOWNER,
                          NA,
                          SSToDS(&psmteheap) ) )
    {
        ldrStop(2, 0);
        goto createerror;
    }

    /*
     * Copy fields from the Linker EXE image to the loader swappable MTE
     * pointers.
     */
    psmte = pmte->mte_swapmte = (ldrsmte_t *) psmteheap;
    memset( psmteheap, '\0', csmte );

    psmte->smte_mpages = pe32->e32_mpages;
    psmte->smte_startobj = pe32->e32_startobj;
    psmte->smte_eip = pe32->e32_eip;
    psmte->smte_stackobj = pe32->e32_stackobj;
    psmte->smte_esp = pe32->e32_esp;
    psmte->smte_pageshift = pe32->e32_pageshift;
    psmte->smte_fixupsize = pe32->e32_fixupsize;
    psmte->smte_objtab = pe32->e32_objtab;
    psmte->smte_objcnt = pe32->e32_objcnt;
    psmte->smte_objmap = pe32->e32_objmap;
    psmte->smte_itermap = pe32->e32_itermap;
    psmte->smte_rsrctab = pe32->e32_rsrctab;
    psmte->smte_rsrccnt = pe32->e32_rsrccnt;
    psmte->smte_restab = pe32->e32_restab;
    psmte->smte_enttab = pe32->e32_enttab;
    psmte->smte_fpagetab = pe32->e32_fpagetab;
    psmte->smte_frectab = pe32->e32_frectab;
    psmte->smte_impmod = pe32->e32_impmod;
    psmte->smte_impproc = pe32->e32_impproc;
    psmte->smte_datapage = pe32->e32_datapage;
    psmte->smte_nrestab = pe32->e32_nrestab;
    psmte->smte_cbnrestab = pe32->e32_cbnrestab;
    psmte->smte_autods = pe32->e32_autodata;
    psmte->smte_debuginfo = pe32->e32_debuginfo;
    psmte->smte_debuglen = pe32->e32_debuglen;
    psmte->smte_heapsize = pe32->e32_heapsize;
    psmte->smte_stacksize = pe32->e32_stacksize;

    /*
     * Restore 16-bit module value saved in reserved fields of linker
     * EXE image.
     */

    if ( ldrIsNE(pmte) )
    {
        psmte->smte_NEexpver = *(USHORT *) pe32->e32_res3;
        psmte->smte_NEexetype = *(USHORT *) ((PCHAR) pe32->e32_res3 + 2) & (USHORT) 0xff;
        psmte->smte_alignshift = *(USHORT *) ((PCHAR) pe32->e32_res3 + 4);
        psmte->smte_stacksize = (ulong_t)(*(USHORT *)((PCHAR)pe32->e32_res3+6));
    }

    cbuf = psmte->smte_fixupsize + pe32->e32_ldrsize;

    objtaboff = psmte->smte_objtab;

    /*
     * allocate object for tables & preload pages but don't commit memory
     */
    if ( rc = VMAllocMem( cbuf,
                          cbuf,
                          VMS_COMMIT | PG_SWAPPABLE | PG_R | PG_W,
                          HOBNULL,
                          (VMHOB) LDRPRLOBJECT,
                          (VMHOB) LDRPRLOBJECT,
                          NA,
                          0,
                          SSToDS(&vmac) ) )
    {
        ldrStop(3, 0);
        goto createerror;
    }

    /*
     * pobj - Pointer to memory object for loader tables and preload pages
     *
     * pobj points to a memory object which contains the loader tables
     * and pPreload points to a discardable object which contains the
     * preload pages.
     *
     *   pobj-->    ------------------
     *   cadj       |       Pad      |<--- Needed to force preload pages to
     *              |----------------|     page boundary
     *              | Loader Section |
     *              |----------------|
     *              | Fixup Section  |
     * pPreload---> |----------------|<--- Page boundary
     *              | Preload Pages  |
     *              ------------------
     */

    /*
     * read in loader info
     */

    if ( rc = ldrRead( plv->lv_sfn,
                       objtaboff + plv->lv_new_exe_off,
                       (char *)vmac.ac_va,
                       NULL,
                       cbuf,
                       0 ) )
    {
        ldrStop(4, 0);
        goto createerror;
    }

/*
 * Copy the data into the swappable part of the MTE which contains the swappable
 * MTE pointers, pathname, object table, Loader Tables and Fixup Info.
 *
 *                                            Swappable system
 *                                            heap object.
 *                                           +----------------+<-psmteheap
 *                                           | MTE pointers   |
 *                                           +----------------+<-psheap
 *                                           |Space for pathnm|
 *        memory object                    ->|----------------|<-psheap2
 *  pobj->+----------------+               | | Object tables  |
 *        |      Pad       |               | |  or segment    |
 *        |----------------| --+        +--| |----------------|
 *        | Loader Section |   |  Copy  |  | | Loader table   |
 *        |----------------|   |--------+  | |----------------|
 *        | Fixup Section  |   |           | | Fixup info     |
 *        |----------------| --+           ->+----------------+
 *        | Preload Pages  |
 *        +----------------+
 */
    psheap = psmteheap + sizeof(ldrsmte_t);

    psheap2 = psheap + cbpathlen;

    psmte->smte_objtab = (ULONG) psheap2;

    /*
     * for 16-bit modules leave space for expanding of segment table
     */
    count = 0;
    if ( ldrIsNE(pmte) )
    {
       count = psmte->smte_objcnt * (sizeof(ldrste_t) - sizeof(struct new_seg));
    }

    /*
     * copy loader & fixup section to swappable table section of MTE
     */
    memcpy( psheap2 + count,
            (char *)vmac.ac_va,
            cbuf );

    /*
     * An adjustment constant has to be added to pointers in MTE. The
     * pointers in MTE are relative to the beginning of the MTE header.
     * Since space has been added between the header and the tables,
     * the pointers need to be updated by a constant.
     */
    mte_16_32_constant = (ULONG) psheap2 + count - objtaboff;

    /*
     * check that ptrs in MTE are valid
     */
    if ( rc = ldrMTEValidatePtrs( psmte,
                                  (ULONG) psheap2 + csmte,
                                  mte_16_32_constant ) )
    {
        ldrStop(5, 0);
        goto createerror;
    }

    plv->lv_pmte = pmte = (ldrmte_t *) prheap;

    // upper case module name
    ldrUCaseString( (PCHAR) psmte->smte_restab + 1,
                    (ULONG)*((PUCHAR) psmte->smte_restab) );

    // copy module name into heap object
    memcpy( (PUCHAR) &pmte->mte_modname,
            ((PUCHAR) psmte->smte_restab)+1,
            (*((PUCHAR) psmte->smte_restab) > 8) ?
                        8 : *((PUCHAR) psmte->smte_restab));

    // Check to see if this is a system DLL - if so, set the systemdll bit
    // Note - rc == 0 already at this point, so no need to init.
    if (pmte->mte_flags1 & LIBRARYMOD)                          /* @D76609 */
    {
       for (; rc < NUMSYSTBLENT_SWAP; rc++)
       {
            // Are they the same
            //Strings match, and either
            //  a. All 8 bytes match
            //  b. Next byte in mte_modname is a NULL
            if ( !(memcmp( &pmte->mte_modname,
                           ldrSysTbl[rc].SysDll,
                           ldrSysTbl[rc].NamLen ))
                  &&  ((ldrSysTbl[rc].NamLen==8) ||
                       (pmte->mte_modname[ldrSysTbl[rc].NamLen]==0)))
            {
                // Match found - it is a system dll
                if (rc < NUMSYSTBLENT)
                   {
                    pmte->mte_flags2 |=  MTEFREEFIXUPS; // PERF - defect 98488
                   pmte->mte_flags2 |= MTESYSTEMDLL;
                   ldrSysTbl[rc].pMTE = pmte;
                   }
                /*  make the pages swappable after loading
                 */
                pmte->mte_flags1 |=  MTESWAPONLOAD;   // perf enh
            }
       }
    }
    // rc = NO_ERROR; don't bother - reset just below

    // Set size of pathname (not including terminating NUL).
    psmte->smte_pathlen = (USHORT)(cbpathlen - 1);

    // copy pathname from TempBuf into MTE
    psmte->smte_path = (ULONG)psheap;
    memcpy( (void *) psmte->smte_path,
            ldrpFileNameBuf,
            cbpathlen );

    // Upper case pathname
    ldrUCaseString( (PCHAR)psmte->smte_path, cbpathlen-1 );

    // create the mte handle (hModule)
    if ( rc = VMCreatePseudoHandle( (void *) pmte,
                                    (VMHOB) LDRMTEOWNER,
                                    SSToDS(&hobmte)))
    {
        ldrStop(7, 0);
        goto createerror;
    }

    plv->lv_hobmte = pmte->mte_handle = hobmte;

    // Initialize MTE Flags
    pmte->mte_flags1 |= plv->lv_class;

    // is this a device driver module?
    if (plv->lv_type == EXT_DEVICE)
        pmte->mte_flags1 |= DEVDRVMOD;

    // or is it an FSD module
    else if (plv->lv_type == EXT_FSD)
    {
        pmte->mte_flags1 |= FSDMOD;

        // force all segments to swappable
        pmte->mte_flags1 &= ~MTE_MEDIAFIXED;
    }

    // Clear internal flags
    pmte->mte_flags1 &= ~MTE_INTNL_MASK;

    if ( ldrIsNE(pmte) )
    {   /*
         * Save pointer to start of swappable mte in mte_fpagetab so
         * the debugger command .lm <addr> works for 16-bit modules
         */
        psmte->smte_fpagetab = (ULONG) psmteheap;
        psmte->smte_fixupsize = csmte;

        /*
         * expand segments by adding a handle and selector field
         * after each segment table entry
         */
        ldrExpandSegment( pmte, plv->lv_type );

        /*
         * Check for porthole modules
         */
        if ((psmte->smte_NEexetype == 2) || (psmte->smte_NEexetype == 0 &&
          ((psmte->smte_NEexpver & 0xff00) == 0x200 ||
          (psmte->smte_NEexpver & 0xff00) == 0x300)))
            pmte->mte_flags1 |= MTEPORTHOLE;
        else
            pmte->mte_flags1 &= ~MTEPORTHOLE;

        /*
         * check for program modules
         */
        if (plv->lv_type == EXT_PROGRAM)
        {

            /*
             * Validate start segment
             */
            if (psmte->smte_startobj == 0 ||
                ldrNumToSte(pmte, psmte->smte_startobj) == 0)
            {
                rc = ERROR_INVALID_STARTING_CODESEG;
                ldrStop(8, 0);
                goto createerror;
            }

            /*
             * Validate stack segment
             */
            if (psmte->smte_stackobj == 0 ||
                (pste = ldrNumToSte(pmte, psmte->smte_stackobj)) == 0 ||
                pste->ste_flags & STE_SHARED)
            {
                rc = ERROR_INVALID_STACKSEG;
                ldrStop(9, 0);
                goto createerror;
            }

            /*
             * Validate auto data segment
             */
            if (psmte->smte_autods != 0 &&
                ldrNumToSte(pmte, psmte->smte_autods) == 0)
            {
                rc = ERROR_INVALID_STARTING_CODESEG;
                ldrStop(10, 0);
                goto createerror;
            }

            /*
             * if SS = DS and SP = 0
             */
            if (psmte->smte_stackobj == psmte->smte_autods &&
                psmte->smte_esp == 0)
            {

                /*
                 * Set SP to top of auto data segment just below
                 * the additional heap
                 */
                psmte->smte_esp = RESIZE64K(pste->ste_minsiz) +
                                                         psmte->smte_stacksize;
            }

        }                           /* end if for EXT_PROGRAM */
        else if (psmte->smte_startobj == 0)
        {
            /*
             *  DLL has no init routine: mark it as global
             *  complete.  This way libinit will not bother about
             *  it.  Also clear instance libinit bit as linker can
             *  turn this on even without an init routine; this
             *  should be an error, but is not for compatibility
             *  reasons.
             */
            pmte->mte_flags1 |= GINISETUP | GINIDONE;
            pmte->mte_flags1 &= ~INSTLIBINIT;
        }
    }                          /* end if for NE module */
    else
    {                          /* 32-bit module */

        if (plv->lv_type == EXT_PROGRAM)
        {

            /*
             * Validate starting code object
             */
            if (psmte->smte_startobj == 0 ||
                ldrNumToOte(pmte, psmte->smte_startobj) == 0)
            {
                rc = ERROR_INVALID_STARTING_CODESEG;
                ldrStop(11, 0);
                goto createerror;
            }

            /*
             * Validate stack and
             * validate that esp is within virtual size of object
             */
            if (psmte->smte_stackobj == 0 || psmte->smte_esp == 0 ||
                (pote = ldrNumToOte(pmte, psmte->smte_stackobj)) == 0 ||
                (psmte->smte_esp > pote->ote_size))
            {
                rc = ERROR_INVALID_STACKSEG;
                ldrStop(12, 0);
                goto createerror;
            }
        }
        else
        {                      /* Else some flavor of DLL */
            /*
             *  For 32-bit DLLs, fail the load if
             *
             *  A. The specified starting object is bad, or
             *  B. no starting object is specified, but either
             *     instance DLL initialization or instance DLL
             *     termination is requested, or
             *  C. instance DLL termination is requested, but the
             *     the starting object is 16-bit.
             */
            if ( (psmte->smte_startobj == 0
                  && (pmte->mte_flags1 & (MTEDLLTERM | INSTLIBINIT)))
              || (psmte->smte_startobj != 0
                  && (pote = ldrNumToOte(pmte, psmte->smte_startobj)) == NULL)
              || ((pmte->mte_flags1 & MTEDLLTERM)
                  && !(pote->ote_flags & OBJBIGDEF))

                )
            {
                if    (pmte->mte_flags1 & MTEDLLTERM  /* if 16-bit entry in 32-bit */
                   && !(pote->ote_flags & OBJBIGDEF) ) /* linked .DLL, clear term bit */
                {
                    pmte->mte_flags1 &= ~MTEDLLTERM;
                }
                else
                {
                    rc = ERROR_INVALID_STARTING_CODESEG;
                    goto createerror;
                }
            }

            if (psmte->smte_startobj == 0)
                pmte->mte_flags1 |= GINISETUP | GINIDONE;
                                    /* Fake global initialization done */
        }
    }

    /*
     * Check each segment or object in module
     */

    if ( ldrIsNE(pmte) )
    {
        for ( lobjnum = 0; lobjnum < psmte->smte_objcnt; lobjnum++ )
        {
            pste = &((ldrste_t *) psmte->smte_objtab)[lobjnum];

            /*
            *  Check that the amount of data in the file does
            *  not exceed the size of the segment.
            */
            if ((cbseg = pste->ste_minsiz) == 0)
                cbseg = _64K;       /* Get size of segment */
            if ((cbfile = pste->ste_size) == 0 && pste->ste_offset != 0)
                cbfile = _64K;      /* Get amount of data in file */
            if (cbfile > cbseg)
            {
                /* Error if file size > segment size */
                rc = ERROR_INVALID_MINALLOCSIZE;
                ldrStop(13, 0);
                goto createerror;
            }

            /*
             * Check for auto data segment.  If found, add in stack
             * and heap sizes and store in ste entry and check for
             * overflow
             */
            if (lobjnum + 1 == psmte->smte_autods)
            {
                if ((cbseg += psmte->smte_heapsize +
                     psmte->smte_stacksize) > _64K)
                {
                    rc = ERROR_AUTODATASEG_EXCEEDS_64k;
                    ldrStop(14, 0);
                    goto createerror;
                }
            }
            pste->ste_minsiz = (USHORT) cbseg;

        }
    } // end of if NE

    else   // 32-bit module
    {
        for ( lobjnum = 0; lobjnum < psmte->smte_objcnt; lobjnum++ )
        {
            pote = &((ldrote_t *) psmte->smte_objtab)[lobjnum];

            /*
             * if not swapping or loading from removable media force
             * preload of resources.
             */
            if (    pote->ote_flags & OBJRSRC
                 && (    !swapping
                      || !(pmte->mte_flags1 & MTE_MEDIAFIXED) ) )
                pote->ote_flags |= OBJPRELOAD;

            /*
             * Clear bit in flags to use as allocation flag
             */
            pote->ote_flags &= ~OBJALLOC;

            /*
             * Check for auto data object.  If found, add heap size
             * to virtual size of object. If virtual size > 64k
             * and this is USE16 object goto error.
             */
            if (lobjnum + 1 == psmte->smte_autods  &&
               (pote->ote_size += psmte->smte_heapsize) > _64K &&
               !(pote->ote_flags & OBJBIGDEF))
            {
                rc = ERROR_AUTODATASEG_EXCEEDS_64k;
                ldrStop(15, 0);
                goto createerror;
            }

            /*
             * check that executable objects don't have write access also
             */
            if (pote->ote_flags & OBJEXEC && pote->ote_flags & OBJWRITE)
            {
                rc = ERROR_BAD_EXE_FORMAT;
                ldrStop(16, 0);
                goto createerror;
            }

            /*
             * if object readonly force it to be shared
             */
            if (!(pote->ote_flags & OBJWRITE))
                pote->ote_flags |= OBJSHARED;

        }
    }

    /*
     * Check if internal name for a LIBRARY matches the module name
     * but not for porthole apps.
     */
    if ((plv->lv_type == EXT_LIBRARY) &&
      !(pmte->mte_flags1 & MTEPORTHOLE) &&
      (rc = ldrCheckInternalName(pmte)) != NO_ERROR)
    {
        ldrStop(17, 0);
        goto createerror;
    }

    /*
     * BUGBUG LTS - 9/26/89: remove this check when linker is fixed
     * For a 32-bit module add one to the imported module names table offset
     * to skip over the byte of zero which was place there to ensures
     * non-zero first entry offset for 16-bit modules
     */
    if (    ldrIsLX(pmte)
         && (pmte->mte_impmodcnt > 0)
         && (*((uchar_t *) psmte->smte_impmod) == 0) )
      psmte->smte_impmod++;

    pmte->mte_sfn = plv->lv_sfn;    /* save system file number in mte */

#if 0
    if (    IOPLEnabled
         || (ldrChkIOPLTable(psmte) == NO_ERROR) )
        pmte->mte_flags1 |= MTEIOPLALLOWED;
#endif
    /*
     * link mte in list of mtes
     */
    ldrLinkMTE( pmte );

    /*
     * first name in resident name table is the module name.
     * Skip over name and ordinal, now psrc points to second entry in the
     * resident name table. Save for later.
     */
    count = *((PCHAR ) psmte->smte_restab);

    psrc = (PCHAR )psmte->smte_restab + count + 1;

    /*
     * Go thru the resident name table scanning for a string of zero
     * length. This makes sure that the table is properly terminated
     */
    while ( *psrc != 0 )
    {
        count = (int) *psrc++;              /* length of module name */
        psrc += count + 1;                  /* skip over name and ordinal */
        if ((ULONG) psrc > ((ULONG) psheap + csmte))
        {
            rc = ERROR_BAD_EXE_FORMAT;
            ldrStop(18, 0);
            goto createerror;
        }
    }

    pmte->mte_flags1 |= MTEVALID;           /* Demand loader can look now */

    /*
     * Verify that Linker zeros callgate field of a type 2 entry table entry
     * fail load it it doesn't.
     */
    if ( ldrIsLX(pmte) )
    {
        if ( rc = ldrEachObjEntry( 0,
                                   pmte,
                                   ldrInitEntry,
                                   0 ) )
        {
            ldrStop(19, 0);
            goto createerror;
        }
    }

    // Free memory that the mte header was read into.
#ifdef LDRSTRICT
    ldrAssert( !VMFreeMem( vmac.ac_va, NA, NA ) );
#else
    VMFreeMem( vmac.ac_va, NA, NA );
#endif
    vmac.ac_va = 0;

    /*
     * See if we can close this module's file, the file can be closed
     * once the MTE is created for modules that have no physical data
     * & the non-resident name table is zero. (32-bit Forwarder MTEs)
     */
    if (    ldrIsLX(pmte)
         && psmte->smte_mpages == 0
         && psmte->smte_cbnrestab == 0
         && pmte->mte_sfn != SFN_NULL  )
    {
        ldrClose(pmte->mte_sfn);
        pmte->mte_sfn = SFN_NULL;
    }

    /*
    **----------------------------------------------------------------
    ** Check to see if we have a lie list.  If we do then parse
    ** the list to see if the module name is in the file.  If it is
    ** then store the lie version information in swappable mte, so
    ** that when we need to lie it is available.  Also set the bit
    ** on in the mte flags for a additional quick check.  NOTE:  the
    ** lie list has already been verified for syntax in ldrinita.asm,
    ** w_get_version checks for lie list information.
    **----------------------------------------------------------------
    */

    if (LDRLieList) {      /* do we have a lie list? ie. OS2VER */

      unsigned version;    /* 161596 */

      lobjnum = 0;         /* not found flag */

      pLie = LDRLieList;

      /* While not found and there are still lie list entries to be processed */
      while ( (!lobjnum) && (*pLie) ) {

	/*
	**------------------------------------------------------
	** Pull the version info from the first few bytes.
	**------------------------------------------------------
	*/

	for (version = 0 ; *pLie >= '0' && *pLie <= '9'; ++pLie)
	  version = 10 * version + (*pLie - '0');

	pLie++;                      /* skip over "=" and goto name  161596 */

	pName = (uchar_t *)(psmte->smte_path);

lie_continue:
	while ( (*pName) && (*pLie!=*pName) )
                pName++;

	if (*pName) {

	  /*
	  **------------------------------------------------------
	  ** Found a first character match, so see if rest of name
	  ** matches.  If not, reset pLie and resume pName++ scan.
	  **------------------------------------------------------
	  */

	  pLieHold = pLie;
	  pNameHold = pName;
	  while ((*pName) && (*pLie) && (*pLie!=';') && (*pName==*pLie)) {
	    pLie++; pName++;
	  }
	  if ((!(*pName)) &&  ( (!(*pLie)) || (*pLie==';') )) {
	    lobjnum = 1;
	  }
	  else {
	    pLie = pLieHold;
	    pName = pNameHold;
	    pName++;
	    goto lie_continue;
	  }
	}
	
	if ( (!lobjnum) && (!(*pName)) ) {

	  /* Fell off end of name, so go to next lie list entry. */

	  while ( (*pLie) && (*pLie!=';')) pLie++;
	  while (*pLie==';') pLie++;

	}
	
      }

      /*
      **------------------------------------------------------
      ** We've found a match, so set the bit in the MTE flags
      ** then load the SMTE with the version information.
      **------------------------------------------------------
      */

      if (lobjnum) {

	/*
	**----------------------------------------------------------------
	** Note: Major and Minor versions are computed in the following
	** manner.  The first digit in the version is expanded one tens
	** place to compute the major version.  The second digit in the
	** version is expanded one tens place to compute the minor version.
	**----------------------------------------------------------------
	*/

	pmte->mte_flags2 |= MTE20LIELIST;
	psmte->smte_major_os = (version / 10)  * 10;  /* expand to major */ 
	psmte->smte_minor_os = (version % 10)  * 10;  /* expand to minor */

      }

    }

createerror:
    if (rc)
    {
        ldrmte_t *pmtehead;

        /*
         * see if MTE linked into chain, if so, remove it
         */
        switch (pmte->mte_flags1 & CLASS_MASK)
        {
          case CLASS_PROGRAM:
            pmtehead = program_h;
            break;
          case CLASS_GLOBAL:
            pmtehead = global_h;
            break;
          case CLASS_SPECIFIC:
            pmtehead = specific_h;
            break;
        }
        if (pmtehead == pmte)
            ldrUnlinkMTE(pmte);

        /*
         * check if buffer with preload pages allocated
         */
        if ( vmac.ac_va )   /* free memory on error */
        {
#ifdef LDRSTRICT
            ldrAssert( !(VMFreeMem( vmac.ac_va, NA, NA )));
#else
            VMFreeMem( vmac.ac_va, NA, NA );
#endif
        }

        /*
         * Check if Pseudo Handle allocated
         */
        if (hobmte)
            VMFreePseudoHandle(hobmte);

        /* check if resident MTE allocated */
        if (pmte)
            VMFreeKHB( VM_HKH_PUB_RESRW, (void *)pmte );

        /* check if swappable MTE allocated */
        if (psmteheap)
            VMFreeKHB( VM_HKH_PUB_SWAPRW, (void *)psmteheap );

        /* close the file and report the error */
        ldrClose( plv->lv_sfn );
        plv->lv_sfn = SFN_NULL;
        load_error( rc, NULL );
    }
#if RAS
    else
    {
        if (fDYNActive)
            DYNAttachCheck(hobmte);         /* Check DynTrc Attachments */
    }
#endif

    return( rc );

} // end of ldrCreateMte


#pragma code_seg()
#pragma code_seg("LOAD_FTUNE1_SWP","CODE")


/***LP  ldrExpandSegment - expand segments in place
 *
 *      copy each segment table entry from pointer at psrc to pmte->mte_objtab,
 *      the region starting immediately after the space for pathname, and
 *      expand each entry by adding two bytes for the segment handle and 4 bytes
 *      for the linear address of the segment
 *
 *      ENTRY   pmte                    - pointer to mte
 *              type                    - module type
 *
 *      EXIT    none                    - segments expanded
 */

void ldrExpandSegment( ldrmte_t *pmte,   /* pointer to module table entry */
                       UCHAR     type )  /* module type */
{
    register ldrste_t *psrc;             /* pointer to source */
    register ldrste_t *pdst;             /* pointer to destination */
    register int       i;                /* count of segments */
    ldrsmte_t         *psmte;            /* pointer to swappable mte */
    ushort_t           ldrccodeseg = 0;  /* count of seg that can be packed */
    ulong_t            ldrcsegpack = 0;  /* running size of packed segs */

    psmte = pmte->mte_swapmte;
    psrc = pdst = (ldrste_t *)psmte->smte_objtab;

    (ULONG)psrc += psmte->smte_objcnt*(sizeof(ldrste_t)-sizeof(struct new_seg));

    /* loop for all STEs */
    for (i = 0; pdst != psrc; i++, ((struct new_seg *) psrc)++, pdst++)
    {
        /*
         * Make sure the following flags are cleared:
         */
        psrc->ste_flags &= ~(STE_PACKED | STE_SEMAPHORE | STE_SELALLOC |
                             STE_HUGE | STE_WAITING);

        if (type == EXT_DEVICE)
        {
            /*
             * For a device driver module.
             * If it is a first or the second segment, set the GDTSEG flag.
             * If the segment is ring 2 segment (IOPL), set the GDTSEG flag.
             */
            if (i >= 2)
            {
                if ((psrc->ste_flags & STE_SEGDPL) == STE_RING_2)
                    psrc->ste_flags |= STE_GDTSEG;
            }
            else
                psrc->ste_flags |= STE_GDTSEG;
        }

        if ((type == EXT_DEVICE) || (type == EXT_FSD))
        {
            /*
             * This is an FSD/DD module. We need to force the seg's to be
             * movable, preloaded and have DPL = 3(because the init routine
             * will run in ring 3). These segments cannot be shared, cannot
             * be conforming and cannot be discarded.
             */
            psrc->ste_flags |= (STE_PRELOAD|STE_RING_3);
            psrc->ste_flags &= ~(STE_SHARED|STE_CONFORM);
        }

        /*
         * If swapping is not on or loading from removable media
         * preloading segments, else don't.
         */
        if (!swapping || !(pmte->mte_flags1 & MTE_MEDIAFIXED))
            psrc->ste_flags |= STE_PRELOAD;
        else
            psrc->ste_flags &= ~STE_PRELOAD;

        /*
         * if readonly, force segment to be shared
         */
        if (psrc->ste_flags  & STE_ERONLY)
            psrc->ste_flags |= STE_SHARED;

        /*
         *  KDH - If this is a code segment, force it to be shared.
         */
        if ((psrc->ste_flags & STE_TYPE_MASK) == STE_CODE)
        {
            psrc->ste_flags |= STE_SHARED;
            ldrccodeseg++;
        }

        /*
         * Set Pageable bit:
         * If read-only data and fixed media, then it is pageable
         */
        if (((psrc->ste_flags & STE_DATA) &&
          !(psrc->ste_flags & STE_ERONLY)) ||
          !(pmte->mte_flags1 & MTE_MEDIAFIXED))
            psrc->ste_flags &= ~STE_PAGEABLE;
        else
            psrc->ste_flags |= STE_PAGEABLE;

        /*
         * copy ste to final destination
         */

        *(struct new_seg *)pdst = *(struct new_seg *)psrc;

        /*
         * initialize segment handle and selector to zero
         */
         pdst->ste_selector = 0;
         pdst->ste_seghdl = HOBNULL;

        /*
         * initialize segment fixup table to null
         */
         pdst->ste_fixups = HOBNULL;
    }

    /*
     * KDH - Check if we can pack the segments.  Here we want to pack
     *       segments for both EXEs and DLLs.
     */
    if (VMPackedMem && ((type == EXT_PROGRAM) || (type == EXT_LIBRARY))
          && ldrccodeseg > MINSEGPACKCNT)
    {
        pdst = (ldrste_t *) psmte->smte_objtab;
        ldrccodeseg = 0;
        for (i = 0; i < (int) psmte->smte_objcnt; i++, pdst++)
        {
            if ((pdst->ste_flags & STE_TYPE_MASK) == STE_CODE)
            {
                if (((pdst->ste_minsiz % (USHORT) PAGESIZE) <
                    (USHORT) MINPGPACKSIZE) &&
                    (pdst->ste_minsiz < (USHORT) MAXSEGPACKSIZE))
                {
                     ldrccodeseg++;
                     pdst->ste_flags |= STE_PACKED;
                     pdst->ste_flags &= ~STE_PRESENT;
                     pdst->ste_minsiz = (pdst->ste_minsiz + (USHORT) 3) &
                                        (USHORT) ~3;
                     ldrcsegpack += (ulong_t) pdst->ste_minsiz;
                }
            }
        }
        psmte->smte_csegpack = ldrccodeseg;
        psmte->smte_ssegpack = ldrcsegpack;
    }

    /*
     * zero init call gate selector value in entry table for all objects - 0
     * NOTE: no error will be returned.
     */
    ldrEachObjEntry( 0, pmte, ldrInitEntry, 0 );

} // end of ldrExpandSegment


#pragma code_seg()
#pragma code_seg("SWAP32_14","CODE")


/***LP  ldrMTEValidatePtrs - Validate pointers in MTE table and update to new
 *                           values
 *
 *      Check for any wild pointers in MTE before converting to new
 *      value.
 *
 *      ENTRY   psmte                   - pointer to swappable loader MTE
 *              limit                   - max value any ptrs may be
 *              constant                - value to update pointers by
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 */

int ldrMTEValidatePtrs( ldrsmte_t *psmte,          /* pointer to loader MTE */
                        ULONG      limit,
                        ULONG      constant )
{
    register PULONG p;
    register UINT   i;

    /*
     * do for each entry in MTE found in validatetbl
     */

    for (i = 0; validatetbl[i] != ENDMTETBL; i++)
    {
        /*
         * point to entry in exe image
         */
        (PCHAR ) p = ((PCHAR ) psmte + validatetbl[i]);

        /*
         * check if pointer valid, non-zero
         */
        if (*p != NULL)
            /*
             * range check and add constant to value at pointer
             */
            if ((*p += constant) > limit)
                return(ERROR_BAD_EXE_FORMAT);
    }
    return(NO_ERROR);

} // end of ldrMTEValidatePtrs

#pragma code_seg()
#pragma code_seg("SWAP32_13","CODE")



/***LP  ldrIsAttached - Check if process is already attached to module
 *
 *      Check if the current (or child) process is attached to
 *      each of the objects in the given module.
 *
 *      This procedure performs the following steps:
 *
 *      for each segment (object) in the module,
 *              if shared,
 *                      if (error from VMIsAttached)
 *                              return (FALSE)
 *              else (it is private)
 *                      if no handle maps the segment (object) address
 *                              retun (FALSE)
 *      return (TRUE)
 *
 *      ENTRY   pmte                    - pointer to mte to check
 *
 *      EXIT    int
 *              FALSE                   - Process is not attached to module
 *              TRUE                    - Process is attached to module
 *
 */

int ldrIsAttached( ldrmte_t *pmte )
{
    ULONG               objno;
    VMHOB               hptda;
    VMHOB               hob;
    register ldrsmte_t *psmte;
    register ldrote_t  *pote;
    register ldrste_t  *pste;

    if ( (hptda = pPTDACur->ptda_child) == 0 )
        hptda = pPTDACur->ptda_handle;
    psmte = pmte->mte_swapmte;

    if (ldrIsNE(pmte))
    {
        // No code or data - assume forwarder, so must return FALSE.
        if (psmte->smte_objcnt == psmte->smte_rsrccnt)
            return(FALSE);

        pste = (ldrste_t *)(psmte->smte_objtab);

        if (pste->ste_flags & STE_SHARED)
        {
            if ( !(pste->ste_seghdl) )
                return(FALSE);
            else
                return (!( VMIsAttached( (VMHOB)pste->ste_seghdl,
                           hptda ) ) );
        }
        else return (!( VMGetHandle( SELSelToLa( pste->ste_selector,
                                                 TKContext() ),
                                     hptda,
                                     SSToDS(&hob) ) ) );
    }

    // Module must be LX ...

    // If there are no non-resource objects, then we must
    // say we are not attached, since this may be a forwarder
    // module, and we need to process its list of static links.
    if (!(psmte->smte_objcnt))
        return(FALSE);

    for ( objno = 0,
          pote = (ldrote_t *)(psmte->smte_objtab);
          objno < psmte->smte_objcnt;
          objno++, pote++)
    {
        // skip resource objects for 32-bit modules
        if (pote->ote_flags & OBJRSRC)
            continue;

        if (pote->ote_flags & OBJSHARED)
        {
            if ( !(pote->ote_handle) )
                return(FALSE);
            else
                return (!( VMIsAttached( (VMHOB)pote->ote_handle,
                                         hptda ) ) );

        }
        else return (!( VMGetHandle( pote->ote_base,
                                     hptda,
                                     SSToDS(&hob) ) ) );

    }
    return(FALSE);

} // end of ldrIsAttached



/***LP  ldrChkLoadType - Check if module matches requested load type
 *
 *      ENTRY   lflags          - flags
 *              plv             - pointer to local variables
 *
 *      EXIT    none            - return successful or call load_error
 */

int ldrChkLoadType( ULONG             lflags,  /* module table entry flags */
                    register ldrlv_t *plv    ) /* pointer to local variables */
{
    if ( !( (plv->lv_type != EXT_PROGRAM) ^ ( !(lflags & LIBRARYMOD) ) ) )
    {
        ldrClose( plv->lv_sfn );
        plv->lv_sfn = SFN_NULL;
        load_error( ERROR_INVALID_MODULETYPE, NULL );
        return( ERROR_INVALID_MODULETYPE );
    }
    return( NO_ERROR );

} // end of ldrChkLoadType


#pragma code_seg()
#pragma code_seg("SWAP32_10","CODE")


/***LP  ldrExpandHeader - Expand 16-bit EXE header into 32-bit header
 *
 *      A module with a "NE" signature is being loaded, this 16-bit EXE header
 *      must be expanded to a 32-bit EXE header.
 *
 *      The expansion of the header is performed by zeroing the end of
 *      the 32-bit header then copying each entry in the 16-bit header
 *      to its location in the 32-bit header.  This is done starting
 *      at the end of the 16-bit header and working toward the beginning.
 *      A table of bytes is used that parallels each word in the 16-bit
 *      header.  This table contains the offset in the 32-bit header
 *      where each entry is to be moved.  To allow specific fields to
 *      not be copied the value SKIPCOPY (-1) may be placed in the
 *      corresponding entry in the table.  Each entry in the table is
 *      a byte value since all of the offsets in the 32-bit header are
 *      within 255 bytes of the beginning.  This algorithm also relies
 *      on each word being moved to an offset with a greater value than
 *      its offset in the 16-bit header, otherwise it would over write
 *      part of the 16-bit header that has not yet been copied.  When
 *      each entry is moved its source location is zeroed so that all
 *      uninitialized fields in the 32-bit header are set to zero.
 *      This allows 32-bit fields to be automatically zero extended.
 *      Also note that the table containing the offset is reversed.
 *
 *      ENTRY   pe32                    - buffer containing 16-bit exe header
 *
 *      EXIT   none                     - buffer contains a converted 16-bit
 *                                        exe header to a 32-bit exe header
 *
 *      - Zero from end of 16-bit exe header to end of 32-bit exe header.
 *      - Move entry table offset from 16-bit exe header to low word of
 *        32-bit exe header
 *      - Compute loader section size from 16-bit field by:
 *              - (16-bit entry table offset + 16-bit entry table size) -
 *              - sizeof(16-bit exe header)
 *        move into 32-bit loader section size.
 *      - Set 32-bit exe header byte ordering & word ordering
 *      - Zero 32-bit exe header module format level
 *      - Set 32-bit exe header module CPU type to 80286
 *      - Zero 32-bit exe header module OS type
 *      - Expand 16-bit flags into 32-bit flags field
 *      - Zero 32-bit exe header module version field
 *      - Move # of resource entries from 16-bit exe header to 32-bit header
 *      - Move non-resident name table offset from 16-bit to 32-bit
 *      - Move imported name table offset from 16-bit exe header and move to
 *        32-bit import procedure name table offset.
 *      - Move module reference table offset from 16-bit exe header and move
 *        to 32-bit import module table offset.
 *      - Move the resident name table offset from 16-bit to 32-bit.
 *      - Move the resource table offset from 16-bit to 32-bit.
 *      - Move the 16-bit segment table offset to the 32-bit object table offset
 *      - Move the 16-bit # of bytes in the non-resident name table to the
 *        32-bit size of non-resident name table.
 *      - Move the 16-bit # in segment table to 32-bit # of objects in module
 *      - Move low word of 16-bit ss&gml.sp to 32-bit ss object #
 *      - Move hi word of 16-bit ss&gml.sp to 32-bit ESP
 *      - Move low word of 16-bit cs&gml.ip to 32-bit ip object #
 *      - Move hi word of 16-bit cs&gml.ip to 32-bit EIP
 *      - Move the value 4k into 32-bit executable format page size
 *      - Zero 32-bit reserve_1 field
 *      - Zero the following 32-bit fields:
 *              fixup section checksum
 *              fixup section size
 */

void ldrExpandHeader( struct e32_exe *pe32 )   /* pointer to linker EXE image */
{
    register PUSHORT        psrc;
    register PUSHORT        pdst;
    USHORT                  temp;
    register USHORT         i;
    UCHAR                   newflags;


    /* Save ne_flagsothers before we hose them */
    newflags = ((struct new_exe *)pe32)->ne_flagsothers;

    /*
     * zero fill from end of 16-bit hdr to end of buffer
     */
    memset( (PCHAR ) pe32 + sizeof(struct new_exe) + 2,
            0,
            sizeof(struct e32_exe) - sizeof(struct new_exe) - 2);

    /* Starting at the end of the 16-bit header and walking toward
     * the beginning, copy each word to the 32-bit header location
     * as defined by mptbl.  Prior to storing each copied value, zero
     * the source location in the 16-bit header so they will be initialized
     * in the 32-bit header.
     */
    psrc = ((PUSHORT) ((PCHAR) pe32 + sizeof(struct new_exe)));

    for( i = 0; mptbl[i] != ENDMTETBL; i++ )
    {
        temp = *psrc;               /* fetch value from 16-bit header */
        *psrc-- = 0;                /* zero value in header */
        pdst = (PUSHORT) mptbl[i];
        if ((ULONG) pdst != (USHORT) SKIPCOPY)
        {
            (ULONG) pdst = (ULONG) pe32 + (ULONG) pdst;
            *pdst = temp;           /* set value in 32-bit header */
        }
    }

    /*
     * mask out those flags that are not used in the 32-bit header
     */
    pe32->e32_mflags &= (AUTODS_MASK | INSTLIBINIT | LDRINVALID |
                         LIBRARYMOD | E32APPMASK | NEBOUND);

    if (newflags & NELONGNAMES)
        pe32->e32_mflags |= MTELONGNAMES;

    /*
     * calculate the loader section size
     */
    pe32->e32_ldrsize += pe32->e32_enttab - sizeof(struct new_exe);

} // end of ldrExpandHeader

#pragma code_seg()


// WARNING - do NOT optimise ldrMungeFlags with the MS optimising compiler.
//           It takes out this function entirely and replaces it with a
//           mask - the replacement is, however, incorrect.
//           This is a compiler bug.
#pragma optimize("", off)

#pragma code_seg("SWAP32_9","CODE")

/***LP  ldrMungeFlags - Translate Module flags to internal flags
 *
 *      Translate the module info in the EXE header to the internal
 *      format.
 *
 *      ENTRY   pe32    Pointer to the linker EXE image
 *
 *      EXIT    NO_ERROR
 *              ERROR_BAD_EXE_FORMAT, if the module type is garbage
 */
int ldrMungeFlags( register struct e32_exe *pe32 )
{
    register int modtype;

    modtype = pe32->e32_mflags & E32MODMASK;
    pe32->e32_mflags &= ~E32MODMASK;

    switch (modtype)
    {

        case E32MODDLL:
            pe32->e32_mflags |= LIBRARYMOD;
        case E32MODEXE:
            break;

        case E32MODPDEV:
            pe32->e32_mflags |= DEVDRVMOD | LIBRARYMOD;
            break;

        // Warning - turn off the protdll bit if this is a vdd - this allows
        //           the MS and Toronto compilers to use the _loadds support
        //           to build a VDD.

        case E32MODVDEV | E32PROTDLL:
            pe32->e32_mflags &= ~MTEMODPROT;
        case E32MODVDEV:
            pe32->e32_mflags |= VDDMOD | LIBRARYMOD;
            break;

        case E32MODPROTDLL:
            pe32->e32_mflags |= MTEMODPROT | LIBRARYMOD;
            break;

        default:
            return( ERROR_BAD_EXE_FORMAT );
    }

    return( NO_ERROR );
}

#pragma optimize("", on)


#pragma code_seg()
#pragma code_seg("SWAP32_17","CODE")


/***LP  ldrCheckInternalName - Check if internal name of a library matches
 *                             its module name
 *
 *      ENTRY   pmte    pointer to its MTE
 *                      File name assumed to be in ldrFileNameBuf
 *
 *      EFFECTS The internal name is changed to Upper Case and NULL terminated
 *
 *      EXIT    NO_ERROR                name matches
 *              ERROR_INVALID_NAME      mismatch
 */
int ldrCheckInternalName( register ldrmte_t *pmte )
{
    PUCHAR          pchintname;     /* pointer to internal name */
    PCHAR           pchmodname;     /* pointer to module   name */
    USHORT          cchmodname;     /* length  of module   name */
    PCHAR           ptemp;

    pchintname = (PCHAR) &pmte->mte_modname;

    /*
     * For library modules, the internal name must match the name
     * by which the module is being loaded. We extract the name from
     * the fully qualified name in ldrFileNameBuf for comparison.
     */
    if (pmte->mte_flags1 & LIBRARYMOD)
    {
        cchmodname = (USHORT)ldrGetFileName( ldrpFileNameBuf,
                                             SSToDS(&pchmodname),
                                             SSToDS(&ptemp) );
        ldrUCaseString( pchmodname,
                        (ulong_t) cchmodname);
        //Strings match, and either
        //  a. All 8 bytes match
        //  b. Next byte in mte_modname is a NULL
        if ( !( ldrMatchStringsN( pchmodname, pchintname, cchmodname ) )
                && ((cchmodname == 8) ||
                    (pmte->mte_modname[cchmodname] == 0))
        )
            /*
             * We have a library module with internal name and the
             * file name not matching.
             */
            return(ERROR_INVALID_NAME);

    }
    return (NO_ERROR);

} // end of ldrCheckInternalName



#pragma code_seg()
#pragma code_seg("LOAD_TUNEDIT1_SWP","CODE")

/***LP  load_error - General error handler for new EXE load errors
 *
 *      ENTRY   errcode                 - load error code
 *              pmte                    - pointer to mte if it exists
 *
 *      EXIT    none
 *
 */

void load_error( int       errcode,   /* load error code */
                 ldrmte_t *pmte )     /* pointer to module table entry */
{

    ldrWriteErrTxt( errcode, pmte );

    ldrInvTgtErrTxt();

    if (pmte)
        pmte->mte_flags1 |= LDRINVALID;

    return;

} // end of load_error


#pragma code_seg()
#pragma code_seg("LDRSWAP_TEXT", "CODE")


/***EP  LDRUpdateGlobalErrTxt - similar to ldrWriteErrTxt but takes in a
 *                              MTE handle.
 *
 *      Called by Libinit to copy the module name which error'ed to the
 *      error buffer pointed to by TCB_ObjNameBuf.
 *
 *      ENTRY   hMTE    MTE Handle
 *
 *      EXIT    None
 *
 */
void LDRUpdateGlobalErrTxt( VMHOB hMTE )
{
    ldrmte_t *pmte;

    pmte = ldrpMTEFromHandle(hMTE);
    ldrWriteErrTxt(ERROR_INIT_ROUTINE_FAILED, pmte);

} // end of LDRUpdateGlobalErrorTxt



#pragma code_seg()
#pragma code_seg("LOAD_TUNEDIT1_SWP","CODE")


/***LP  ldrWriteErrTxt - write error message into user's message buffer
 *
 *      This procedure preforms the following steps:
 *
 *      - if user supplied buffer zero error message will not be setup
 *      - else copy module name into user suppiled buffer
 *      - if errcode = ERROR_PROC_NOT_FOUND copy procedure name
 *        to user buffer
 *
 *      ENTRY   errcode                 - loader error code
 *              pmte                    - pointer to module table entry
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 *                                      - else ERROR_PROTECTION_VIOLATION
 *
 *      EFFECTS error message copied into user's message buffer
 */

int ldrWriteErrTxt( int       errcode,  /* loader error code */
                    ldrmte_t *pmte )    /* pointer to module table entry */
{
    register PTCB pTCB = pTCBCur;
    int           rc = NO_ERROR;
    USHORT        cch;
    PUSHORT       pcch = SSToDS(&cch);
    PCHAR         pch;
    PCHAR         *ppch = SSToDS(&pch);
    int           fsrcmod = TRUE;
    int           fFault;         // Are we processing a page fault ?
    char          ordbuf[12];     // Buffer to translate ordinal number

    // If fault buffer exists, assume that we are in a page fault, and use
    // the fault buffer.
    if (    (pTCB->TCB_FaultBufL != 0)
         && (pTCB->TCB_FaultBuf != 0) )
    {
        // Remember that we are in a page fault
        fFault = 1;

        // Remember where to put the text and how long it can be.
        pch = pTCB->TCB_FaultBuf;
        cch = pTCB->TCB_FaultBufL;

        // Mark buffer invalid to prevent overwrites
        pTCB->TCB_FaultBufL = 0;

    }
    else
    {
        // No fault buffer set up, so assume we are not page fault processing
        fFault = FALSE;

        // Give up and get out if no user supplied buffer exists - we have
        // nowhere to copy the data to.
        if (    (pTCB->TCB_ObjNameBufL == 0)
             || (pTCB->TCB_ObjNameBuf == 0) )
            return(NO_ERROR);

        // Remember where to put the text and how long it can be
        pch = pTCB->TCB_ObjNameBuf;
        cch = pTCB->TCB_ObjNameBufL;

        // Mark buffer invalid to prevent overwrites
        pTCB->TCB_ObjNameBufL = 0;

    }

    /*
     * Check if module name setup by ldrSetupSrcErrTxt. If so use that
     * module else check the MTE passed.
     */
    if (pTCB->TCB_SrcModNameBufL != 0)
    {
        rc = ldrCopyName( (PCHAR) pTCB->TCB_SrcModNameBuf,
                          pTCB->TCB_SrcModNameBufL,
                          ppch,
                          pcch,
                          fFault );
    }

    /*
     * check if MTE pointer valid
     */
    else if (pmte)
    {
        /*
         * MTE valid so copy module name from the field pointed to
         * by mte_modname.
         */
        if (pmte->mte_modname[0] != NULL)
            rc = ldrCopyName( (PCHAR)&pmte->mte_modname,
                              8,          /* 8 is length of pmte-mte_modname */
                              ppch,
                              pcch,
                              fFault );
    }
    else fsrcmod = FALSE;

//  if (rc == NO_ERROR && pTCB->TCB_TgtModNameBufL != 0) 66879
    if (    ( rc == NO_ERROR )
         && ( errcode != ERROR_FILE_NOT_FOUND )
         && ( errcode != ERROR_VC_DISCONNECTED )         //135552
         && ( pTCB->TCB_TgtModNameBufL != 0 ) )
    {
        /*
         * First, place a '->' in the buffer to delimit the source
         * and target module names.  Protect against small buffers...
         */
        if ( fsrcmod && ( rc = ldrCopyName( "->",
                                            2,
                                            ppch,
                                            pcch,
                                            fFault)) == NO_ERROR)

            rc = ldrCopyName( (PCHAR) pTCB->TCB_TgtModNameBuf,
                              pTCB->TCB_TgtModNameBufL,
                              ppch,
                              pcch,
                              fFault );
    }

    /*
     * If errcode == ERROR_PROC_NOT_FOUND, then TCB_ProcNameBuf has
     * the procedure name. Else if errocode == ERROR_INVALID_ORDINAL
     * then TCB_ProcNameBuf has the ordinal.
     */
    if (    (    (errcode == ERROR_PROC_NOT_FOUND)
              || (errcode == ERROR_INVALID_ORDINAL) )
         && (rc == NO_ERROR) )
    {

        /*
         * First, place a '.' in the buffer to delimit the module
         * and procedure names.  Protect against small buffers...
         */
        if (cch > 1)
        {
            if (fFault)
                *pch = '.';
            else
                rc = TKSuByte('.', pch);
            pch++;
            cch--;

            /*
             * Next, save the procedure name in the buffer.
             */

            if (rc == NO_ERROR)
            {
                if (errcode == ERROR_PROC_NOT_FOUND)
                {
                    rc = ldrCopyName( pTCB->TCB_ProcNameBuf + 1,
                                      (USHORT)(pTCB->TCB_ProcNameBuf[0]),
                                      ppch,
                                      pcch,
                                      fFault );
                }
                else        /* errcode == ERROR_INVALID_ORDINAL */
                {
                    IntToDecAscii( (int) pTCB->TCB_ProcNameBuf,
                                   SSToDS(ordbuf) );
                    rc = ldrCopyName( SSToDS(ordbuf),
                                      4,
                                      ppch,
                                      pcch,
                                      fFault );
                }
            }
        }
    }

    /*
     * null terminate the message string. If buffer full, use last byte.
     */
    if (cch == 0)
        pch--;
    if (fFault)
        *pch = 0;
    else
        rc = TKSuByte( '\0', pch );

    return(rc);

} // end of ldrWriteErrorText


#pragma code_seg()
#pragma code_seg("LOAD_TUNEDIT1_SWP","CODE")


/***LP  ldrCopyName - copy ModName/ProcName to user or fault message buffer
 *
 *      ENTRY   pachsrc         - pointer to source  buffer
 *              cchsrc          - size of source buffer
 *
 *      EXIT    int             - return code (NO_ERROR if successful)
 */
int ldrCopyName( PCHAR            pachsrc,  /* pointer to source buffer */
                 register USHORT  cchsrc,   /* size of source buffer */
                 PCHAR           *ppachdst, /* pointer to pointer to destination buffer */
                 PUSHORT          pcchdst,  /* pointer to size of destination buffer */
                 int              fFault )  /* TRUE, if during Page fault */
{
    int rc;

    /*
     * check if source string > available space in user buffer
     */
    if (cchsrc > *pcchdst)
        cchsrc = *pcchdst;

    if (fFault)
    {
        // We are copying during a page fault - we therefore know that
        // we have access to the storage that we are to copy to ... it
        // was set up for us by the page manager before calling us.
        memcpy( *ppachdst, pachsrc, cchsrc );
        rc = NO_ERROR;
    }
    else
    {
        // We are copying during a call from ring 3 - we need to verify
        // that we have access to the buffer rather than just copying
        // blindly.
        rc = TKSuBuff( *ppachdst, pachsrc, cchsrc, TK_UM_FATAL );
    }

    *ppachdst += cchsrc;
    *pcchdst  -= cchsrc;

    return(rc);

} // end of ldrCopyName


#pragma code_seg()
#pragma code_seg("SWAP32_15","CODE")


/***LP  ldrGetModParams - get program module startup parameters
 *
 *      The program module startup parameters are obtained
 *      from the module's MTE header and are returned to the
 *      Exec function through the exec_info structure.  This
 *      routine gets these parameters from the MTE and validates
 *      them and places them into the exec_info structure passed
 *      on the stack.
 *
 *      This procedure performs the following steps:
 *
 *      - Validates the starting code segment number.
 *      - Stores the starting CS:IP in the exec_info structure.
 *      - Validates the initial stack segment number and
 *        checks that it is not a shared segment.
 *      - Stores the initial SS in the exec_info structure.
 *      - Stores the auto data selector in the exec_info structure.
 *      - Calculates the initial SP and stores it in the exec_info
 *        structure.
 *      - Stores the additional heap size and stack size values in
 *        the exec_info structure.
 *
 *      ENTRY   pmte                    - pointer to module table entry
 *              pei                     - pointer to exec_info structure
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 */

void ldrGetModParams( ldrmte_t          *pmte,    /* pointer to mte */
                      register ldrrei_t *pei  )   /* pointer to return buffer */
{
    ldrsmte_t       *psmte;         /* pointer to swappable mte */

    psmte = pmte->mte_swapmte;

    /*
     * see what type of module it is
     */
    if (ldrIsNE(pmte))
    {
        register ldrste_t   *pste;

        pei->ei_loadtype = LDR_16bit;

        /*
         * The start object was checked in ldrCreateMTE
         */
        pste = ldrNumToSte(pmte, psmte->smte_startobj);
        pei->ei_startaddr.ptr_off = (USHORT) psmte->smte_eip;
        pei->ei_startaddr.ptr_sel = pste->ste_selector;

        /*
         * compute stack, force word alignment.  Stack object checked in
         * ldrCreateMTE.
         */
        pste = ldrNumToSte( pmte, psmte->smte_stackobj );
        pei->ei_stackaddr.ptr_sel = pste->ste_selector;
        pei->ei_stackaddr.ptr_off = (USHORT) (psmte->smte_esp & 0x0fffe);
        pei->ei_stacksize = (USHORT)psmte->smte_stacksize;
        pei->ei_heapsize = (USHORT)psmte->smte_heapsize;

        /*
         * setup autods
         */
        if (psmte->smte_autods != 0)
        {
            pste = ldrNumToSte( pmte, psmte->smte_autods );
            pei->ei_ds = pste->ste_selector;
        }
        else
            pei->ei_ds = 0;                /* insure that value is ZERO */

        /* Zero out values used only for LDR_32bit */
        pei->ei_pstackmax.ptr_flat = 0;

    }
    else /* 32-bit module */
    {
        register ldrote_t   *pote;

        /*
         * The start object was checked in ldrCreateMTE
         */
        pote = ldrNumToOte( pmte, psmte->smte_startobj );

        /*
         * check to see what type of entry point we have in this
         * 32-bit module.
         */
        if ( pote->ote_flags & OBJBIGDEF )    /* 32-bit object */
        {
            pei->ei_loadtype = LDR_32bit;
            pei->ei_startaddr.ptr_flat = pote->ote_base + psmte->smte_eip;
            pei->ei_ds = 0;

            /*
             * The stack object was checked in ldrCreateMTE
             */
            pote = ldrNumToOte( pmte, psmte->smte_stackobj );
            pei->ei_stackaddr.ptr_flat = pote->ote_base + psmte->smte_esp;
            pei->ei_pstackmax.ptr_flat = pote->ote_base;
            pei->ei_heapsize = 0;
        }
        else                        /* 16-bit object */
        {
            pei->ei_loadtype = LDR_16bit;
            pei->ei_heapsize = (USHORT) psmte->smte_heapsize;
            pei->ei_startaddr.ptr_off = (USHORT) psmte->smte_eip;
            pei->ei_startaddr.ptr_sel = SELLaToSel( pote->ote_base,
                                                    TKContext() )
                                        | (USHORT) SEL_RPL3;

            if ((psmte->smte_autods == 0) ||
                (pote = ldrNumToOte(pmte, psmte->smte_autods)) == NULL)
                 pei->ei_ds = 0;
            else
                pei->ei_ds = SELLaToSel( pote->ote_base,
                                         TKContext() )
                             | (USHORT) SEL_RPL3;
            /*
             * The stack object was checked in ldrCreateMTE
             */
            pote = ldrNumToOte( pmte, psmte->smte_stackobj );
            // ok to call tiled as this can not be packed
            pei->ei_stackaddr.ptr_sel = LaToSelTiled( pote->ote_base +
                                                               psmte->smte_esp )
                                        | (USHORT) SEL_RPL3;
            pei->ei_stackaddr.ptr_off = (ushort_t) psmte->smte_esp;
            pei->ei_pstackmax.ptr_sel = pei->ei_stackaddr.ptr_sel;
            pei->ei_pstackmax.ptr_off = 0;

            /* Zero out ei_stacksize so tkCreateThread will pick up */
            /* correct stack offset from ei_stackaddr               */
            pei->ei_stacksize = 0;
        }

    }                               /* end 32-bit module */

    return;

} // end of ldrGetModParams
#pragma code_seg()
#pragma code_seg("LOAD_TUNE4_SWP","CODE")

/***EP  w_SetExtLIBPATH - set the ptda_pBeginLIBPATH or ptda_pEndLIBPATH
 *      New: Feature 75220
 *
 *      This routine is called by API32.ASM to service the DosSetExtLIBPATH
 *      API
 *
 *      ENTRY
 *              THUNKSEF  sef   stuff on the stack from the thunk
 *              pszExtLIBPATH   ASCIIZ name of path to be set
 *              flags           ULONG telling whether the path is for
 *                              ptda_pBeginLIBPATH or ptda_pEndLIBPATH
 *
 *      EXIT    int error code
 *              NO_ERROR, if successful
 *
 */
 /* this version is invoked through api32.asm */
int cdecl w_SetExtLIBPATH (THUNKSEF sef,PSZ pszExtLIBPATH, ULONG flags)
{
   int rc;

   rc = w_nt_SetExtLIBPATH(pszExtLIBPATH, flags);
   return (rc);

   sef;           /* remove compiler error */

}
 /* this version is called by w_SetExtLIBPATH and syiProcExtLIBPATH */
int w_nt_SetExtLIBPATH (PSZ pszExtLIBPATH, ULONG flags)
{


  #define MAXTOKENSIZE  1024       /* note: this needs to be the same */
                                   /* as MaxTokenSize in inicfg.asm   */
  int rc=0;
  PCHAR ptemp_path, temp_ptr;
  unsigned int path_index, i,
               separator_index, separator_char,
               percent_index, percent_char,
               current_end_index, path_end;


  if((!(TKProbeBuff((char *)pszExtLIBPATH,1,
      (TK_UM_READ|TK_UM_WRITE)))) && (flags == 1 || flags == 2))
  {

    if (pszExtLIBPATH[0] != '\0')
    /* if they are not setting it to a null path */
    {
      if (!( rc = VMAllocKHB( VM_HKH_PUB_RESRW,
                             (ULONG) MAXTOKENSIZE,
                             (VMHOB) LDRMTEPATHOWNER,
                             NA,
                             SSToDS(&(ptemp_path)) ) ) )
      {

        /* Validate the input path string */

        separator_char = ';';
        separator_index = 0;
        percent_char = '%';
        percent_index = 0;
        path_index = 0;
        current_end_index = 0;
        temp_ptr = pszExtLIBPATH;
        while ((separator_index != -1) && (rc == NO_ERROR))
        {
          separator_index = ldrFindChar(temp_ptr, separator_char);
          if ((separator_index != -1) ||   /* if separator was found        */
              (temp_ptr[0] != '\0'))       /* last path element doesn't have*/
                                           /*  a separator trailing         */
          {

            if (separator_index == -1)
            {
              path_end = strlen(temp_ptr);
            }
            else
            {
              path_end = separator_index;
            } /* endif */

            /* If the separator preceded the current path, treat */
            /*  it as a null path entry and skip over it.        */
            /*  Fix for trap- defect 95157                       */
            if (path_end == 0)
            {
               temp_ptr = temp_ptr + 1;
            }
            else
            {
               /* temporarily replace the separator character with an */
               /*  end of string char                                 */
               temp_ptr[path_end] = '\0';

               /* see if there are any %BeginLIBPATH% or %EndLIBPATH% */
               percent_index = ldrFindChar(temp_ptr, percent_char);
               if (percent_index == -1)
               {
                 /* normal path element, validate it */
                 if (ldrIsPath(temp_ptr))
                 {
                   if (current_end_index + path_end < MAXTOKENSIZE)
                   {
                     /* copy the validated path to the temporary path */
                     strcpy (&(ptemp_path[current_end_index]),temp_ptr);
                     /* change the current_end_index to new end */
                     current_end_index = current_end_index + path_end;
                     /* set the separator charactor at the end of the path */
                     ptemp_path[current_end_index] = ';';
                     /* increment current_end_index past the separator */
                     current_end_index++;
                     temp_ptr = temp_ptr+path_end+1;
                   }
                   else
                     rc = ERROR_BAD_PATHNAME;
                 }
                 else
                 {
                    rc = ERROR_BAD_PATHNAME;
                 }
               }
               else
               {
                 /* skip over any spaces or tabs prior to % */
                 for (i = 0; i < percent_index; i++ )
                 {
                   if (temp_ptr[i] != ' ' && temp_ptr[i] != '\t')
                   {
                     rc = ERROR_BAD_PATHNAME;
                   }
                 } /* endfor */

                 if (rc == NO_ERROR)
                 {
                   ldrUCaseString(&(temp_ptr[percent_index]), strlen(temp_ptr));
                   if (strncmp(&(temp_ptr[percent_index]),
                                "%BEGINLIBPATH%",strlen("%BEGINLIBPATH%")) == 0 &&
                               TKContext()->ptda_pBeginLIBPATH != NULL) //144009
                   {
                     i = strlen(TKContext()->ptda_pBeginLIBPATH);
                     if (current_end_index + i + 1  < MAXTOKENSIZE)
                     {
                       strcpy (&(ptemp_path[current_end_index]),
                               TKContext()->ptda_pBeginLIBPATH);
                       current_end_index = current_end_index + i;

                       /* look for garbage before separator */
                       if (percent_index + strlen("%BEGINLIBPATH%") <
                                 path_end)
                       {
                         for (i = percent_index+strlen("%BEGINLIBPATH%");
                              i < path_end; i++ )
                         {
                           if (temp_ptr[i] != ' ' && temp_ptr[i] != '\t')
                           {
                             rc = ERROR_BAD_PATHNAME;
                           }
                         }
                       } /* end if there is space between %% and separator */

                       temp_ptr = temp_ptr+path_end+1;
                     } /* end if adding the beginlibpath will fit */
                     else
                       rc = ERROR_BAD_PATHNAME;

                   } /* endif % string is %BEGINLIBPATH% */

                   else if (strncmp(&(temp_ptr[percent_index]),
                           "%ENDLIBPATH%",strlen("%ENDLIBPATH%")) == 0 &&
                           TKContext()->ptda_pEndLIBPATH != NULL) //144009
                   {
                     i = strlen(TKContext()->ptda_pEndLIBPATH);
                     if (current_end_index + i + 1 < MAXTOKENSIZE)
                     {
                       strcpy (&(ptemp_path[current_end_index]),
                               TKContext()->ptda_pEndLIBPATH);
                       current_end_index = current_end_index + i;

                       /* look for garbage before separator */
                       if (percent_index + strlen("%ENDLIBPATH%") <
                                 path_end)
                       {
                         for (i = percent_index+strlen("%ENDLIBPATH%");
                              i < path_end; i++ )
                         {
                           if (temp_ptr[i] != ' ' && temp_ptr[i] != '\t')
                           {
                             rc = ERROR_BAD_PATHNAME;
                           }
                         }
                       } /* end if there is space between %% and separator */

                       temp_ptr = temp_ptr+path_end+1;

                     } /* end if adding the endlibpath will fit */
                     else
                       rc = ERROR_BAD_PATHNAME;
                   } /* endif % string is %ENDLIBPATH% */

                   else         /* % operator not valid */
                     rc = ERROR_BAD_PATHNAME;
                 }
                 else; /* rc set to failure, continue */

               } /* endif percent was found */
            } /* endif */

          } /* endif separator_char is != -1 or temp_ptr is not null */

        } /* endwhile */
      } /* end if vmallockhb worked */
      else; /* rc set to error, continue */

      /* make sure the string is null terminated */
      ptemp_path[current_end_index] = '\0';

      /* allocate the new memory with the proper size */
      if (!( rc = VMAllocKHB( VM_HKH_PUB_RESRW,
                             (ULONG) current_end_index+1,
                             (VMHOB) LDRMTEPATHOWNER,
                             NA,
                             SSToDS(&(temp_ptr)) ) ) )
      {
        /* copy the processed path into the official memory area */
        strcpy(temp_ptr,ptemp_path);
      }
      else; /* RC from VMAllocKHB set, continue */

      /* free up the temporary memory area */
      VMFreeKHB(VM_HKH_PUB_RESRW,ptemp_path);
    }
    else
    {
      temp_ptr = NULL;
    } /* endif */


    if (rc == NO_ERROR)
    {
      if ( rc = TKTakeKernelSem() )
      {
          return(rc);
      }
      if (flags == 1)
      {
        ptemp_path = TKContext()->ptda_pBeginLIBPATH;
        TKContext()->ptda_pBeginLIBPATH= temp_ptr;
      }
      else /* must be 2 */
      {
        ptemp_path = TKContext()->ptda_pEndLIBPATH;
        TKContext()->ptda_pEndLIBPATH= temp_ptr;
      } /* endif */
      TKClearKernelSem();

      /* if there was memory previously allocated, free it */
      if (ptemp_path)
      {
        VMFreeKHB(VM_HKH_PUB_RESRW,ptemp_path);
      }
    }  /* end if no error, set libpath variable */
  } /* end if probe on input parameter ok */
  else
  {
     rc = ERROR_INVALID_PARAMETER;
  } /* endif */

  return(rc);

}

/***EP  w_QueryExtLIBPATH - return the ptda_pBeginLIBPATH or ptda_pEndLIBPATH
 *      New: Feature 75220
 *
 *      This routine is called by API32.ASM to service the DosQueryExtLIBPATH
 *      API
 *
 *      ENTRY
 *              THUNKSEF  sef   stuff on the stack from the thunk
 *              pszExtLIBPATH   ASCIIZ pointer where path should be put
 *              flags           ULONG telling which path :
 *                              ptda_pBeginLIBPATH or ptda_pEndLIBPATH
 *
 *      EXIT    int error code
 *              NO_ERROR, if successful
 *
 */
int cdecl w_QueryExtLIBPATH (THUNKSEF sef,PSZ pszExtLIBPATH, ULONG flags)
{
  int rc;

// temp code


  if ( rc = TKTakeKernelSem() )
  {
      return(rc);
  }
  if (flags == 1)
  {
    if (TKContext()->ptda_pBeginLIBPATH != NULL)
    {
       strcpy(pszExtLIBPATH, TKContext()->ptda_pBeginLIBPATH);
    }
    else
    {
       *pszExtLIBPATH = NULL;
    } /* endif */
  }
  else if (flags == 2)
  {
    if (TKContext()->ptda_pEndLIBPATH != NULL)
    {
       strcpy(pszExtLIBPATH, TKContext()->ptda_pEndLIBPATH);
    }
    else
    {
       *pszExtLIBPATH = NULL;
    } /* endif */
  } /* endif */
  TKClearKernelSem();
  return(rc);
  sef;           /* remove compiler error */

}


void ldrFreeMTEFixups(pmte, flag)
ldrmte_t *pmte;
int flag;
{
    ldrsmte_t *psmte;            /* pointer to swappable mte */
    ULONG      csmte;                   // number of bytes saved
    PCHAR  pold;
    PCHAR  pnew;
    ULONG  cmove;
    ULONG  i,c;
    ULONG  ulsave;
    PULONG pul;
    ULONG              lobjnum;
    uchar_t           *pfrt;
    uchar_t           *pfrtend;
    ldrote_t  *pote;           // pointer to object table entry

            psmte = pmte->mte_swapmte;

   if (flag == 1)  {
      for ( lobjnum = 0, pote = (ldrote_t *) psmte->smte_objtab;
            lobjnum < psmte->smte_objcnt;
            lobjnum++, pote++ )
      {

    c = (pote->ote_pagemap-1);
    pfrt = (uchar_t *) (psmte->smte_frectab +
                                ((ulong_t *) psmte->smte_fpagetab)[c]);

    c += pote->ote_mapsize;

    pfrtend = (uchar_t *) (psmte->smte_frectab +
                              ((ulong_t *) psmte->smte_fpagetab)[c]);



//       printf("pote=%x flags=%04x %x %x %x\n",pote,pote->ote_flags,
//                     !( pote->ote_flags & (OBJEXEC | OBJSHARED)),
//                     pfrt,pfrtend
//                     );

        if (  (!( pote->ote_flags & (OBJEXEC | OBJSHARED)))  &&
               pfrt != pfrtend) {
//             printf("found bad dll\n");
               return;
        }
//       for (i=(pote->ote_pagemap-1);i<=c ;i++ ) {
//              pfrt = (uchar_t *) (psmte->smte_frectab +
//                              ((ulong_t *) psmte->smte_fpagetab)[i]);
//              printf("%3x  %x\n",i,pfrt);
//       } /* endfor */
      }
   }

            pul = (PULONG)pmte->mte_swapmte;
            pul -= 2;

            csmte = *pul & 0xffffff;
            csmte -= 8;

            if (flag == 0)  pnew = (PCHAR)psmte->smte_fpagetab;
            else            pnew = (PCHAR)psmte->smte_frectab;

            pold = (PCHAR)psmte->smte_impmod;

//            ul    = (ULONG)pold - (ULONG)psmte;
            cmove = csmte -   ( (ULONG)pold - (ULONG)psmte );

            ulsave= (ULONG)pold - (ULONG)pnew;

//            printf("ulsave=%x\n",ulsave);

            for (i=0; i<cmove ; i++) {
                 *pnew++ = *pold++;
            }

            if (VMReallocKHB( VM_HKH_PUB_SWAPRW,
                               &(pmte->mte_swapmte),
                              csmte - ulsave,
                              VMAC_ALIGNPAGE ) == NO_ERROR) {
                psmte->smte_impmod    -= ulsave;
                psmte->smte_impproc   -= ulsave;
                psmte->smte_fixupsize  = 0;
            }
    return;
}



/*
 *  End of swappable code
 */

#pragma code_seg()


/*
 *  Start of init code
 */
#pragma code_seg("CINIT_TEXT", "CODE")




/***EP  LDRLoadVDD - load a VDD
 *
 *      This routine is called by the VDM to load a VDD and
 *      its referenced library modules.
 *
 *
 *      ENTRY   pachModname             - pointer to ASCII module name
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 */

int LDRLoadVDD( PCHAR pachModname )      /* pointer to ASCII module name */
{
    ldrmte_t        *pmte;          /* pointer to loaded MTE */
    USHORT          cchModname;     /* module name length */
    int             rc;

    /*
     * Compute VDD name string length and validate it.
     */
    if ( rc = TKFuBuffLenz( SSToDS(&cchModname),
                            pachModname,
                            MAXPATHLEN,
                            0) )
        return(rc);

    if ( rc = LDRGetSem() )
        return(rc);

    /* Setup error handling information */
    ldrSetupTgtErrTxt( pachModname, cchModname );

    pmte = 0L;
    rc = (ldrIsPath(pachModname)) ? CLASS_SPECIFIC
                                  : CLASS_GLOBAL;
    rc = ldrGetModule( pachModname,

// compiler bug        ((ulong_t)cchModname<<16) | (EXT_VDD<8) | ((uchar_t)rc),

                       ((ulong_t)cchModname<<16) | 0x0400 | ((uchar_t)rc),
                       SSToDS(&pmte) );
    ldrSetLoaded();

    if (rc)
    {
        ldrDoGCollect = 1;
    }
    else
    {
        pTCBCur->TCBCheckedSig = 0;

        // Force VDDs to be class global, not class specific.
        if ((pmte->mte_flags1 & CLASS_MASK) == CLASS_SPECIFIC)
        {
            ldrPromoteMTE( pmte );
        }

        pTCBCur->TCB_ObjNameBufL = 0;   /* Invalidate module name buffer */

        // Add to the global DLD record so that GC sees it as owned
        ldrIncDLDRecord( pmte->mte_handle, NULL );

        ldrClose(pmte->mte_sfn);
        pmte->mte_sfn = SFN_NULL;
    }

    LDRClearSem();

    return (rc);

} // end of LDRLoadVDD


/***EP  LDRUnloadVDD - unload a VDD
 *
 *      This routine is called by MVDM to unload a VDD
 *
 *      ENTRY
 *              pachname        ASCIIZ name of VDD to unload
 *
 *      EXIT    int error code
 *              NO_ERROR, if successful
 *
 */
int LDRUnloadVDD( register VMHOB hobmte )
{
    int             rc;

    if ( rc = LDRGetSem() )
        return(rc);

#ifdef LDRSTRICT
    if ( !(ldrValidateMteHandle(hobmte)) )
        return( ERROR_INVALID_HANDLE );
#endif

    ldrDecDLDRecord(hobmte, NULL, TRUE);

    ldrDoGCollect = TRUE;
    LDRClearSem();

    // Notify VDMM - we need to call VDMM back or VDMM will panic
    VDMMUnloadingVDD(hobmte);

    return( rc );

} // end of ldrUnloadVDD

#pragma code_seg()
