#define SCCSID  "@(#)ldrsubr.c  6.47 92/03/29"

/***    OS/2 Loader subroutines
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 *      This file contains low level routines for the loading of
 *      new .EXE file formats.
 *
 *  Modifications:
 *  Defect  Date     Author/Description
 *   77939  02/09/94 Catherine E. Bundy - Changed LDRFreeModule
 *                   to check if force is set before freeing device
 *                   driver.
 *
 *   85346  11/15/94 Change Team - Change #define in LDRTstnClrSem()
 *                   from 0 to 1.
 *
 *  133454  10/24/95 Change Team - Regression. Commented out line 
 *                   that was unnecessarily resetting error code.
 *
 *  137076  12/08/95 Change Team - change clean-up of nonresident names table
 *                   to be cleaned-up during garbage collection.
 *
 *  147968  05/06/96 Richard Moore - Removed FSD and DD owner limits.
 *
 *  158243  06/24/96 Brenda Acosta - Add condition to free mini IFS after 
 *                                   it returns from MFSH_TERM
 *
 */

#define INCL_TYPES              // for basedef.h typedefs
#define INCL_ERROR_H            // for bseerr.h
#define INCL_ERROR2_H           // for bseerr.h

// BUGBUG ... from TKLOCAL.H ... pray no one changes TKLOCAL.H ...
#define TK_TYPEDT 2

#include <basemac.h>
#include <basedef.h>
#include <osconst.h>
#include <ostypes.h>

#define FNO_NUM FNO_LDRSUBR_C
#include <fnomem.h>
#include <osmac.h>
#include <bseerr.h>
#include <dossymc.h>
#include <mi.h>
#include <sigdef.h>
#include <except.h>
#include <pgexport.h>
#include <vmexport.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>
#include <ldrxport.h>
#include <setjmp.h>
#define FOR_EXEHDR      1
#include <newexe.h>
#include <exe386.h>
#include <kmode.h>                      /* Must be before ldrvars.h */
#include <ldrvars.h>
#include <selxport.h>
#include <seldesc.h>
#include <dynapi.h>
#include <dynxport.h>
#include <dynlocal.h>
#include <vmowner.h>
#include <dbgex.h>
#include <string.h>
#include <sft.h>
#include <ioexp.h>

extern UCHAR DDInitTime;

struct lgcs_s
{
    VMHOB     lgcs_hmte;      // MTE handle
    PPTDA     lgcs_pptda;     // PTDA pointer using hmte
};
typedef struct lgcs_s lgcs_t;

struct lfrm_s
{
    VMHOB   lfrm_hmte;      // MTE handle
    ulong_t lfrm_base;      // object base address
    ulong_t lfrm_size;      // object size
};
typedef struct lfrm_s lfrm_t;

int ldrForceResModule( UCHAR *pszmodname,
                       int cbnamelen );

int ldrSetupResource( VMHOB hob,
                      ldrmte_t **ppmte );

int ldrFindObject( VMHOB hobmte,
                   VMHOB hob,
                   ldrmte_t **ppmte,
                   ldrste_t **ppste,
                   int *pcount );

int _stdcall ldrGetContextSub( PPTDA pptda,
                               lgcs_t *plgcs );

void _stdcall LDRINITSetGDT_DPL0( VMHOB hobmte );

int cdecl Ldr32DirtyWorker( ulong_t Command, void *pData );

extern int ldrEnumDLLTerm( dllterm_t **ppdt );

extern int ldrSegFixAddRecord( ldrste_t *,
                               ulong_t,
                               ulong_t,
                               uchar_t *,
                               ulong_t );

extern int tkAddExitListRoutine( ptr_t,
                                 USHORT,
                                 USHORT );

extern int tkDeleteExitListRoutine();

extern UCHAR IsMiniIFS;
#if RAS
extern ushort_t RASselSTDA;
#endif /* RAS */

extern UCHAR *LDRLibPath;
extern ulong_t LDRLibPathLen;
extern ushort_t BasicHWConfig;




/***LD  pfnDLLTermDisp32 - address of ring 3 flat DLL termination thunk
 *
 *      This variable contains the address of the ring 3 routine
 *      that calls back into the kernel after a DLL termination
 *      routine has finished executing.
 */

PFN pfnDLLTermDisp32 = 0;




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



/***LP  ldrEachObjEntry - scan entry table entries for given object(s)
 *
 *      Scan the entry table entries for the matching object and
 *      call the worker routine for processing at each entry.
 *
 *      The worker routines are:
 *      ldrEditProlog
 *      ldrGetCallGate
 *      ldrInitEntry
 *      ldrFreeLDTGate
 *      ldrFreeCallGate
 *
 *      ENTRY   objnum                  - number of object to search for
 *                                        0 - match all objects
 *              pmte                    - pointer to module table entry
 *              pworker                 - pointer to worker routine
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 *
 *      This procedure performs the following steps
 *
 *      - For each entry in the entry table
 *      -   if input object number equal 0
 *      -        call worker routine
 *      -   else if input object number equal to entry table entry object
 *      -        call worker routine
 */

int ldrEachObjEntry( ULONG                   objnum,       /* object number to search on */
                     register ldrmte_t       *pmte,        /* pointer to object table entry */
                     int     (*pworker)(LDREACHOBJWPARMS), /* pointer to worker routine */
                     ldrlv_t                 *plv )        /* pointer to local vars */
{
    register ldret_t   *pet;       /* pointer to entry table */
    register ldrsmte_t *psmte;     /* pointer to swappable mte */
    USHORT              etobj;     /* object number from entry table */
    ULONG               pentry;    /* pointer to entry table entry */
    ULONG               i;
    int                 rc;

    psmte = pmte->mte_swapmte;

    /*
     * does module contain an entry table
     */
    if ((pet = (ldret_t *) (psmte->smte_enttab)) == 0)
        return(NO_ERROR);

    while (TRUE)
    {
        if (pet->b32_cnt == 0)              /* check for end of table */
            return(NO_ERROR);

        if (pet->b32_type == EMPTY)         /* check for empty bundle */
        {
            (ULONG) pet += sizeof(ldrempty_t); /* Skip empty bundle    */
            continue;                          /* and start loop again */
        }

        if (ldrIsNE(pmte))                  /* check for 16-bit module */
        {   /*
             * skip over count and type fields
             */
            pentry = (ULONG) pet + sizeof(ldrempty_t);

            /*
             * call routine for each entry
             */
            for ( i = 1; i <= (USHORT) pet->b32_cnt; i++ )
            {
                if (pet->b32_type == 0xff)
                    etobj = (USHORT) (((ldrcte_t *) pentry)->cte_obj);
                else
                    etobj = pet->b32_type;

                if ((objnum == 0) || (objnum == (ULONG) etobj))
                {   /*
                     * call worker routine
                     */
                    if ( rc = pworker( pet, (PULONG) pentry, pmte, plv ) )
                        return(rc);
                }
                if (pet->b32_type == 0xff)
                    pentry += sizeof(ldrcte_t);
                else
                    pentry += sizeof(ldret16_t);
            }
            (ULONG) pet += pentry - (ULONG) pet;
        }

        else                                /* must be 32-bit module */
        {
            if (pet->b32_type == GATE16)    /* only process 16-bit callgates */
            {
                /*
                 * call work routine
                 */
                if ((objnum == 0) || (objnum == (ULONG) pet->b32_obj))
                {
                    /*
                     * point to first entry of bundle (skip bundle info)
                     */
                    pentry = (ULONG) pet + sizeof(ldret_t);

                    /* call routine for each entry */
                    for (i = 1; i <= pet->b32_cnt; i++)
                    {
                        if ((rc = pworker(pet, (PULONG) pentry, pmte, 0))
                                != NO_ERROR)
                            return(rc);
                       #if 0
                        pentry += ldrNextEntry(pmte, pet);
                       #else
                        /* We know it's LX and b32_type == GATE16 */
                        pentry += sizeof(ldret16iopl_t);
                       #endif
                    }
                    (ULONG) pet += pentry - (ULONG) pet;
                }
                else                                /* skip this bundle */
                {
                    (ULONG) pet += ldrSkipEnts( 0,  // known to be LX
                                                pet->b32_type,
                                                pet->b32_cnt);
                }
            }
            else                            /* skip this bundle */
            {
               (ULONG) pet += ldrSkipEnts( 0,   // known to be LX
                                           pet->b32_type,
                                           pet->b32_cnt);
            }

        } // end of is LX

        TKTCYield();
    }
    return(NO_ERROR);
}


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

/***LP  ldrNextEntry - increment entry table pointer to next entry
 *
 *      ENTRY   pmte                    - pointer to module table entry
 *              pet                     - pointer to entry table
 *
 *      EXIT    ULONG           - count to increment by
 *
 */

ULONG ldrNextEntry( ldrmte_t  *pmte,  /* pointer to a module table entry */
                    ldret_t   *pet )  /* pointer to entry table */
{

    /*
     * move to next entry
     */
    if (ldrIsNE(pmte))
    {
        if (pet->b32_type == 0xff)
            return(sizeof(ldrcte_t));
        else
            return(sizeof(ldrent_t));
    }
    else
    {
        switch (pet->b32_type & ~TYPEINFO)
        {
          case ENTRY16:
            return(sizeof(ldret16_t));
          case GATE16:
            return(sizeof(ldret16iopl_t));
          case ENTRY32:
            return(sizeof(ldret32_t));
        }
    }

#ifdef LDRSTRICT
    panic("ldrNextEntry");
#endif

}
#endif


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

/***LP  ldrEditProLog - Check and edit an entry point's prelog.
 *
 *      ENTRY   pet                     - pointer to entry table bundle
 *              pentry                  - pointer to entry table entry
 *              pmte                    - pointer to module table entry
 *              plv                     - pointer to local vars
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 *
 */
int ldrEditProLog( ldret_t  *pet,     /* pointer to entry table bundle */
                   PULONG    pentry,  /* pointer to entry table entry */
                   ldrmte_t *pmte,    /* pointer to module table entry */
                   ldrlv_t  *plv )    /* pointer to local vars */
{
    ldrsmte_t    *psmte;         /* pointer to swappable mte */
    UCHAR         flags;
    USHORT        offset;
    ldrste_t     *pste;
    SEL           selautods;
    ljmp_t        lj;
    uchar_t      *temp;

#define EF_EXPORT       1
#define EF_GDATA        2

    psmte = pmte->mte_swapmte;

    flags = ((ldret16_t *) pentry)->et16_flags;

    if (!(flags & EF_EXPORT) || (pmte->mte_flags1 & LIBRARYMOD &&
      !(flags & EF_GDATA)))
        return(NO_ERROR);

    if (pet->b32_type == 0xff)
        offset = ((ldrcte_t *) pentry)->cte_off;
    else
        offset = ((ldret16_t *) pentry)->et16_off;

    /*
     * The fields lv_type and lv_class are reused here, lv_type is used to
     * tell if ldrSegment or ldrLoadSegment is the calling routine.  If
     * ldrSegment is the calling routine (lv_type == 0xff) we loading
     * a page of a 16-bit module and only need to do edit prologs of the
     * faulting page. The faulting page can but found in the lv_class field.
     */

    if (plv->lv_type == 0xff)
    {
        if (plv->lv_class != (offset >>PAGESHIFT))
            return(NO_ERROR);
        /*
         * If we are loading a single page, then we are using the
         * buffer allocated by the PM. Therefore, we need to adjust
         * the offset to yield the page offset, instead of the
         * segment offset.
         */

        offset &= PAGEMASK;
    }

    /*
     * check for auto ds, if none don't edit prolog
     */
    if ( (pste = ldrNumToSte(pmte, psmte->smte_autods)) == 0 )
        return(NO_ERROR);

    selautods = pste->ste_selector;

    if (TKCatchFault(SSToDS(&lj)) != NO_ERROR)
    {
        ldrStop(0,0);
        return(ERROR_BAD_EXE_FORMAT);
    }

    (ulong_t) temp = plv->lv_lbufaddr + (ulong_t) offset;

    /*
     * is the instruction PUSH DS op code 0x1e, POP AX op code 0x58 or
     * MOV AX,DS; op code 0x0d88c
     */
    if (    temp[0] == 0x01e && temp[1] == 0x058
         || temp[0] == 0x08c && temp[1] == 0x0d8 )
    {
        /*
         * And the next instruction is a NOP op code 0x90,
         * edit the instruction to: MOV AX,autods
         */
        if (temp[2] == 0x090)
        {
            temp[0] = 0x0b8;

            *(ushort_t*)(&temp[1]) = selautods;
            /*
             * Get the pste for the object being loaded
             */
            pste =& ((ldrste_t *)psmte->smte_objtab)[plv->lv_objnum-1];

            if (pste->ste_fixups)
            {
                ldrSegFixAddRecord(pste,offset,3,temp,0);
            }

        }
    }

    TKClrFault();

    return(NO_ERROR);
}

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

/***EP  LDRNumtoAddr - Get linear address for a given object number
 *
 *      Given an object number first check if the object is in the range
 *      of object numbers for this module.  Than return the linear address
 *      of this object to the caller.
 *
 *      ENTRY   hobmte          - mte handle
 *              lobjnum         - object number
 *              paddr           - on entry: pointer to ptda of target
 *                                on exit: pointer to Linear addr of obj
 *
 *      EXIT    int             - return code (NO_ERROR if successful)
 *
 *
 *   This procedure performs the following steps:
 *
 *      - Check range of number passed to routine
 *      - return error if not in exe range
 *      - Search objects looking for match
 *      - return linear address
 */

int LDRNumtoAddr( VMHOB   hobmte,
                  ULONG   lobjnum,
                  PULONG  paddr )
{
    register ldrmte_t   *pmte;          /* pointer to module table entry */
    register ldrsmte_t  *psmte;         /* pointer to swappable mte */

    // see if handle exists
    if ( !(pmte = ldrValidateMteHandle(hobmte) ) )
        return( ERROR_INVALID_HANDLE );

    psmte = pmte->mte_swapmte;

    if ((lobjnum < 1) || (lobjnum > psmte->smte_objcnt))
        return(ERROR_INVALID_PARAMETER);

    if (ldrIsNE(pmte))
    {
        /*
         * convert selector to 0:32 address
         */
        register SEL sel;

        sel = ((ldrste_t *)psmte->smte_objtab)[--lobjnum].ste_selector;

        if (sel & SEL_LDTBIT)
            *paddr = SELSelToLa((ulong_t)sel, (struct ptda_s *)*paddr);

        else if (sel == 0 || ((*paddr = SELVirtToLin (0, sel)) == (ULONG) -1L))
            return(ERROR_INVALID_ADDRESS);
    }
    else
    {
        /*
         * just return base address of object
         */
        *paddr = ((ldrote_t *)psmte->smte_objtab)[--lobjnum].ote_base;
    }

    return(NO_ERROR);
}


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

/***EP  LDRClearSem - Clear the loader semaphore and wakeup waiting procs
 *
 *      Release semaphore if no garbage collection to do.
 *      If garbage collection is pending, hold semaphore till after garbage
 *      collection is over.
 *
 *      ENTRY   None
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 *                                        or ERROR_INTERRUPT
 *
 *      EFFECTS                         - semaphore cleared
 */

void LDRClearSem()
{
    USHORT cnest;

#ifdef LDRSTRICT

    register ldrmte_t *pmte;
    register int       rc;

    rc = KSEMQueryMutex( (HKSEM)&LdrSem, SSToDS(&cnest) );
    ldrAssert(rc == TRUE && cnest != 0);

#else

    KSEMQueryMutex( (HKSEM)&LdrSem, SSToDS(&cnest) );

#endif

    if ( cnest > 1 || !ldrDoGCollect )
    {
        goto ClearSem_doit;
    }

#ifdef LDRSTRICT
    for (pmte = mte_h; pmte != NULL; pmte = (ldrmte_t *)pmte->mte_link)
    {
        if (pmte->mte_flags1 & USED)
            panic("ldrClearSem: USED");
    }
#endif

    ldrGarbageCollect();

ClearSem_doit:
    KSEMReleaseMutex( (HKSEM)&LdrSem );
    return;

}


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

/***EP  LDRTstnSetSem - Take the semaphore if it is free.
 *
 *      If the loader sempahore is either free or owned by the current thread
 *      it is grabbed. Else it is not available.
 *
 *      ENTRY   None
 *
 *      EXIT    - TRUE  if the semaphore is FREE or OWNED
 *              - FALSE if the semaphore is BUSY or ERROR_INTERRUPT occurs
 */

int LDRTstnSetSem()
{

    register int rc;

    if ( KSEMQueryMutex( (HKSEM)&LdrSem, NULL ) )
    {
        // Owned by this process, so increment nest count

        if (!( rc = KSEMRequestMutex( (HKSEM)&LdrSem, -1 ) ))
            return( TRUE );

        ldrAssert( rc == ERROR_INTERRUPT );
        return( FALSE );
    }

    // Owned by a different process - return fail
    return( FALSE );

}


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

/***EP  LDRTstnClrSem - Clear the semaphore if it is owned.
 *
 *      If the loader sempahore is owned by the current thread
 *      it is freed.
 *
 *      ENTRY   None
 *      EXIT    Semaphore freed if owned.
 */

//85346 #if 0
#if 1

void LDRTstnClrSem()
{
    USHORT  cnest;

    if ( KSEMQueryMutex( (HKSEM)&LdrSem, SSToDS(&cnest) ) )
    {
        if ( cnest )
            KSEMReleaseMutex( (HKSEM)&LdrSem );
    }
}

#endif


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

/***EP LDRGetTKModuleInfo -  Get info needed by exit list and libinit from MTE
 *
 *      Given a MTE handle, return in the buffer supplied info from the
 *      MTE needed.
 *
 *
 *      ENTRY   hobmte -        MTE handle
 *              pdata -         pointer to data space to place info to return
 *                              which contains:
 *                              load_type       - 16-bit or 32-bit
 *                              attrib          - type of module
 *                              startaddr       - start address, if any
 *                              stack_ptr       - 16:32 address
 *                              stack_size      - size of stack object
 *                              auto_ds         - automatic data segment
 *                              heapsize        - heap size
 *
 *      EXIT    int             return code (NO_ERROR if successful)
 *                              pdata contains info
 *                              error if MTE handle not valid
 *
 *      NOTES: This routine is called by:
 *              ExitList to get info about Program modules (EXEs)
 *              Libinit to get info LIBRARIES
 *              MVDM to get info about VDDs
 *          In all these cases the module should have a start address.
 */

int LDRGetTKModuleInfo( VMHOB             hobmte, /* handle to a module table entry */
                        register ldrmi_t *pdata ) /* pointer to return info */
{
    register ldrmte_t   *pmte;  /* pointer to a module table entry */
    register ldrsmte_t  *psmte; /* pointer to swappable mte */

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

    // map handle -assume valid for retail
    if ( !(pmte = ldrValidateMteHandle(hobmte) ) )
        return( ERROR_INVALID_HANDLE );
#else
    pmte = ldrpMTEFromHandle(hobmte);
#endif

    psmte = pmte->mte_swapmte;

    if (pmte->mte_flags1 & LIBRARYMOD)
    {
        pdata->ldrmi_attrib = LDRLIBRARY;

        if ( !(pmte->mte_flags1 & MTEPORTHOLE) )
            if (psmte->smte_startobj == 0)
                return(ERROR_INIT_ROUTINE_FAILED);

        if (pmte->mte_flags1 & INSTLIBINIT)
            pdata->ldrmi_attrib |= LDRLIBINIT;

        if (pmte->mte_flags1 & MTEDLLTERM)
            pdata->ldrmi_attrib |= LDRDLLTERM;

        if (pmte->mte_flags1 & GINISETUP)
            pdata->ldrmi_attrib |= LDRGLOBALSETUP;

        if (pmte->mte_flags1 & GINIDONE)
            pdata->ldrmi_attrib |= LDRGLOBALDONE;

        // See if this is PortHole MTE, if it is set flag to indicate
        // to Libinit that it is and return entry point to porthole init.
        if (    (pPortHoleMte)
             && (pmte->mte_flags1 & MTEPORTHOLE) )
        {
            pdata->ldrmi_phflag = LDR_PORTHOLE;
            pdata->ldrmi_phaddr.ptr_off = (ushort_t) pPortHoleMte->mte_swapmte->smte_eip;
            pdata->ldrmi_phaddr.ptr_sel = (ushort_t) (pPortHoleMte->mte_swapmte->smte_eip >> 16);
            pdata->ldrmi_attrib |= LDRLIBINIT;
        }
        else
        {
            // Clear porthole flag, no porthole init needed.
            pdata->ldrmi_phflag = 0;
        }
    }

    else if (pmte->mte_flags1 & DEVDRVMOD)
         pdata->ldrmi_attrib = LDRDEVICE;

    else if (pmte->mte_flags1 & (FSDMOD | FSHMOD))
         pdata->ldrmi_attrib = LDRFSD;

    else pdata->ldrmi_attrib = LDRPROGRAM;

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

        pdata->ldrmi_ldtype = LDR_16bit;

        // Get start address
        if (pmte->mte_flags1 & MTEPORTHOLE && psmte->smte_startobj == 0)
        {
            pdata->ldrmi_startaddr.ptr_flat = 0;
        }

        else
        {
            if ( (pste = ldrNumToSte( pmte, psmte->smte_startobj )) == NULL )
                return(ERROR_INVALID_STARTING_CODESEG);

            pdata->ldrmi_startaddr.ptr_off = (USHORT) psmte->smte_eip;
            pdata->ldrmi_startaddr.ptr_sel = pste->ste_selector;
        }

        // Get starting stack address, if it exists.
        if (psmte->smte_stackobj == 0)
        {
            pdata->ldrmi_stackptr.ptr_flat = 0;
        }
        else
        {
            if ( (pste = ldrNumToSte( pmte, psmte->smte_stackobj )) == 0 )
                return(ERROR_INVALID_STACKSEG);

            pdata->ldrmi_stackptr.ptr_off = (USHORT) psmte->smte_esp;
            pdata->ldrmi_stackptr.ptr_sel = pste->ste_selector;
        }

        // Get auto-ds
        if (   (psmte->smte_autods == 0)
            || (pste = ldrNumToSte( pmte, psmte->smte_autods ) ) == NULL)
        {
             pdata->ldrmi_autods = 0;
        }
        else
        {
            pdata->ldrmi_autods = pste->ste_selector;
        }

        // Get heap size
        pdata->ldrmi_heap = (USHORT) psmte->smte_heapsize;
    }
    else
    {
        register ldrote_t *potecs;        /* Pointer to code object */
        ldrote_t          *pote;          /* Pointer to stack/autods object */

        pdata->ldrmi_ldtype = LDR_32bit;

        // Get start address
        if ((potecs = ldrNumToOte(pmte, psmte->smte_startobj)) == NULL)
            return(ERROR_INVALID_STARTING_CODESEG);

        if (psmte->smte_stackobj == 0)
        {
            pdata->ldrmi_stackptr.ptr_flat = pdata->ldrmi_stackbase = NULL;
        }
        else
        {
            if ((pote = ldrNumToOte(pmte, psmte->smte_stackobj)) == 0)
                return(ERROR_INVALID_STACKSEG);

            pdata->ldrmi_stackbase = pote->ote_base;

            if (potecs->ote_flags & OBJBIGDEF)
            {
                pdata->ldrmi_stackptr.ptr_flat = pote->ote_base + psmte->smte_esp;
            }
            else
            {
                // ok to call tiled - can not be packed
                pdata->ldrmi_stackptr.ptr_sel = LaToSelTiled(pote->ote_base) | (SEL) SEL_RPL3;
                pdata->ldrmi_stackptr.ptr_off = (ushort_t) psmte->smte_esp;
            }
        }

        // check if start object a 16-bit object or 32-bit object
        if (potecs->ote_flags & OBJBIGDEF)          /* 32-bit object */
        {
            pdata->ldrmi_heap = 0;
            pdata->ldrmi_startaddr.ptr_flat = potecs->ote_base + psmte->smte_eip;
            pdata->ldrmi_autods = 0;
        }
        else                                /* 16-bit object */
        {
            pdata->ldrmi_ldtype = LDR_16bit;
            pdata->ldrmi_heap = (USHORT) psmte->smte_heapsize;
            pdata->ldrmi_startaddr.ptr_off = (USHORT) psmte->smte_eip;
            pdata->ldrmi_startaddr.ptr_sel = SELLaToSel( potecs->ote_base,
                                                         pPTDACur )
                                             | (SEL) SEL_RPL3;
            if (   (psmte->smte_autods == 0)
                || (pote = ldrNumToOte(pmte, psmte->smte_autods)) == NULL)
            {
                pdata->ldrmi_autods = 0;
            }
            else
            {
                // ok to call tiled - can not be packed
                pdata->ldrmi_autods = LaToSelTiled(pote->ote_base) |
                                                    (SEL) SEL_RPL3;
            }
        }
    }

    return(NO_ERROR);
}


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

/***EP  LDRGetModuleType - determine module type
 *
 *      This routine is similar to LDRGetTKModuleInfo, but does not
 *      return nearly so much information, as not all is needed by some
 *      callers.  Given an hmte, it returns the type of module (DD, FSD,
 *      DLL, EXE) for that mte.
 *
 *      ENTRY   hobmte          MTE handle
 *              pType           pointer to return the type into
 *
 *      EXIT    int             return code (NO_ERROR if successful)
 *                              error if MTE handle not valid
 *
 */
int LDRGetModuleType( VMHOB      hobmte,
                      ushort_t  *pType )
{
    register ldrmte_t *pmte;

    // map and validate mte handle
    if ( !(pmte = ldrValidateMteHandle(hobmte) ) )
        return( ERROR_INVALID_HANDLE );

    if (pmte->mte_flags1 & LIBRARYMOD)
    {
        *pType = LDRLIBRARY;

        if (pmte->mte_flags1 & INSTLIBINIT)
            *pType |= LDRLIBINIT;

        if (pmte->mte_flags1 & MTEDLLTERM)
            *pType |= LDRDLLTERM;

        if (pmte->mte_flags1 & GINISETUP)
            *pType |= LDRGLOBALSETUP;

        if (pmte->mte_flags1 & GINIDONE)
            *pType |= LDRGLOBALDONE;
    }

    else if (pmte->mte_flags1 & DEVDRVMOD)
        *pType = LDRDEVICE;

    else if (pmte->mte_flags1 & (FSDMOD | FSHMOD))
        *pType = LDRFSD;

    else *pType = LDRPROGRAM;

    return(NO_ERROR);
}

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

/***EP  LDRSetTKLibiFlags -  Set/Reset Libinit flags in the MTE
 *
 *      Given a MTE handle, set the libinit flags in the MTE
 *
 *      ENTRY   hobmte          MTE handle
 *              flag            Flag to set/reset
 *              fSet            Set/Clear
 *
 *      EXIT    int             return code (NO_ERROR if successful)
 *                              error if MTE handle not valid
 */

int LDRSetTKLibiFlags( VMHOB hobmte,  /* handle to a module table entry */
                       int   flag,    /* LDRGLOBALDONE or LDRGLOBALSETUP */
                       int   fSet )   /* If TRUE, set else reset */
{
    register ldrmte_t  *pmte;          /* pointer to a module table entry */

    // map handle -assume valid for retail
#ifdef LDRSTRICT
    if ( !(pmte = ldrValidateMteHandle(hobmte) ) )
        return( ERROR_INVALID_HANDLE );
#else
    pmte = ldrpMTEFromHandle(hobmte);
#endif

    switch (flag)
    {
      case LDRGLOBALSETUP:
        if (fSet)
            pmte->mte_flags1 |=  GINISETUP;
        else
            pmte->mte_flags1 &= ~GINISETUP;
        break;

      case LDRGLOBALDONE:
        if (fSet)
             pmte->mte_flags1 |=  GINIDONE;
        else pmte->mte_flags1 &= ~GINIDONE;
        break;
    }
    return(NO_ERROR);
}


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


//137076 Start **************************************************
/***EP  LDRFreeNonResNamSegs - free nonresident nametable segments
 *
 *      If the resulting reference count is zero, this procedure walks
 *      the list of nonresident nametable segments and frees them up.
 *      All nonresident nametable segments are freed.
 *
 *      ENTRY   None
 *      EXIT    None
 *
 */
//
//void LDRFreeNonResNamSegs()
//{
//    register struct nonres_s *pnr;
//
//#ifdef LDRSTRICT
//    // check for overflow
//   if (cNonResNamTbl == 0)
//        panic("LDRFreeNonResNamSegs: overflow");
//#endif
//
//    cNonResNamTbl--;
//
//    /*
//     *  Request control of non-resident name tables
//     */
//    if (cNonResNamTbl == 0)
//    {
//        KSEMRequestMutex( hNonResNamTbl, -1 );
//
//        //  If no one using non-resident name tables, then walk
//        //  the list and free them.
//
//        while ( pNonResNamTbl )
//        {
//            pnr = pNonResNamTbl;
//            pNonResNamTbl = pnr->nr_link;
//
//            VMFreeKHB( VM_HKH_PUB_SWAPRW, pnr );
//        }
//
//        KSEMReleaseMutex( hNonResNamTbl );
//    }
//}
//
//
//
/***EP  LDRUseNonResNamSegs - increment use count for non-resident name tables
 *
 *      This function increments the count of active potential users
 *      of the list of non-resident name tables.
 *
 *      ENTRY   None
 *      EXIT    None
 */
//
//void LDRUseNonResNamSegs()
//{
//    cNonResNamTbl++;
//
//#ifdef LDRSTRICT
//    // check for overflow
//    if (cNonResNamTbl == 0)
//        panic("LDRUseNonResNamSegs: overflow");
//#endif
//
//}
//137076 End **************************************************
#pragma code_seg()
#pragma code_seg("SWAP32_4","CODE")

/***LP  ldrValidateMteHandle - given a handle validate it as a MTE handle
 *
 *      Validate the passed handle, returning a pointer to the MTE.
 *
 *      ENTRY   hobmte                  - handle to an object
 *
 *      EXIT    laddr                   -    linear address of an MTE
 *                                        or NULL if invalid handle
 */

ldrmte_t *ldrValidateMteHandle( register VMHOB hMTE )
{
    register ldrmte_t *pMTE;    // pointer to the MTE

    pMTE = mte_h;  // head of the MTE list

    // walk the list until we fall off the end
    while (pMTE)
    {
        // all done if we find it
        if ( pMTE->mte_handle == hMTE )
            break;

        pMTE = pMTE->mte_link;
    }

    return( pMTE );

}


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

/***LP  ldrpMTEFromHandle - given a handle find the MTE pointer
 *
 *      NOTE - this is the .ASM routine - for 'C' code, use the macro
 *             defined in ldrvars.h.
 *
 *      ENTRY   ushort_t                - MTE handle
 *
 *      EXIT    ldrmte *                - pointer to the MTE
 *                                        NULL if not found
 */

// ldrmte_t *ldrpMTEFromHandle( register ushort_t hMTE )

ldrmte_t *ldrASMpMTEFromHandle( register ushort_t hMTE )
{
    return( (ldrmte_t *)VMPseudoHandleMap( hMTE ) );
}


#pragma code_seg()
#if 0

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

/***LP  ldrBlastResource
 *
 *      ENTRY   pMTE
 *              Sel
 *
 *      EXIT    void
 *
 */

void ldrBlastResource( ldrmte_t *pMTE,
                       ulong_t LAddr )
{
    int        rc = ERROR_INVALID_PARAMETER;
    ulong_t    ObjNum = 0;

    register ldrsmte_t *pSMTE;
    register ldrote_t  *pote;
    register ldrste_t  *pste;

    pSMTE = pMTE->mte_swapmte;

    // 32-bit module
    if ( ldrIsLX(pMTE) )
    {
        while ( (rc) && (ObjNum < pSMTE->smte_objcnt) )
        {
            pote = &((ldrote_t *)pSMTE->smte_objtab)[ObjNum];
            if ( LAddr ==  pote->ote_base )
                rc = NO_ERROR;
            ObjNum++;
        }
    }

    // 16-bit module
    else
    {
        while ( (rc) && (ObjNum < pSMTE->smte_objcnt) )
        {
            pste = &((ldrste_t *)pSMTE->smte_objtab)[ObjNum];
            // don't look up linear for a zero selector; ok to use tiled call
            // as can not be packed
            if (pste->ste_selector)
                if ( LAddr == SelToLaTiled(pste->ste_selector) )
                    rc = NO_ERROR;
            ObjNum++;
        }
    }

    ObjNum--;
    // If not found, return. This implies that VM and Ldr are inconsistent ?
    if (rc)
        return;

    // 32-bit module
    if ( ldrIsLX(pMTE) )
    {
        pote->ote_base = 0L;
        pote->ote_selector = 0;
        pote->ote_handle = HOBNULL;
        pote->ote_flags &= ~OBJALLOC;
    }
    else  // 16-bit module
    {
        pste->ste_selector = 0;
        pste->ste_seghdl = 0;
    }

    return;
}

#pragma code_seg()
#endif

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


/***LP  ldrfindObject - find a object from a mte handle and a hob
 *
 *      ENTRY   hobmte                  - handle to mte
 *              hob                     - handle to search for
 *              ppmte                   - return pmte
 *              ppste                   - return pste
 *              pcount                  - return for object number
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 */

int ldrFindObject( VMHOB               hobmte,     /* handle to mte */
                   VMHOB               hob,        /* handle to search for */
                   ldrmte_t          **ppmte,
                   register ldrste_t **ppste,
                   int                *pcount )
{
    register ldrsmte_t *psmte;
    register ULONG      lcount;
    int                 rc = ERROR_INVALID_HANDLE;

    // map and validate mte handle
    if ( !(*ppmte = ldrValidateMteHandle(hobmte) ) )
        return( ERROR_INVALID_HANDLE );

    // find the object
    psmte = (*ppmte)->mte_swapmte;
    if ( ldrIsNE(*ppmte) )
    {
        for ( lcount = 0; lcount < psmte->smte_objcnt; lcount++ )
        {
            *ppste = &((ldrste_t *)psmte->smte_objtab)[lcount];
            if ((*ppste)->ste_seghdl == hob)
            {
                rc = NO_ERROR;
                break;
            }
        }
    }
    else
    {
        for ( lcount=0; lcount < psmte->smte_objcnt; lcount++ )
        {
            if (((ldrote_t *)psmte->smte_objtab)[lcount].ote_handle == hob)
            {
                rc = NO_ERROR;
                break;
            }
        }
    }

    if (!rc)
        *pcount = lcount;

    return(rc);
}





/***LP  ldrSetupResource - Setup for a call by ldrGetResource or
 *                         VR32QueryResourceSize.
 *
 *      This routine will:
 *      - Validate and map the handle passed to the correct MTE.
 *      - Validate that the MTE is attached to the current process.
 *      - Return a pointer to the MTE.
 *
 *      ENTRY   phob            Pointer to Module handle
 *              ppmte           Pointer to return the mte pointer in.
 *
 *      EXIT    int             return code (NO_ERROR if successful)
 */
int ldrSetupResource( VMHOB                hob,
                      register ldrmte_t  **ppmte )

{
    register ULONG   lflags;
    register int     rc;

    /*
     * check if the caller is passing handle to the mte that he has
     * access to. The caller will have access to a mte if:
     * o it is his program module's mte
     * o a mte that is either directly or indirectly linked
     *       to his program module
     * o it is found in the loader RMP segment in the record
     *       with caller's PTDA
     * if this condition is satisfied, it means that the caller
     * loaded the module through a previous DOSLOADMODULE API
     */

    if ((rc = LDRCheckModHandle(hob)) != NO_ERROR)
        return(rc);

    // map and validate mte handle
    if ( !(*ppmte = ldrValidateMteHandle(hob) ) )
        return( ERROR_INVALID_HANDLE );

    ldrMarkTaskInGraph(pPTDACur, NULL);
    ldrFindGraph(TRUE);
    lflags = (*ppmte)->mte_flags1;
    ldrClearGraph();

    if (! (lflags & USED))
        return(ERROR_INVALID_HANDLE);

    if ((*ppmte)->mte_swapmte->smte_rsrccnt == 0)
        return(ERROR_INVALID_PARAMETER);

    return(NO_ERROR);

}


/***LP  ldrGetResource - get resource
 *
 *      This routine returns a selector:offset for a resource in a 16-bit
 *      module, and a flat pointer address for a 32-bit module
 *
 *      ENTRY   typeid          type id
 *              nameid          name id
 *              hob             Module handle
 *              prrsrc          Pointer to resource return buffer
 *              ldrtype         LDR_16bit - caller is w_getresource
 *                              LDR_32bit - caller is VR32GetResource
 *
 *      EXIT    int             return code (NO_ERROR if successful)
 */
int ldrGetResource( ULONG typeid,
                    ULONG nameid,
                    VMHOB hob,
                    ldrrrsrc_t *prrsrc,
                    int ldrtype )
{
    ldrmte_t            *pmte;       /* pointer to mte */
    register ldrsmte_t  *psmte;      /* pointer to swappable mte */
    register ULONG       lrsrc;
    int                  rc;

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

    if (hob == NULL)
        hob = pPTDACur->ptda_module;

#ifdef LDRSTRICT
    if (DDInitTime)
        panic("GR:DDInitTime");
#endif

    if ( rc = ldrSetupResource( hob, SSToDS(&pmte) ) )
        goto ldrrsrclabel;

    psmte = pmte->mte_swapmte;

    /*
     * If module is a 32-bit module make sure caller is VR32GetResource
     */
    if (ldrIsLX(pmte) && !(ldrtype == LDR_32bit))
    {
        rc = ERROR_INVALID_HANDLE;
        goto ldrrsrclabel;
    }

    rc = ERROR_INVALID_PARAMETER;

    if (ldrIsNE(pmte))
    {
        for (lrsrc = 0; lrsrc < psmte->smte_rsrccnt; lrsrc++)
        {
            prrsrc->ldrrrsrc_type = LDR_16bit;

            if ((((ldrrsrc16_t *)psmte->smte_rsrctab)[lrsrc].ldrrsrc16_type
                                    == (USHORT) typeid) &&
                (((ldrrsrc16_t *)psmte->smte_rsrctab)[lrsrc].ldrrsrc16_name
                                    == (USHORT) nameid) )
            {
                rc = ldrGetRsrcSel(typeid, nameid, hob, pmte,
                         psmte->smte_objcnt - psmte->smte_rsrccnt+lrsrc+1,
                         &prrsrc->ldrrrsrc_ptr);
                break;
            }
        }
    }
    else
    {
        for (lrsrc = 0; lrsrc < psmte->smte_rsrccnt; lrsrc++)
        {
            prrsrc->ldrrrsrc_type = LDR_32bit;

            if ((((ldrrsrc32_t *)psmte->smte_rsrctab)[lrsrc].ldrrsrc32_type
                                    == (USHORT) typeid) &&
                (((ldrrsrc32_t *)psmte->smte_rsrctab)[lrsrc].ldrrsrc32_name
                                    == (USHORT) nameid) )
            {
                rc = ldrGet32Rsrc(hob, pmte,
                  ((ldrrsrc32_t *)psmte->smte_rsrctab)[lrsrc].ldrrsrc32_obj,
                              &prrsrc->ldrrrsrc_ptr);
                if (rc == NO_ERROR)
                    prrsrc->ldrrrsrc_ptr.ptr_flat +=
                    ((ldrrsrc32_t *)psmte->smte_rsrctab)[lrsrc].ldrrsrc32_off;
                break;
            }
        }
    }

ldrrsrclabel:
    LDRClearSem();
    TKClearKernelSem();
    return(rc);

}

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

/***EP  VR32GetResource - Entry point for DOS32GETRESOURCE
 *
 *      DOS32GETRESOURCE returns pointer associated with
 *      the passed type/name ID pair in the passed module.
 *
 *      ENTRY   ModHandle       Module handle returned by DOSLOADMODULE
 *              TypeID          Type id
 *              NameID          Name id
 *              pAddress        Pointer to where the address should be returned.
 *
 *      EXIT    NO_ERROR
 *              ldrGetResource error code.
 */
int cdecl VR32GetResource( THUNKSEF  sef,
                           VMHOB     hMTE,
                           ULONG     nameid,
                           ULONG     typeid,
                           laddr_t  *pAddress )
{
    register int        rc;
    struct   ldrrrsrc_s rrsrc;

    if ( (rc = ldrGetResource( nameid,
                               typeid,
                               hMTE,
                               SSToDS(&rrsrc),
                               LDR_32bit ) ) == NO_ERROR )
    {
        if (rrsrc.ldrrrsrc_type == LDR_16bit)
        {
           rrsrc.ldrrrsrc_ptr.ptr_flat =
                                   // ok to call tiled - can not be packed
                                   SelToLaTiled( rrsrc.ldrrrsrc_ptr.ptr_sel )
                                   + rrsrc.ldrrrsrc_ptr.ptr_off;
        }
        rc = TKSuDWord( rrsrc.ldrrrsrc_ptr.ptr_flat, pAddress );
    }

    return(rc);

    sef;
}

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

/***EP  VRDosQueryABIOSSupport - Entry point for DosQueryABIOSSupport
 *
 *      ENTRY   ulong_t        reserved = 0
 *
 *      EXIT    ulong_t        flags
 *
 */
int cdecl VRDosQueryABIOSSupport( SEF      sef,
                                  ulong_t  dummy )
{
    return( (ulong_t)BasicHWConfig );

    sef;
    dummy;
}



/***EP  VRDosQueryModFromEIP - Entry point for DosQueryModFromEIP
 *
 *      This function takes the passed flat address (supposed to be code,
 *      but could also be data or resource) and returns the hMTE, module
 *      name and object number. Note, for non-shared addresses (eg for
 *      a .EXE the owner returned will be for the current context.
 *      For debug support, also return offset within the object.
 *
 *      ENTRY                  ulong_t        *phMod,
 *                             ulong_t        *pObjNum,
 *                             ulong_t         BuffLen,
 *                             char           *pBuff,
 *                             ulong_t        *pOffset,
 *                             ulong_t         Address )
 *
 *
 *      EXIT    int             return code (NO_ERROR if successful)
 *
 */
int cdecl VRDosQueryModFromEIP( THUNKSEF       sef,
                                ulong_t        *phMod,
                                ulong_t        *pObjNum,
                                ulong_t         BuffLen,
                                char           *pBuff,
                                ulong_t        *pOffset,
                                ulong_t         Address )
{
    VMOD                od;       // object info buffer
    int                 rc;
    register ldrsmte_t *pSMTE;    // pointer to the owner MTE
    register ldrmte_t  *pMTE;     // pointer to the owner MTE
    ulong_t              ObjNum;  // number of the object in the module
    ldrote_t            *pote;
    ldrste_t            *pste;
    ulong_t              Offset;

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

    od.od_laddr = Address;
    od.od_pPTDA = pPTDACur;
    if (rc = VMGetOd( NA, SSToDS(&od)) )
        goto ModFromEIP_exit;

    if (phMod)
        *phMod = (ulong_t)(VMMTEOd(od));

    // Find the pointer to the MTE handle for this resource, and then the
    // object number within that module
    if (!( pMTE = ldrValidateMteHandle( VMMTEOd(od) ) ))
    {
        rc = ERROR_INVALID_PARAMETER;
        goto ModFromEIP_exit;
    }
    pSMTE = pMTE->mte_swapmte;

    ObjNum = 0;
    rc = ERROR_INVALID_PARAMETER;
    while ( (rc) && (ObjNum < pSMTE->smte_objcnt) )
    {
        // 32-bit module
        if ( ldrIsLX(pMTE) )
        {
            pote = &((ldrote_t *)pSMTE->smte_objtab)[ObjNum];
            if (    ( Address >=  pote->ote_base )
                 && ( Address <=  pote->ote_base + pote->ote_size ) )
            {
                rc = NO_ERROR;
                Offset = Address - pote->ote_base;
            }
        }

        // 16-bit module
        else
        {
            pste = &((ldrste_t *)pSMTE->smte_objtab)[ObjNum];
            // don't even try this for a null selector
            if (pste->ste_selector)
            {
                Offset = SELSelToLa( pste->ste_selector,
                                     pPTDACur );
                if (    ( Address >= Offset )
                     && ( Address <= Offset + pste->ste_minsiz ) )
                {
                    rc = NO_ERROR;
                    Offset = Address - Offset;
                }
            }
        }
        ObjNum++;
    }
    ObjNum--;

    // Not found ??
    if (rc)
    {
        Offset = -1;
        ObjNum = -1L;
        rc = NO_ERROR;
    }
    if (pObjNum)
        *pObjNum = ObjNum;
    if (pOffset)
        *pOffset = Offset;

    // copy the module name if the MTE is valid
    if ( (BuffLen) && (pBuff) )
    {
        ObjNum = BuffLen < 8 ? BuffLen : 8; /* 8 is size of pMTE->mte_modname */

        rc = TKSuBuff( pBuff,
                       ((unsigned char *)&pMTE->mte_modname),
                       ObjNum,
                       TK_UM_FATAL );
        pBuff[ObjNum] = '\0';
    }


ModFromEIP_exit:
    LDRClearSem();
    TKClearKernelSem();

    return(rc);

    sef;
}



/***EP  VRDosQueryModFromCS - Entry point for DosQueryModFromCS
 *
 *      This function takes the passed code selector and determines the
 *      associated MTE handle, segment number and module name.
 *
 *      ENTRY   presult         pointer to where to return data
 *              sel             selector of CS
 *
 *      EXIT    int             return code (NO_ERROR if successful)
 *
 */
int cdecl VRDosQueryModFromCS( SEF             sef,
                               struct FarPtr   presult,
                               SEL             sel )
{
    VMOD                    od;
    struct seldesc_s        seldesc;
    int                     rc;
    register ldrmte_t       *pmte;
    register ldrsmte_t      *psmte;
    ldrste_t                *pste;
    ldrote_t                *pote;
    ulong_t                 lobjcnt;
    laddr_t                 lbase;
    struct FarPtr           pdst;

// The selector must be for a code segment described in the current LDT.
#define CurLDT          pPTDACur->ptda_ldtaddr
#define DescOffset(sel) (sel & ~(RPL_MASK | TABLE_MASK))
#define AccessByte(sel) ((struct desctab *)(CurLDT+DescOffset(sel)))->d_access
#define CodeSeg         (D_SEG | D_CODE)

    if (!(sel & SEL_LDTBIT) ||
        ((AccessByte(sel) & CodeSeg) != CodeSeg))
    {
        rc = ERROR_INVALID_PARAMETER;
        goto ldrdqmfcs;
    }

    od.od_pPTDA =pPTDACur;
    if ( ( rc = SELGetOd( VMGS_NOBLOCK,
                          sel,
                          SSToDS(&seldesc),
                          SSToDS(&od) ) ) != NO_ERROR )
        goto ldrdqmfcs;

    // map and validate mte handle
    if ( !(pmte = ldrValidateMteHandle(VMHobMTEOd(od)) ) )
    {
        rc = ERROR_INVALID_HANDLE;
        goto ldrdqmfcs;
    }

    if ( ( rc = LDRCheckModHandle( VMHobMTEOd(od)) ) != NO_ERROR )
        goto ldrdqmfcs;

    psmte = pmte->mte_swapmte;

    if ( ldrIsNE(pmte) )
        pste = (ldrste_t *) psmte->smte_objtab;
    else
    {
        pote = (ldrote_t *) psmte->smte_objtab;
        lbase = SELSelToLa( sel, pPTDACur );
    }

    for (lobjcnt=0; lobjcnt<psmte->smte_objcnt; lobjcnt++,pste++,pote++)
    {
        if (ldrIsNE(pmte) && pste->ste_selector == sel)
            break;
        else if (ldrIsLX(pmte) && pote->ote_base == lbase)
            break;
    }

    if ((lobjcnt >= psmte->smte_objcnt))
        goto ldrdqmfcs;

    pdst = presult;
    pdst.Offst += (ushort_t) FIELDOFFSET(ldrwhois_t, whois_ssegnum);

    lobjcnt++;
    if ((rc = SELSuBuff(pdst, SSToDS(&lobjcnt),
                        sizeof(lobjcnt), NULL)) != NO_ERROR)
        return(rc);

    pdst = presult;
    pdst.Offst += (ushort_t) FIELDOFFSET(ldrwhois_t, whois_hmte);

    if ((rc = SELSuBuff(pdst, &(VMMTEOd(od)),
                        sizeof(VMHOB), NULL)) != NO_ERROR)
        return(rc);

    pdst = presult;
    pdst.Offst += (ushort_t) FIELDOFFSET(ldrwhois_t, whois_achname);

    /*
     * Check that pathname not greater than hard code length of
     * pathname found in PM code.  Otherwise GP fault user app.
     */

#ifdef LDRSTRICT
    ldrAssert(strlen((char *) psmte->smte_path) + 1 <= 256);
#endif

    return(SELSuBuff(pdst, (void *) psmte->smte_path,
                     strlen((char *) psmte->smte_path) + 1, NULL));

ldrdqmfcs:
    return(rc);

    sef;
}


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

/***EP  LDRCheckModHandle - return error if hobmte is not a valid module handle.
 *
 *      LDRCheckModHandle is used to verify a module handle.  The MTE must
 *      be in the module list for the handle to be considered valid.
 *
 *      ENTRY   hobmte    - Module Handle
 *
 *      EXIT    ULONG, standard error code
 *                      NO_ERROR : hobmte is a valid MTE handle
 *                      ERROR_MOD_NOT_FOUND : hobmte is not a valid MTE handle
 *
 */

int LDRCheckModHandle( register VMHOB hobmte )
{
    register ldrmte_t *pmte;

    pmte = mte_h;
    while ( (pmte) && (pmte->mte_handle != hobmte) )
        pmte = pmte-> mte_link;

    return ( (pmte) ? NO_ERROR
                    : ERROR_MOD_NOT_FOUND );
}


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

/***LP  ldrGet32Rsrc - Get a resource from a 32-bit module
 *
 *      This routine will return a resource from a 32-bit module
 *
 *      ENTRY   hob             Module handle of mte
 *              pmte            pointer to mte
 *              lobj            object number of resource
 *              paddr           Pointer to where the address should be returned.
 *
 *      EXIT    int             return code (NO_ERROR if successful)
 */
int ldrGet32Rsrc( VMHOB    hob,
                  ldrmte_t *pmte,
                  ULONG    lobj,
                  ptr_t    *paddr )
{
    register ldrsmte_t *psmte;       // pointer to swappable mte
    register ldrote_t  *pote;        // pointer to ote
    PPTDA               pptda;       // current or execchild
    int                 rc = NO_ERROR;
//  int                 retried = 0;

    psmte = pmte->mte_swapmte;

#ifdef LDRSTRICT
    ldrAssert( lobj <= psmte->smte_objcnt );
#endif

    pote = &((ldrote_t *) psmte->smte_objtab)[lobj-1];

    pptda = TKContext();

// get32rsrc_retry:

    // Check if we are attached to resource if not call ldrAllocObject
    if (    (rc)
         || (pote->ote_handle == 0)
         || (VMIsAttached( pote->ote_handle,
                           pptda->ptda_handle) != NO_ERROR ) )
    {
        if ( rc = ldrAllocObject( pmte, pote, 0, pptda, lobj ) )
            return(rc);
    }

    /*
     * Keep count of number of uses of this resource.  Since a object
     * may contain more than one resource.  We only have to count the
     * use of the object not the resource.  So increment the record
     * for this object.
     */
    ldrIncRefCount( RSRC32TYPEID, lobj, pmte->mte_handle, pmte );

// 62652
//  if (rc = ldrIncRefCount( RSRC32TYPEID, lobj, pmte->mte_handle, pmte ) )
//  {
//      if (!retried)
//      {
//          retried = 1;
//          goto get32rsrc_retry;
//      }
//  }

    paddr->ptr_flat = pote->ote_base;

    return(rc);

    hob;
}


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

/***EP  LDRGetDYNMteInfo - Get MTE information for DynamicTrace.
 *
 *      LDRGetDYNMteInfo gathers information about a module that
 *      DynamicTrace requires.  The dtl array contains per-object
 *      information that is used by DynTrc.
 *
 *      *plModType will be retuned as either DTO_DLL or DTO_PROGRAM.
 *      If the module is neither type (as in a device driver or the kernel),
 *      *plModType will be returned as zero.
 *
 *      LDRGetDYNMteInfo verifies hobmte.
 *
 *      ENTRY   hobmte          - MTE Handle to gather information from
 *              ppadtl          - pointer to return location of dtl array
 *              plcdtl          - pointer to return number of objects in module
 *              plModType       - pointer to return type of module.
 *
 *      EXIT    ULONG, standard error code
 *
 */

int LDRGetDYNMteInfo( VMHOB     hobmte,
                      dtl_t   **ppadtl,
                      PULONG    plcdtl,
                      PULONG    plModType )
{
    ULONG   i, FirstResource;
    int     rc;
    register ldrmte_t    *pmte;
    register ldrsmte_t   *psmte;
    register dtl_t       *pdtl;

    /*
     * Find the MTE...
     */

    pmte = mte_h;
    while ((pmte != NULL) && (pmte-> mte_handle != hobmte))
            pmte = pmte-> mte_link;

    if (pmte == NULL)
        return (ERROR_MOD_NOT_FOUND);

    psmte = pmte->mte_swapmte;

    /*
     * The module must have at least one object to be useful here.
     * MVDM.DLL is an example of this.
     */

    if ((*plcdtl = psmte->smte_objcnt) == 0)
    {
       return (ERROR_INVALID_MODULETYPE);
    }

    /*
     * Determine the type of module: program, dll, or neither.
     * If it's not a LIBRARYMOD, it's a program; else it's a
     * DLL only if none of the listed bits are set.
     */

    *plModType = (pmte->mte_flags1 & LIBRARYMOD) ?
                 ((pmte->mte_flags1 & (DOSMOD | DEVDRVMOD | VDDMOD |
                                       MVDMMOD | FSDMOD | FSHMOD)) ?
                    0 : DTO_DLL) : DTO_PROGRAM;

    /*
     * Allocate space from the resident heap for the dyntrc info.
     * We need enough space to hold one dtl_s for each object.
     * DynTrc will free this array of dtl structures later.
     */

    if (NO_ERROR != (rc = VMAllocKHB(VM_HKH_PUB_RESRW,
        sizeof(dtl_t) * (*plcdtl), (ULONG) DYNMTEDTLOWNER, NA,
        (void *) ppadtl)))
    {
       return (rc);
    }
    pdtl = *ppadtl;

    /*
     * Gather per-object information depending on the type of MTE.
     */

    if (ldrIsNE (pmte))
    {
       register ldrste_t *pste = (ldrste_t *) (psmte->smte_objtab);

       FirstResource = psmte->smte_objcnt - psmte->smte_rsrccnt;

       for (i = 1; i <= psmte->smte_objcnt; i++, pdtl++, pste++)
       {
           pdtl-> dtl_fs = ((pste-> ste_flags & STE_TYPE_MASK) ==
               STE_CODE) ? (USHORT) DTL_CODE : (USHORT) 0;

           pdtl-> dtl_fs |= (i >= FirstResource) ? DTL_RESOURCE:0;

           pdtl-> dtl_sel = pste-> ste_selector;
           pdtl-> dtl_pBase = SELVirtToLin(0, pste->ste_selector);
           pdtl-> dtl_cb = (ULONG) pste-> ste_size;
       }
    }
    else
    {
       register ldrote_t *pote = (ldrote_t *) (psmte->smte_objtab);

       for (i = 1; i <= psmte->smte_objcnt; i++, pdtl++, pote++)
       {
           pdtl-> dtl_fs = (USHORT)(pote->ote_flags & OBJEXEC) ?
               (USHORT) DTL_CODE : (USHORT) 0;

           pdtl-> dtl_fs |= (pote-> ote_flags & OBJRSRC) ?
               DTL_RESOURCE : 0;

           pdtl-> dtl_sel = ((pote-> ote_base) < SEL_512MEG)
                                ? SELLaToSel( pote->ote_base, pPTDACur )
                                : (USHORT) 0;

           pdtl-> dtl_pBase = pote-> ote_base;
           pdtl-> dtl_cb = pote-> ote_size;
       }
    }
    return (NO_ERROR);
}





/***EP  ldrGetContextSub - ScanTasks worker routine for LDRGetContext
 *
 *      ENTRY   pptda                   - PTDA pointer
 *              plgcs                   - lgcs structure pointer.
 *
 *      EXIT    ULONG, standard error code
 *              TRUE  (1)       - This process uses this hmte.  See lgcs_hmte.
 *              FALSE (0)       - This process does not use this hmte.
 *
 *      Local Data Structure:
 *              lgcs_s (LDR Get Context Sub Structure) is used only by
 *              LDRGetContext and ldrGetContextSub.  It contains the
 *              return information to pass through the ScanTasks call.
 */

int ldrGetContextSub( PPTDA   pptda,
                      lgcs_t  *plgcs )
{
    // Set plgcs-> lgcs_pptda to the ptda being scanned, and
    // return TRUE if the ptda uses the specified mte handle.

    return ( (plgcs->lgcs_pptda = pptda)->ptda_module == plgcs->lgcs_hmte );
}



/***EP  LDRGetContext - Get ptda handle using specified MTE.
 *
 *      LDRGetContext searches the PTDA tree to find some process
 *      which is using the specified MTE handle as it's executable
 *      module.  If multilple contexts are using the module, the
 *      first context found will be returned.
 *
 *      LDRGetContext will only work on program modules.  Use
 *      LDRGetDYNMteInfo to determine this before calling LDRGetContext.
 *      LDRGetContext skips dying processes, but scans embryo processes.
 *
 *      PERFORMANCE WARNING: This routine is fairly slow, as it must
 *      scan the entire process tree.  Use it sparingly.
 *
 *      WARNING - do not call at DD init time. See ldrGetContextSub
 *
 *      ENTRY   hobmte  - mte handle
 *              phptda  - address to return context handle (a PTDA handle)
 *
 *      EXIT    ULONG, standard error code
 *              NO_ERROR                - Success.  *phptda = context handle.
 *              ERROR_MOD_NOT_FOUND     - No process is using this MTE.
 *
 */

int LDRGetContext( VMHOB   hobmte,
                   VMHOB   *phptda )
{
    lgcs_t       lgcs;   // Data Structure for ScanTasks
    register int rc;     // Return code from ScanTasks

    // If the current context uses the module, just return the current
    // process context to save the scan.

#ifdef LDRSTRICT
    if (DDInitTime)
        panic("GC:DDInitTime");
#endif

    if (hobmte == pPTDACur->ptda_module)
    {
        *phptda = pPTDACur->ptda_handle;
        return (NO_ERROR);
    }

    lgcs.lgcs_hmte = hobmte;        // Set hmte to search for in lgcs_hmte.

    // Search the whole process tree, stopping if a match is found, and
    // returning the error code from ldrGetContextSub.

    rc = TKScanTasks( TK_TG_ALL| TK_SF_WORKER| TK_SF_WRETCODE| TK_SF_EXEC,
                      0,
                      ldrGetContextSub,
                      (void *) SSToDS(&lgcs) );

    // Note that rc represents a nonstandard error code here.
    // ScanTasks is only able to stop when the worker routine returns a
    // nonzero error code.  So, a NO_ERROR condition from TKScanTasks
    // is actually a failure!  The only success return code is 1 (TRUE).
    // Note that we propogate other unexpected ScanTasks error codes,
    // but NO_ERROR is translated into ERROR_MOD_NOT_FOUND.

    if ( rc != 1 )
        return ( rc ? rc : ERROR_MOD_NOT_FOUND );

    // lgcs.lgcs_pptda now points to the desired ptda.

    *phptda = (VMHOB) lgcs.lgcs_pptda->ptda_handle;

    return (NO_ERROR);
}



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

/***LP  ldrInitEntry - Zero initialize INT 3Fh in movable entries
 *
 *      For ring 2 segments the callgate selector overlays the int 3fh
 *      instruction in a movable entry table entry.  This value is
 *      intitialized to zero to indicate that the callgate has not yet
 *      been allocated.  Also for 16-bit modules that are pageable, check
 *      to see if any exported procedure entries cross a page boundary.
 *      (Called from CreateMTE)
 *
 *      ENTRY   pet                     - pointer to entry table bundle
 *              pentry                  - pointer to entry table entry
 *              pmte                    - pointer to module table entry
 *              plv                     - pointer to local vars
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 *
 *      This procedure performs the following steps
 *
 *      - check for movable entry
 *      - zero entry
 */

int ldrInitEntry( ldret_t   *pet,     /* pointer to entry table bundle */
                  PULONG     pentry,  /* pointer to entry table entry */
                  ldrmte_t  *pmte,    /* pointer to module table entry */
                  ldrlv_t   *plv )    /* pointer to local vars */
{
    UCHAR           flags;
    USHORT          offset;
    USHORT          obj;

    if (ldrIsNE(pmte))   // 16-bit module
    {
        flags = ((ldret16_t *) pentry)->et16_flags;

        /*
         * Check to see if have an exported entry point which is not a
         * library module or a library module which has global data.
         */
        if (flags & EF_EXPORT && !(pmte->mte_flags1 & LIBRARYMOD) ||
                (pmte->mte_flags1 & LIBRARYMOD && flags & EF_GDATA))
        {

            /*
             * Remove offset and object number from entry table bundle
             */
            if (pet->b32_type == 0xff)
            {
                offset = ((ldrcte_t *) pentry)->cte_off;
                obj = ((ldrcte_t *) pentry)->cte_obj;
            }
            else
            {
                offset = ((ldret16_t *) pentry)->et16_off;
                obj = pet->b32_type;
            }
        }

        /*
         * check for movable entry
         */
        if (pet->b32_type != 0xff)
            return(NO_ERROR);

        ((ldrcte_t *)pentry)->cte_sel = 0; /* init callgate selector to 0 */
    }

    else    // 32-bit module
    {
        if (pet->b32_type == GATE16)
        {
            if (((ldret16iopl_t *)pentry)->et16iopl_call != 0)
            {
                ldrStop(0, 0);
                return(ERROR_BAD_EXE_FORMAT);
            }
        }
    }

    return(NO_ERROR);

    plv;
}


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

/***LP  ldrGetCallGate - Allocate or attach callgates for IOPL segments.
 *
 *      ENTRY   pet                     - pointer to entry table bundle
 *              pentry                  - pointer to entry table entry
 *              pmte                    - pointer to module table entry
 *              plv                     - pointer to local vars
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 *
 *      This procedure performs the following steps
 *
 *      - Check for movable 16-bit entries
 *      - Or 32-bit 286 call gate entries.
 *        - For 32-bit modules we must check the object type since it wasn't
 *          done by the caller.  Only allocate callgates for IOPL Non-Conforming
 *          code objects.
 *      - call ldrAllocCallGate
 */

int ldrGetCallGate( ldret_t  *pet,      /* pointer to entry table bundle */
                    PULONG    pentry,   /* pointer to entry table entry */
                    ldrmte_t *pmte,     /* pointer to module table entry */
                    ldrlv_t  *plv )     /* pointer to local vars */
{
    ldrote_t  *pote;

    // 16 bit module ?
    if (ldrIsNE(pmte))
    {
        // check for movable entry, we should not process any fixed entries here
        if (pet->b32_type != 0xff)
            return(NO_ERROR);
    }
    else  // must be 32-bit module
    {
        // check for 286 call gate
        if (pet->b32_type != GATE16)
            return(NO_ERROR);

        // We have a 16-bit call gate entry point, do we need to create a
        // call gate ?
        if ( (pote = ldrNumToOte( pmte, (ULONG) pet->b32_obj ) ) == NULL )
            return(ERROR_INVALID_SEGMENT_NUMBER);

        // Only allocate callgates for objects that are IOPL, Non-comfroming
        // and executable.
        if ( (pote->ote_flags & (OBJIOPL | OBJCONFORM | OBJEXEC) )
                                                        != (OBJIOPL | OBJEXEC) )
            return(NO_ERROR);
    }

    // get callgate
    return( ldrAllocCallGate( pet, pentry, pmte ) );

    plv;
}

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

/***LP  ldrAllocCallGate - Allocate or attach callgates for IOPL segments.
 *
 *
 *      ENTRY   pet                     - pointer to entry table bundle
 *              pentry                  - pointer to entry table entry
 *              pmte                    - pointer to module table entry
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 *
 *      This procedure performs the following steps
 *
 *      - check if call gate has been allocated
 *      - allocate or attach to call gate
 *      - return error if allocate of attach fails
 *      - store call gate in entry table
 *      - store entry point in call gate
 */

int ldrAllocCallGate( ldret_t  *pet,     /* pointer to entry table bundle */
                      ULONG    *pentry,  /* pointer to entry table entry */
                      ldrmte_t *pmte )   /* pointer to module table entry */
{
    SEL        sel;            /* callgate selector */
    ULONG      ltgtobj;        /* target object */
    USHORT     stgtoff;        /* target offset */
    USHORT     fstgtflgs;      /* target flags */
    ldrste_t  *pste;
    ldrote_t  *pote;
    int        rc;
    SEL        tgtsel;

    if (ldrIsNE(pmte))
    {
        // get target info
        sel = ((ldrcte_t *)pentry)->cte_sel;
        ltgtobj = (ULONG) ((ldrcte_t *)pentry)->cte_obj;
        stgtoff = ((ldrcte_t *)pentry)->cte_off;
        fstgtflgs = ((ldrcte_t *)pentry)->cte_flags;

        if ( ( pste = ldrNumToSte( pmte, ltgtobj ) ) == NULL )
            return(ERROR_INVALID_SEGMENT_NUMBER);

        tgtsel = pste->ste_selector;
    }
    else
    {
        sel = ((ldret16iopl_t *)pentry)->et16iopl_call;
        ltgtobj = (ULONG) pet->b32_obj;
        stgtoff = ((ldret16iopl_t *)pentry)->et16iopl_off;
        fstgtflgs = ((ldret16iopl_t *)pentry)->et16iopl_flags;

        if ( ( pote = ldrNumToOte( pmte, ltgtobj ) ) == NULL )
            return(ERROR_INVALID_SEGMENT_NUMBER);

        if ( pote->ote_base < SEL_512MEG )
        {
            tgtsel = SELLaToSel( pote->ote_base, pPTDACur ) |
                                 (SEL) ((pote->ote_flags &OBJIOPL) ? SEL_RPL2
                                                                   : SEL_RPL3 );
        }
        else
            tgtsel = 0;
    }

    // If the selector is already allocated, and is a GDT selector,
    // then we must already be attached, so return NO_ERROR.
    if ((sel != 0) && (!(sel & SEL_LDTBIT)))
        return(NO_ERROR);

    // If this is a DLL, and we do not have a selector allocated yet, then
    // we attempt to allocate a GDT callgate selector.    Note that any error
    // from SELAllocGDT except ERROR_NOT_ENOUGH_MEMORY is returned to the
    // caller without attempting to reserve an LDT selector.
    if ( (sel == 0) && (pmte->mte_flags1 & LIBRARYMOD) )
        rc = SELAllocGDT(SSToDS(&sel));
    else
        rc = ERROR_NOT_ENOUGH_MEMORY;       /* emulate SELAllocGDT failure */

    // If we don't have the selector reserved yet (either because we don't
    // have enough GDT selectors for a DLL, or this is an EXE module), then
    // we attempt to Reserve an LDT selector. The SELReserveLDT call takes
    // one of three forms, all expressed in the first parameter of
    // SELReserveLDT :
    //
    // Sel != 0, any module type    : Reserve the same SPECIFIC (-1) selector again.
    // Sel == 0, and a DLL module : reserve a new SHARED    (1) selector.
    // Sel == 0, and an EXE module: reserve a new PRIVATE (0) selector.

    if (rc == ERROR_NOT_ENOUGH_MEMORY)
    {
        rc = SELReserveLDT( ( (sel != 0) ? -1
                                         : (pmte->mte_flags1 & LIBRARYMOD)
                                                      ? SELSD_SHARED
                                                      : 0 ),
                            1,
                            pPTDACur->ptda_child,
                            SSToDS(&sel) );
    }

    // If we have a selector, then set it up.
    if (!rc)
    {
        SELSetDescInfo( SELSD_GATE,
                        (SEL) (((ULONG) (fstgtflgs>>3)<<8)|D_GATE3),
                        sel,
                        (ULONG) stgtoff,
                        tgtsel,
                        0,
                        pPTDACur->ptda_child,
                        0 );
        if (ldrIsNE(pmte))
            ((ldrcte_t *) pentry)->cte_sel = sel;
        else
            ((ldret16iopl_t *)pentry)->et16iopl_call = sel;
    }

    return (rc);
}



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

/***EP  LDRFreeObject - zero handle or selector field in object table entry
 *
 *      ENTRY   hobmte                  - handle to mte
 *              hob                     - handle to search for
 *              lflags                  - task termination flag
 *              pptda                   - point to ptda
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 */

int LDRFreeObject( VMHOB hobmte,     /* handle to mte */
                   VMHOB hob,        /* handle to search for */
                   ULONG lflags,     /* flags */
                   PPTDA pptda )
{
    int                 rc;
    ULONG               i;
    ldrmte_t           *pmte;
    ldrste_t           *pste;
    ldrote_t           *pote;
    register ldrsmte_t *psmte;
    ULONG               j;
    ldrste_t           *pstetemp;

    if (hob==HOBNULL)
        return(NO_ERROR);

    if (rc = ldrFindObject( hobmte,
                            hob,
                            SSToDS(&pmte),
                            SSToDS(&pste),
                            SSToDS(&i) ) )
        return(rc);

    psmte = pmte->mte_swapmte;

    // Free object if is not a resource

    // If it is 16-bit
    if (ldrIsNE(pmte))
    {
        // If it is not a resource
        if (i < (psmte->smte_objcnt - psmte->smte_rsrccnt))
        {
            if (!((pste->ste_seghdl != NULL) &&
               ((pste->ste_flags & STE_SHARED) == 0) &&
               (pste->ste_selector & TABLE_MASK)))
            {

                // If we are freeing the object for the packed segments,
                // sweep through the ste and zero all the ste_seghdl's
                // that refer to this object.
                if (pste->ste_flags & STE_PACKED)
                {
                    pstetemp = (ldrste_t *)psmte->smte_objtab;
                    for ( j = psmte->smte_objcnt - psmte->smte_rsrccnt;
                          j > 0;
                          --j, ++pstetemp)
                    {
                        if (pstetemp->ste_flags & STE_PACKED)
                        {
#ifdef LDRSTRICT
                            ldrAssert(    (hob == pstetemp->ste_seghdl)
                                       || (hob == 0)
                                       || (pstetemp->ste_seghdl == 0)
                                     );
#endif
                            pstetemp->ste_seghdl = 0;
                        }
                    }
                }
                else
                {
                    pste->ste_seghdl = 0;
                }
            }
            return(NO_ERROR);  // Return for the NE, non-resource case,
                               // otherwise drop through.

        } // end of it is not a resource

    } // end of if it is 16-bit (NE)

    else // must be LX format
    {
        ldrAssert( ldrIsLX(pmte) );

        pote = &((ldrote_t *)psmte->smte_objtab)[i];
        if (!(pote->ote_flags & OBJRSRC))
        {
            pote->ote_handle = 0;
            pote->ote_selector = 0;

            // Do not do this - the OBJALLOC flag is used by ldrFreeMTE
            // to decide whether or not to free the arena record. This call
            // from VM is for the deletion of the last object record. Ideally
            // we want to clear the OBJALLOC flag, but then FreeMTE needs a
            // new mechanism (?). Note that ldrAllocObject use the fact that
            // the hob field is zero. Nasty, nasty, and very unclean, but it
            // does work, and I really do not want to change the code much
            // as it is real close to the 2.1 ship. Paul.

            // pote->ote_flags &= ~OBJALLOC;

            return(NO_ERROR);     // Return for the LX, non-resource case,
                                  // otherwise drop through.
        }

    } // end of 32-bit (LX)

    /*
     * The object is a resource.
     */
    if ((rc = ldrCheckForResource( i+1,
                                   pmte,
                                   pptda->ptda_rsrclist,
                                   pptda,
                                   lflags )) == NO_ERROR)
    {
        if ( NO_ERROR == (rc = ldrRsrcFindGlobal(i+1, pmte, NULL)) )
        {
            if (ldrIsLX(pmte))                        /* 32-bit module */
            {
                pote = (ldrote_t *)psmte->smte_objtab;
                pote[i].ote_handle = 0;
                pote[i].ote_base = 0;
                pote[i].ote_selector = 0;
                pote[i].ote_flags &= ~OBJALLOC;
            }
            else                                      /* 16-bit module */
            {
                pste->ste_seghdl = 0;
                pste->ste_selector = 0;

                /* @DB73642 - if the resource is not discardable, then */
                /*            set the PreLoad flag to indicate that it */
                /*            needs to be reloaded                     */
                if (!(pmte->mte_flags1 & MTE_MEDIAFIXED))
                  pste->ste_flags |= STE_PRELOAD;
                /* @DB73642 - End of insertion                         */

                /*
                 * Free all segments of a hugh resource
                 */
                ldrFreeHuge(i+1, pmte);
            }
        }
        return( NO_ERROR );    // force the no_error for vm for task term
    }

    ldrAssert( (rc==ERROR_BUSY) | (rc==NO_ERROR) );

    return( rc );
}


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

int LDRGetProcAddr( VMHOB             hmte,      // Module handle
                    ulong_t           ulord,     // Ordinal number
                    register uchar_t *pchname,   // Procedure name
                    ptr_t            *paddress,  // Place to return address
                    int               fflat,     // If true caller wants flat addr
                    PULONG            penttype ) // pointer to procedure type
{
    register ldrmte_t  *pmte;          /* Pointer to MTE */
    struct taddr_s  taddr;
    int             cch;            /* Length of name */
    int             rc;             /* Return code */
    uchar_t         achname[MAX_PROC_LEN];
                                    /* Procedure name */

    if ( rc = LDRGetSem() )
        return( rc );                /* Get loader semaphore */

    do                              /* Dummy loop */
    {
        if (    hmte != DosModHdl
             && ( rc = ldrWasLoadModuled( hmte, pPTDACur, NULL ) ) )
            break;                  // Error if not DosLoadModuled
                                    // BugBug - maybe we should allow it
                                    //          anyways ????

        pmte = (ldrmte_t *) VMPseudoHandleMap(hmte);

        if (ulord == 0)   /* If name specified, not ordinal */
        {
            if ( rc = TKFuBuffz( SSToDS(&cch),
                                 SSToDS(achname),
                                 pchname,
                                 MAX_PROC_LEN,
                                 0 ) )
                break;              /* Fetch name from user space */

            /* Get DS-relative pointer to buffer */
            pchname = SSToDS(achname);

            /* If name too long */
            if (pchname[--cch] != '\0')
            {
                rc = ERROR_INVALID_NAME;
                break;
            }

            /* If not ascii ordinal */
            if (pchname[0] != '#')
            {
                /*
                 *  Convert ASCIIZ string to the type length and
                 *  non-null string in place. This is needed by
                 *  ldrGetOrdNum.
                 */
                ldrStrCpyB( pchname + 1, pchname, cch );
                pchname[0] = (uchar_t) cch;

                /* Get access to non-resident names */
//137076                LDRUseNonResNamSegs();

                /* Look up the ordinal */
                rc = ldrGetOrdNum(pmte,pchname,SSToDS(&ulord));

                /* Done with names */
//137076                LDRFreeNonResNamSegs();

                if (rc)
                    break;          /* Exit if name not found */
            }
            else
            {
                ulord = ldrAscToOrd( pchname + 1, cch - 1 );
            }

        }

        while(TRUE)
        {
            memset( (PCHAR)SSToDS(&taddr), 0, sizeof(taddr) );

            /* Look up the address */
            rc = ldrGetEntAddr( (USHORT)ulord,
                                pmte,
                                SSToDS(&taddr),
                                STE_RING_3 | (fflat ? 0 : NRALIAS),
                                0,
                                pPTDACur );

            /* Exit if okay or not DOSCALLS */
            if ( (rc == NO_ERROR) || !(pmte->mte_flags1 & DOSMOD))
                break;

            pmte = pmteDoscall1;    /* Search DOSCALL1 */
        }

        if (rc)
            break;

        if (!fflat)  /* If 16:16 address wanted */
        {
            if (taddr.toff > _64K )
            {
                rc = ERROR_INVALID_BLOCK;
                break;
            }

            /* Form 16:16 address */
            taddr.toff += (ulong_t) taddr.tsel << WORDSHIFT;
        }

        else    /* Flat address wanted */
        {

            /*
             * Check that a callgate wasn't returned, since we are
             * returning a 32-bit address a callgate can not be used
             * return a error.
             */
            if (taddr.toff == 0 && taddr.tsel != 0)
            {
                rc = ERROR_ENTRY_IS_CALLGATE;
                break;
            }
        }

        /* Return address to user */
        rc = TKSuDWord( taddr.toff, &paddress->ptr_flat );

        /* Return procedure type */
        if (penttype != NULL)
        {
            if (taddr.fflags & FWD_ALIAS16)
                rc = TKSuDWord( PT_16BIT, penttype );
            else
                rc = TKSuDWord( PT_32BIT, penttype );
        }

    } while(FALSE);                 /* End dummy loop */

    LDRClearSem();                  /* Release loader semaphore */
    return(rc);                     /* Return error code */
}


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

/***EP  VR32QueryProcAddr - Entry point for DOS32QUERYPROCADDR
 *
 *      Returns a near address to the desired procedure within a dynamic link
 *      library module. Calls ldrGetProcAddr to do the work.
 *
 *      ENTRY   sef             System entry frame
 *              ModHandle       Module handle returned by DOSLOADMODULE
 *              Ordinal         Ordinal number of procedure (can be 0)
 *              pchName         ASCIIZ name of procedure.
 *                              Not used if Ordinal != 0
 *              pAddress        Pointer to where the address should be returned.
 *
 *      EXIT    NO_ERROR
 *              ldrGetProcAddr error code.
 */
int cdecl VR32QueryProcAddr( THUNKSEF    sef,
                             VMHOB       hMTE,
                             int         Ordinal,
                             PCHAR       pchName,
                             ptr_t      *pAddress )
{
    return( LDRGetProcAddr( hMTE,
                            Ordinal,
                            pchName,
                            pAddress,
                            TRUE,
                            NULL ) );

    sef;
}



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

/***EP  VR32FreeResource - Entry point for DOS32FREERESOURCE
 *
 *      DOS32FREERESOURCE given a pointer associated with
 *      with a resource will free that resource.
 *
 *      ENTRY   pAddress        Pointer to a resource
 *
 *      EXIT    NO_ERROR
 *              ldrFreeResource error code.
 */
int cdecl VR32FreeResource( THUNKSEF  sef,
                            ULONG     pAddress )
{
    VMOD                od;      // object info buffer
    int                 rc;
    register ldrsmte_t *pSMTE;   // pointer to the owner MTE
    register ldrmte_t  *pMTE;    // pointer to the owner MTE
    ulong_t             ObjNum;  // number of the object in the module
    ldrote_t           *pote;
    ldrste_t           *pste;

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

    // Validate that address is in Shared or Private arena.
    if (!(   VMPrvArena( pAddress, pPTDACur )
          || VMShrArena(pAddress) ))
    {
        rc = ERROR_INVALID_PARAMETER;
        goto VR32FreeResource_exit;
    }

    // Interrogate VM as to the owner of the resource
    od.od_laddr = pAddress;
    od.od_pPTDA = pPTDACur;
    if (rc = VMGetOd( NA, SSToDS(&od)) )
    {
        goto VR32FreeResource_exit;
    }

    // A resource object must be shared.
    if ( !VMShrContOd(od) )
    {
        rc = ERROR_INVALID_PARAMETER;
        goto VR32FreeResource_exit;
    }

    // Validate that this is not an object allocated by the user.
    if ( VMApiOd(od) )
    {
        rc = ERROR_INVALID_PARAMETER;
        goto VR32FreeResource_exit;
    }

    // Find the pointer to the MTE handle for this resource, and then the
    // object number within that module
    if (!( pMTE = ldrValidateMteHandle( VMMTEOd(od) ) ))
    {
        rc = ERROR_INVALID_PARAMETER;
        goto VR32FreeResource_exit;
    }
    pSMTE = pMTE->mte_swapmte;

    ObjNum = 0;
    rc = ERROR_INVALID_PARAMETER;
    while ( (rc) && (ObjNum < pSMTE->smte_objcnt) )
    {
        // 32-bit module
        if ( ldrIsLX(pMTE) )
        {
            pote = &((ldrote_t *)pSMTE->smte_objtab)[ObjNum];
            if (    ( (unsigned long)pAddress >=  pote->ote_base )
                 && ( (unsigned long)pAddress <=  pote->ote_base + pote->ote_size ) )
                rc = NO_ERROR;
        }

        // 16-bit module
        else
        {
            pste = &((ldrste_t *)pSMTE->smte_objtab)[ObjNum];
            // don't even try this for a null selector; ok to use the tiled
            // macro as resources are not packed
            if (pste->ste_selector)
                if (    ( (unsigned long)pAddress >= SelToLaTiled(pste->ste_selector) )
                     && ( (unsigned long)pAddress <= SelToLaTiled(pste->ste_selector) + pste->ste_minsiz ) )
                    rc = NO_ERROR;
        }
        ObjNum++;
    }
    ObjNum--;
    // If not found, return. This implies that VM and Ldr are inconsistent ?
    if (rc)
        goto VR32FreeResource_exit;

    // Validate that this object is a resource.  Note that to call
    // LDRIsResource the object must exist in the mte for a module
    // and the handle must not be zero.
    if ( rc = LDRIsResource( VMHobOd(od),
                             VMMTEOd(od),
                             pPTDACur->ptda_rsrclist ) != NO_ERROR )
    {
        goto VR32FreeResource_exit;
    }

    // decrement the useage count and free if necessary
    if (rc = VMFreeMem( od.od_laddr, pPTDACur->ptda_handle, NA ) )
    {
        rc = NO_ERROR;   // not an error
        goto VR32FreeResource_exit;
    }

    // if the useage count got to zero, zero the entries in the object table
    od.od_laddr = pAddress;
    od.od_pPTDA = pPTDACur;
    if ( VMGetOd( VMOD_ANY_CTXT, SSToDS(&od)) )
    {
        // 32-bit module
        if ( ldrIsLX(pMTE) )
        {
            pote->ote_base = 0L;
            pote->ote_selector = 0;
            pote->ote_handle = HOBNULL;
            pote->ote_flags &= ~OBJALLOC;
        }
        else  // 16-bit module
        {
            pste->ste_selector = 0;
            pste->ste_seghdl = 0;
        }
    }

VR32FreeResource_exit:
    LDRClearSem();
    TKClearKernelSem();
    return(rc);

    sef;
}


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

/***EP  VR32QueryResourceSize - Entry point for DosQueryResourceSize
 *
 *      DOSQUERYRESOURCESIZE returns size of the resource associated with
 *      the passed type/name ID pair in the passed module.
 *
 *      ENTRY   hmod            Module handle returned by DOSLOADMODULE
 *              typeID          Type id
 *              nameID          Name id
 *              psize           Pointer to where the size should be returned.
 *
 *      EXIT    int             return code (NO_ERROR if successful)
 *
 */
int cdecl VR32QueryResourceSize( THUNKSEF        sef,
                                 VMHOB           hmte,
                                 ulong_t         typeid,
                                 ulong_t         nameid,
                                 ulong_t         *psize )
{
    ldrmte_t           *pmte;          /* pointer to MTE */
    register ldrsmte_t *psmte;         /* pointer to swappable MTE */
    ULONG               lrsrc;
    ulong_t             lrsrcsize;
    int                 rc;

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

#ifdef LDRSTRICT
    if (DDInitTime)
        panic("QRS:DDInitTime");
#endif

    if (hmte == NULL)
        hmte = pPTDACur->ptda_module;

    if ((rc = ldrSetupResource(hmte, SSToDS(&pmte))) == NO_ERROR)
    {

        rc = ERROR_INVALID_PARAMETER;
        psmte = pmte->mte_swapmte;
        lrsrcsize = 0;

        for (lrsrc=0; lrsrc < psmte->smte_rsrccnt; lrsrc++)
        {
            if (ldrIsNE(pmte))
            {
                if (( ((ldrrsrc16_t *) psmte->smte_rsrctab)[lrsrc].ldrrsrc16_type
                                    == (USHORT) typeid) &&
                (((ldrrsrc16_t *) psmte->smte_rsrctab)[lrsrc].ldrrsrc16_name
                                    == (USHORT) nameid) ) {
                do {
                    lrsrcsize +=
                      (((ldrste_t *) psmte->smte_objtab)[psmte->smte_objcnt -
                      psmte->smte_rsrccnt + lrsrc].ste_minsiz == 0) ? _64K :
                      (ULONG) ((ldrste_t *)
                      psmte->smte_objtab)[psmte->smte_objcnt -
                      psmte->smte_rsrccnt + lrsrc].ste_minsiz;
                    lrsrc++;
                }
                while
                  (( ((ldrrsrc16_t *) psmte->smte_rsrctab)[lrsrc].ldrrsrc16_type
                                    == (USHORT) typeid) &&
                  (((ldrrsrc16_t *) psmte->smte_rsrctab)[lrsrc].ldrrsrc16_name
                                    == (USHORT) nameid) );


                    rc = TKSuDWord(lrsrcsize, psize);
                    break;
                }
            }
            else
            {  /* module is 32-bit */
                if (( ((ldrrsrc32_t *) psmte->smte_rsrctab)[lrsrc].ldrrsrc32_type
                                    == (USHORT) typeid) &&
                    (((ldrrsrc32_t *) psmte->smte_rsrctab)[lrsrc].ldrrsrc32_name
                                    == (USHORT) nameid) )
                {
                    rc = TKSuDWord(((ldrrsrc32_t *)
                                   psmte->smte_rsrctab)[lrsrc].ldrrsrc32_cb,
                                   psize);
                    break;
                }
            }
        }
    }

    LDRClearSem();
    return(rc);

    sef;
}


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

/***EP  VR32QueryProcType - Entry point for DOS32QUERYPROCTYPE
 *
 *      Returns the procedure type to the desired procedure within a dynamic
 *      link library module.
 *
 *      ENTRY   sef             System entry frame
 *              hmod            Module handle returned by DOSLOADMODULE
 *              Ordinal         Ordinal number of procedure (can be 0)
 *              pchName         ASCIIZ name of procedure.
 *                              Not used if Ordinal != 0
 *              pType           Pointer to where the type should be returned.
 *
 *      EXIT    int             return code (NO_ERROR if successful)
 */
int cdecl VR32QueryProcType( THUNKSEF        sef,
                             VMHOB           hmod,
                             int             Ordinal,
                             PCHAR           pchName,
                             PULONG          pType )
{
   ptr_t   address;

   return (LDRGetProcAddr( hmod,
                           Ordinal,
                           pchName,
                           SSToDS(&address),
                           TRUE,
                           pType));

    sef;
}


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

/***EP  VR32QueryHeaderInfo - Entry point for DOS32QUERYHEADERINFO
 *
 *      DOS32QUERYHEADERINFO will return MTE info for pass mte handle.
 *      See ldrModHdr for fields returned.
 *
 *      ENTRY   sef             System entry frame
 *              hmod            Module handle returned by DOSLOADMODULE
 *              lindex          index to start at
 *              pbuf            pointer to buffer to return info in
 *              lcb             number of bytes to return from start position.
 *              subfunc         number of sub-function to process
 *
 *      EXIT    int             return code (NO_ERROR if successful)
 */
int cdecl VR32QueryHeaderInfo( THUNKSEF  sef,
                               VMHOB     hmod,
                               int       lindex,
                               UCHAR    *pbuf,
                               int       lcb,
                               int       subfunc )
{
    int                     i;
    register ldrmte_t      *pmte;  /* pointer to a module table entry */
    register ldrsmte_t     *psmte; /* pointer to swappable MTE */
    ulong_t                 psrc;
    int                     count;
    UCHAR                   *pdst;
    USHORT                  size;
    ldrste_t                *pste;
    ldret_t                 *pbndl;
    ldr32et_t               *pent;
    ushort_t                entord;
    int                     rc;

    /*
     * Validate Handle as a handle to a MTE for sub-functions:
     *      HEADER_EXEINFO
     *      HEADER_READRSRCTBL
     *      HEADER_READSECTOR
     *      HEADER_FIXENTRY
     *      HEADER_STE
     *      HEADER_MAPSEL
     */

    if (subfunc != HEADER_LIBPATHLEN && subfunc != HEADER_LIBPATH)
    {
        // map and validate mte handle
        if ( !(pmte = ldrValidateMteHandle(hmod) ) )
            return( ERROR_INVALID_HANDLE );

        psmte = pmte->mte_swapmte;
    }

    switch (subfunc)
    {

    case HEADER_EXEINFO:

        pdst = pbuf;
        count = 0;

        for (i = 0; HeaderInfoTbl[i].offset != ENDMTETBL; i++)
        {
                                    /* check if we should copy data */
            if ( i >= lindex
              && (count += (short) HeaderInfoTbl[i].cnt) <= lcb )
            {   /*
                 * special case for size of resource table
                 */
                if (i == 4)
                {
                    if (psmte->smte_rsrctab != 0)
                        size = (USHORT) (psmte->smte_restab -
                                         psmte->smte_rsrctab);
                    else
                        size = 0;
                    psrc = (ulong_t) SSToDS(&size);
                }
                else
                {
                    psrc = (ulong_t) pmte + HeaderInfoTbl[i].offset;
                }

                if ((rc = TKSuBuff(pdst,
                                  (UCHAR *) psrc,
                                  HeaderInfoTbl[i].cnt,
                                  TK_UM_FATAL)) != NO_ERROR)
                    return(rc);
            }

            /*
             * update pointer to user buffer
             */
            pdst += HeaderInfoTbl[i].cnt;

            /*
             * see if we are done copying info
             */
            if (count >= lcb)
                break;
        }
        break;

    case HEADER_READRSRCTBL:
        rc = TKSuBuff(pbuf,
                      (UCHAR *) psmte->smte_rsrctab + lindex,
                      lcb,
                      TK_UM_FATAL);
        break;

    case HEADER_READSECTOR:
        if ((rc = VMAllocKHB(VM_HKH_PUB_SWAPRW, lcb, (ULONG) LDRNONRESOWNER,
                             NA, SSToDS(&pdst))) != NO_ERROR)
        {
            break;
        }
        rc = ldrRead( pmte->mte_sfn,
                      lindex,
                      pdst,
                      NULL,
                      lcb,
                      pmte );

        if (rc == NO_ERROR)
        {
            rc = TKSuBuff(pbuf, pdst, lcb, TK_UM_FATAL);
        }
        VMFreeKHB(VM_HKH_PUB_SWAPRW, pdst);
        break;

    case HEADER_LIBPATHLEN:
        if (lcb != 4)
        {
            rc = ERROR_INVALID_PARAMETER;
            break;
        }

        rc = TKSuBuff(pbuf, (void *) &LDRLibPathLen, lcb, TK_UM_FATAL);

        break;

    case HEADER_LIBPATH:
        rc = TKSuBuff(pbuf, LDRLibPath, LDRLibPathLen, TK_UM_FATAL);
        break;

    case HEADER_FIXENTRY:

        /*
         * Module must be a 16-bit MTE
         */
        if (!ldrIsLX(pmte))
            return(ERROR_INVALID_PARAMETER);

        pbndl = (ldret_t *) psmte->smte_enttab;
        entord = (ushort_t) lcb;
        entord--;                           /* Normalize ordinal number */

        while (TRUE)
        {
            if (pbndl->et_count == 0)       /* check for end of table */
            {
                return(ERROR_INVALID_ORDINAL);
            }

            if (entord < pbndl->et_count)   /* is ordinal in this bundle? */
                break;

            entord -= pbndl->et_count;
            pbndl = (ldret_t *)((char *) pbndl + ldrSkipEnts( (uchar_t)((pmte->mte_flags2)&MTEFORMATNE),
                                                              pbndl->et_type,
                                                              pbndl->et_count));
        }

        if (pbndl->et_type == EMPTY)        /* check for empty bundle */
            return(ERROR_INVALID_ORDINAL);

        pent = (ldr32et_t *)((char *) pbndl + ldrSkipEnts( (uchar_t)((pmte->mte_flags2)&MTEFORMATNE),
                                                           pbndl->et_type,
                                                           (uchar_t) entord));

        pent->et_cgoff = (ushort_t) lindex;

        break;

    case HEADER_STE:

        /*
         * Module must be a 16-bit MTE and module must contain segment
         * number.
         */
        if (!ldrIsNE(pmte))
            return(ERROR_INVALID_PARAMETER);

        if ((ULONG) lindex > psmte->smte_objcnt)
            return(ERROR_INVALID_SEGMENT_NUMBER);

        pdst = pbuf;
        count = lcb;

        pste = ((ldrste_t *) psmte->smte_objtab) + lindex - 1;

        for (i = 0; HeaderInfoSTE[i].offset != ENDMTETBL; i++)
        {

            /*
             * Only copy lcb bytes of info
             */
            if (count <= 0)
                break;

            if ((rc = TKSuBuff(pdst,
                               (UCHAR *) pste + HeaderInfoSTE[i].offset,
                               HeaderInfoTbl[i].cnt,
                               TK_UM_FATAL)) != NO_ERROR)
                    return(rc);
            pdst += HeaderInfoSTE[i].cnt;
            count -= HeaderInfoSTE[i].cnt;
        }

        break;

    case HEADER_MAPSEL:

        /*
         * Module must be a 16-bit MTE
         */
        if (!ldrIsNE(pmte))
            return(ERROR_INVALID_PARAMETER);

        for (i = 0; i < (int) psmte->smte_objcnt; i++)
        {
            pste = ((ldrste_t *) psmte->smte_objtab) + i;
            if (pste->ste_selector == (SEL) lindex)
            {
                i++;                        /* make number 1 based */
                if ((rc = TKSuBuff(pbuf,
                                   SSToDS(&i),
                                   sizeof(ushort_t),
                                   TK_UM_FATAL)) != NO_ERROR)
                    return(rc);
                break;
            }
        }

        if (i == (int) psmte->smte_objcnt)
            return(ERROR_INVALID_PARAMETER);

        break;

    default:
        rc = ERROR_INVALID_PARAMETER;
        break;

    }

    return(rc);

    sef;
}



/***EP  VR32InitializePorthole - Worker routine for DosInitializePortHole API
 *
 *      This accepts a routine address which is to be called before any
 *      of the library initialization routines are called for a WINDOWS
 *      program. If the API is called the first time, the PortHole MTE
 *      is created and initialized.
 *
 *      ENTRY   sef             System Entry Frame
 *              pfnphinit       PortHole initialization routine address
 *              lenttype        entry type of Library routine
 *                              PT_16BIT or PT_32BIT.
 *
 *      EXIT    NO_ERROR        SUCCESS
 *                              The routine address is not validated.
 *                              A bum address will fault the WINDOWS APP.
 *              Error from VMAllocKHB, VMCreatePseudoHandle
 *
 */
int cdecl VR32InitializePorthole( THUNKSEF  sef,
                                  int       (*pfnphinit)(),
                                  ulong_t         lenttype )
{

#if 0
    register ldrote_t *pote;
    ldrsmte_t         *psmte;
    int               rc;
#endif

#ifdef LDRSTRICT
    _asm int 3
#endif

#if 0

    if (pPortHoleMte == NULL)
    {
        /*
         * allocate memory for PortHole MTE
         */
        if ((rc = VMAllocKHB(VM_HKH_PUB_RESRW, sizeof(ldrmte_t) +
                            sizeof(ldrsmte_t), (ULONG) LDRMTEOWNER, NA,
                            &pPortHoleMte)) != NO_ERROR)
            return (rc);

        memset((PCHAR)pPortHoleMte, 0, sizeof(ldrmte_t)+sizeof(ldrsmte_t));

        if ((rc = VMCreatePseudoHandle((void *) pPortHoleMte,
          (ULONG) LDRMTEOWNER,
          &pPortHoleMte->mte_handle)) != NO_ERROR)
        {
            VMFreeKHB (VM_HKH_PUB_RESRW, (PCHAR) pPortHoleMte);
            pPortHoleMte = NULL;
            return (rc);
        }

        psmte = (ldrsmte_t *) ((ulong_t) pPortHoleMte + sizeof(ldrmte_t));
        pPortHoleMte->mte_swapmte = psmte;

        /*
         * Initialize the MTE
         */
        *(PUSHORT)(&pPortHoleMte->mte_flags2) = MTEFORMATLX;
        (PCHAR)(psmte->smte_path) = &pPHMtePath[0];
        (PCHAR)(psmte->smte_restab) = &pPHMteResNam[0];
        pPortHoleMte->mte_flags1  = NOAUTODS | LIBRARYMOD | MTEVALID |
                              CLASS_GLOBAL | MTE_MEDIAFIXED | INSTLIBINIT;
        psmte->smte_startobj = 1;
        psmte->smte_objcnt = 1;
        (ldrote_t *) psmte->smte_objtab = &pPHMteObjTab;
    }

    psmte = (ldrsmte_t *) pPortHoleMte->mte_swapmte;
    psmte->smte_eip = (ulong_t)pfnphinit;
    pote = (ldrote_t *) psmte->smte_objtab;

    if (lenttype & PT_32BIT)
    {
        pote->ote_flags |= OBJBIGDEF;
    }

    return(NO_ERROR);

#endif

    return( ERROR_INIT_ROUTINE_FAILED );

    sef;
    (*pfnphinit)();
    lenttype;
}



/***LP  ldrAllocDD - Allocate memory for Device Drivers
 *
 *      Allocate segment for Device Driver from the kernel resident heap.
 *
 *      ENTRY   pmte                    - pointer to mte
 *              pste                    - pointer to ste
 *              lsize                   - size of segment
 *              lobjnum                 - object number
 *
 *      EXIT    int             return code (NO_ERROR if successful)
 *
 */

int ldrAllocDD( ldrmte_t        *pmte,
                ldrste_t        *pste,
                ULONG           lsize,
                register ULONG  lobjnum )               /* object number */
{
    register int isDD, isData;
    int          rc;
    VMAC         ac;

    isDD   = (pmte->mte_flags1 & DEVDRVMOD);
    isData = (pste->ste_flags & STE_DATA);
    /*
     * Note: Device Drives have the first two segments of their module
     *       always allocated from the Kernel resident heap.  Two heaps
     *       exist for the resident heap and the swappable heap,
     *       read write for data segments and read only for code.
     *       The kernel swappable heap will be
     *       used for the other segments which are being allocated with
     *       GDT selectors (The first two segments always have GDT
     *       selectors).  A Device Drive segment is assigned a GDT selector
     *       by placing the keyword IOPL on the segment definition
     *       statement in the .def file.  The loader checks for this in
     *       ldrexpandsegment and sets the STE_GDTSEG flag in the ste for
     *       that segment.
     *       FSDs segments are all allocated swappable.
     */
    rc = SELAllocKHB(

            /*
             * which heap to allocate from:
             * DATA - for device drivers' first two segs from:
             *      Read-write resident heap.
             *      - for mini ifs, allocate from resident heap
             *      - for others from the Read-write swappable heap.
             * CODE - for device drivers' first two segs from:
             *      Read-only resident heap.
             *      - for mini ifs, allocate from resident heap
             *      - for others from the Read-only swappable heap.
             */
            (VMHKH) (isData ?
            ((isDD && lobjnum <= 2) ? VM_HKRH_PUBRW : VM_HKSH_PUBRW) :
            ((isDD && lobjnum <= 2) ? VM_HKRH_PUBRO : VM_HKSH_PUBRO)),

            /*
             * size of heap segment to allocate:
             */
            lsize,

            /*
             * VM and PG allocation flags:
             *      For DDs first two segs allocate from resident heap and
             *      make data contiugous.
             *      For mini ifs, allocate from resident heap
             *      For others allocate from swappable heap
             */
             ((isDD && lobjnum <= 2) ?
                    PG_RESIDENT | PG_16M : PG_SWAPPABLE) | PG_U |
             ((isDD &&
                    isData && lobjnum <= 2) ? PG_CONTIG : NULL),

            /*
             * Selector Manager flags:
             *      Allocate code as:
             *              code, dpl0, conforming.
             *      Data as:
             *              data, dpl3.
             */
            ((isData ?
                    D_DATA | D_DPL3 : D_CODE | D_DPL0 | D_CONFORM) |
                    ((pste->ste_flags & (ushort_t)STE_ERONLY) ? D_R : D_W)),

            /*
             * Owner handle for Device Drivers set to IDDOWNER
             * (installable device driver) otherwise set to IFS owner
             */

//  147968  (VMHOB) (isDD ? LDRDDOwner : LDRFSDOwner),
            (VMHOB) (pmte->mte_handle),

            /*
             * pointer to returned address
             */
            SSToDS(&ac));


    if (rc == NO_ERROR)
        pste->ste_selector = ac.ac_sel;

    return(rc);
}


/***EP  LDRReallocDD - ReAllocate memory for Device Drivers
 *
 *      ReAllocate segments for Device Driver from the kernel resident heap.
 *
 *      ENTRY   sel                     - GDT selector to realloc
 *              cb                      - new size of segment
 *
 *      EXIT    int             return code (NO_ERROR if successful)
 *
 */

int LDRReallocDD( SEL    sel,
                  ULONG  cb )
{
    int               rc;
    struct  seldesc_s desc;

    if ( rc = SELGetDescInfo( sel,
                              pPTDACur->ptda_handle,
                              SSToDS(&desc) ) )
        return(rc);

    // check if request is to grow heap object
    if ( cb > (desc.desc_cb+1) )
        return(ERROR_INVALID_PARAMETER);

    // Do the shrink. Note that we force the contig flag on here even
    // though it might not have been before - we do not know the status
    // from before. As this is at init time, force contig should not be
    // a problem.
    rc = SELReallocKHB( (VMHKH) ( (desc.desc_acc & D_CODE) ? VM_HKRH_PUBRO
                                                           : VM_HKRH_PUBRW ),
                        sel,
                        cb,
                        PG_RESIDENT | PG_CONTIG,
                        SSToDS( &(desc.desc_laddr) ) );

    ldrAssert(!rc);  // Shrinking must not fail or we have a problem !

    return(rc);

}



/***EP  LDRReallocDD2 - ReAllocate memory for Device Drivers
 *
 *      ReAllocate segments for Device Driver from the kernel swappable heap.
 *
 *      ENTRY   sel                     - GDT selector to realloc
 *              cb                      - new size of segment
 *
 *      EXIT    int             return code (NO_ERROR if successful)
 *
 */

int LDRReallocDD2( SEL             sel,
                   ULONG           cb )
{
    int                     rc;
    struct  seldesc_s       desc;

    if ((rc = SELGetDescInfo(sel, pPTDACur->ptda_handle,
                             SSToDS(&desc))) != NO_ERROR)
        return(rc);

    /*
     * check if request is to grow heap object
     */
    if (cb > (desc.desc_cb+1))
        return( ERROR_INVALID_PARAMETER );

    rc = SELReallocKHB( (VMHKH) 0,
                        sel,
                        cb,
                        PG_SWAPPABLE,
                        SSToDS(&(desc.desc_laddr)) );

    ldrAssert( !rc );

    return( rc );

}


#pragma code_seg()
#pragma code_seg ("SWAP32_27", "CODE")
/***EP  VRFreeModule - API Entry point for DOSFREEMODULE
 *
 *      ENTRY
 *              hMTE    MTE handle of module to be freed
 *
 *      EXIT
 *              NO_ERROR if success
 *              LDRFreeModule error code, if error
 */
int cdecl VRFreeModule( SEF   sef,
                        VMHOB hMTE )
{
    return( LDRFreeModule( hMTE, FALSE ) );

    sef;
}

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

/***EP  LDRCleanupDLLTerm - clean up DLL termination for this thread
 *
 *      This procedure is called to clean up when a thread
 *      that is performing DLL terminations dies without having
 *      finished.  Any existing DLL termination records for this
 *      thread are freed, and ptda_cdllterm is updated accordingly.
 *      The reference counts for all modules thus cleaned up are reset
 *      to one.  Any existing DosDebug free notifications are freed.
 *
 *      LDRCleanupDLLTerm ()
 *
 *      ENTRY   NONE
 *      RETURN  NONE
 */

void LDRCleanupDLLTerm()
{
    VMHOB      hmte;                 /* Module handle */
    dllterm_t *pdt;                  /* DLL termination record pointer */
    ldrdld_t  *pldrdld;              /* Pointer to LoadModule record */

    // While records remain
    while ( pdt = pTSDCur->TSDpDLLTerm )
    {
        // Unlink record from chain
        pTSDCur->TSDpDLLTerm = pdt->dllt_pdlltnext;

        // cannot have just waiting bit - the least sig bit is used as a
        // flag ... that is why inc/dec are by 2s.
        ldrAssert( pPTDACur->ptda_cdllterm > 1 );

        // Decrement reference count
        pPTDACur->ptda_cdllterm -= 2;

        // Get module handle
        hmte = pdt->dllt_ahmod[0];

        // Indicate that DLL is no longer on exit list so that MTE can be freed, #74177
     /* LDRResetDLLOnExitList(hmte);        defect 75809   */

        // Free the data record
        VMFreeKHB( VM_HKH_PUB_SWAPRW, pdt );

        // For each remaining record ...
        pldrdld = pPTDACur->ptda_pldrdldHead;
        while (pldrdld)
        {
            if (pldrdld->ldrdld_hmte == hmte)
            {
                // record found - assert ref count was zero
                ldrAssert( pldrdld->ldrdld_cRef == 0 );

                // reset count to 1, and we are done
                pldrdld->ldrdld_cRef = 1;
                break;
            }
            pldrdld = pldrdld->ldrdld_pldrdldNext;
        }

        // Check we did actually find the record
        ldrAssert( pldrdld );

    }

    // clean up pending debug notifications
    while ( pTCBCur->TCBpLibiFree )
    {
        TKLibiCleanupUnload();
    }

    // If time to wake waiters
    if ( pPTDACur->ptda_cdllterm == 1 )
    {
        // Clear the waiting flag
        pPTDACur->ptda_cdllterm = 0;

        // Wake up the waiters
        TKWakeup( &pPTDACur->ptda_cdllterm,
                  0,
                  NULL );
    }

}


/***EP  LDRResetDLLOnExitList - reset the MTEDLLONEXTLST flag
 *
 *      ENTRY
 *              hMTE    MTE handle of module whose flag is to be reset
 *
 *      EXIT
 *              None
 *
 *void LDRResetDLLOnExitList( VMHOB hMTE )
 *{
 *   register ldrmte_t   *pmte;            Pointer to module table entry
 *
 *     Make sure handle exists before clearing flag
 *   if ( pmte = ldrValidateMteHandle(hMTE) )
 *        pmte->mte_flags2 &= ~MTEDLLONEXTLST;
 *}
 * This procedure is no longer needed as a result of defect 75809 */


/***LP  ldrDispatchDLLTerm - set up for dispatch to next DLL termination
 *
 *      This procedure sets the kernel and user stacks up to
 *      dispatch to the next DLL termination routine associated
 *      with the current DosFreeModule call, or, if none remain,
 *      it sets up for return from the DosFreeModule call and
 *      cleans up the dllterm data buffer.
 *
 *      ldrDispatchDLLTerm ()
 *
 *      ENTRY   NONE
 *      RETURN  NONE
 */

void ldrDispatchDLLTerm()
{
    int                 cref;         /* Reference count */
    ldrmi_t             dllinfo;      /* DLL info buffer */
    VMHOB               hmte;         /* Module handle */
    dllterm_t          *pdt;          /* Pointer to data buffer */
    int                 rc;           /* Return code */
    KMREG               usrregs;      /* User's registers */
    struct
    {                                 /* User stack on 32-bit DLL entry */
        ULONG s_retaddr;              /* Return address (pfnDLLTermDisp32) */
        ULONG s_hmte;                 /* Module handle */
        ULONG s_fterm;                /* Termination flag (1) */
    }                   usrstk;

    // Get pointer to data buffer ... must have one
    pdt = pTSDCur->TSDpDLLTerm;

    ldrAssert( pdt );

    // more DLL term routines still to call ...
    if ( pdt->dllt_chmod != 0 )
    {
        // Get handle of module to call
        hmte = pdt->dllt_ahmod[pdt->dllt_chmod--];

        // Set up for first dispatch
        tkDeleteExitListRoutine(hmte, TK_TYPEDT);        /* 75809 */

        // Copy user's registers
        *(PKMREG) SSToDS(&usrregs) = pdt->dllt_regs;

        // Get user's ESP
        KMQueryClientRegister( pTCBCur,
                               KM_REG_ESP,
                               SSToDS(&usrregs.kmreg_esp) );

        // Get module info - must be 32-bit as no 16-bit dll term
        rc = LDRGetTKModuleInfo( hmte,
                                 SSToDS(&dllinfo) );

        ldrAssert( !rc );
        ldrAssert( dllinfo.ldrmi_ldtype == LDR_32bit );

        // if it is a 16-bit DosFreeModule call
        if (pdt->dllt_regs.kmreg_cs & SEL_LDTBIT)
        {
            // Detile the user's stack
            // Ok to call tiled - can not be packed
            usrregs.kmreg_esp = SelToLaTiled( usrregs.kmreg_ss )
                                                  + (USHORT) usrregs.kmreg_esp;

            // Use flat DS
            usrregs.kmreg_ds = usrregs.kmreg_es = usrregs.kmreg_ss  = R3DS;
        }

        // Set up flat dispatch
        usrregs.kmreg_cs  = R3CS;
        usrregs.kmreg_eip = dllinfo.ldrmi_startaddr.ptr_flat;

        // Set up FS to reference TIB
        usrregs.kmreg_fs  = R3TIB;

        // Zero other registers
        usrregs.kmreg_ebx = usrregs.kmreg_ecx
                          = usrregs.kmreg_edx
                          = usrregs.kmreg_esi
                          = usrregs.kmreg_edi
                          = usrregs.kmreg_ebp
                          = usrregs.kmreg_gs
                          = 0;

        // Set registers on kernel stack
        KMSetAllClientRegisters( pTCBCur,
                                 SSToDS(&usrregs) );

        //  Return to ring 3 dispatcher
        usrstk.s_retaddr = (ulong_t) pfnDLLTermDisp32;

        // Set handle
        usrstk.s_hmte = hmte;

        // Set flag
        usrstk.s_fterm = 1;

        // Push frame on user's stack
        KMPushClientStack( pTCBCur,
                           sizeof(usrstk),
                           SSToDS(&usrstk),
                           TK_UM_FATAL );

    } // end of more DLL term routines to call

    else
    {
        // No more DLL term routines to call, so set up for the return
        // from DosFreeModule. At this point, we have done all the DLL
        // term routines.

        // get the loader semaphore - can not let error interrupt blow us
        // away here.
        while ( (rc = LDRGetSem() ) == ERROR_INTERRUPT );
        ldrAssert( !rc );

        // Mark set of modules to detach
        ldrFindDetach( pdt->dllt_ahmod[0], pPTDACur, 0 );

        // Free runtime attachment record
        cref = ldrDecDLDRecord( pdt->dllt_ahmod[0], pPTDACur, TRUE );

        ldrAssert( !cref );

        // Free references to module in LDT
        ldrLibDetach();

#ifdef INCDEBUG
        // Notify libinit all modules freed
        TKLibiUnloadDone();
#endif

        // Clear graph - BUGBUG - redundant code ???? - already clear ????
        ldrClearGraph();

        // Unlink record from chain
        pTSDCur->TSDpDLLTerm = pdt->dllt_pdlltnext;

        // Cannot have just waiting bit - the least sig bit of the count is
        // actually a flag.
        ldrAssert( pPTDACur->ptda_cdllterm > 1 );

        // is it time to wake waiters ?
        if ( (pPTDACur->ptda_cdllterm -= 2) == 1 )
        {
            // Must have been last record
            ldrAssert( pTSDCur->TSDpDLLTerm == NULL );

            // Clear the waiting flag
            pPTDACur->ptda_cdllterm = 0;

            // Wake up the waiters
            TKWakeup( &pPTDACur->ptda_cdllterm,
                      0,
                      NULL );
        }

        // Reset user registers
        KMSetAllClientRegisters( pTCBCur, &pdt->dllt_regs );

        // Free the data record
        VMFreeKHB( VM_HKH_PUB_SWAPRW, pdt );

        LDRClearSem();
    }
}




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

/***LP  ldrCheckDLLTerm - check if DLL termination in progress
 *
 *      This function checks if DLL termination is in progress
 *      in the given process.  If it is for the current thread,
 *      then this function returns ERROR_INVALID_FUNCTION.  If
 *      it is, but not for the current thread, then the current
 *      thread is blocked until all DLL termination is complete.
 *
 *      ldrCheckDLLTerm ()
 *
 *      ENTRY   NONE
 *      RETURN  int             - error code (NO_ERROR if successful)
 *
 *      CALLS   TKSleep, TKClearKernelSem, TKTakeKernelSem
 *
 *      WARNING This function assumes the calling thread owns the
 *              kernel semaphore for this process.  It also relies
 *              on the fact that an attempt to free the kernel
 *              semaphore by a thread other than the owner is a
 *              no-op.
 *
 *      EFFECTS Current thread may sleep.  If sleep is interrupted,
 *              then thread will no longer own kernel semaphore on
 *              exit.
 */

int ldrCheckDLLTerm()
{
    int rc = NO_ERROR;

    // if DLL termination already in progress, wait for it to complete
    if ( pPTDACur->ptda_cdllterm )
    {

        // Not just waiting bit set
        ldrAssert( pPTDACur->ptda_cdllterm != 1 );

        // Error if active for this thread
        if ( pTSDCur->TSDpDLLTerm != NULL )
            return(ERROR_INVALID_FUNCTION);

        // Loop to wait for completion
        do
        {
            // Set waiting bit
            pPTDACur->ptda_cdllterm |= 1;

            // Sleep until done
            rc = TKSleep( &pPTDACur->ptda_cdllterm,
                          -1L,
                          0,
                          0 );

        } while (     rc == NO_ERROR
                   && pPTDACur->ptda_cdllterm != 0 );
    }

    return(rc);
}


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

/***LP  ldrSetupDLLTerm - set up DLL termination dispatch
 *
 *      This function determines which modules about to be freed
 *      require DLL termination notification and sets up to
 *      dispatch to them.
 *
 *      ldrSetupDLLTerm (hmte)
 *
 *      ENTRY   hmte            - MTE handle of module being DosFreeModule'd
 *      RETURN  int             - error code
 *                                  NO_ERROR if setup complete
 *                                  ERROR_NO_DLL_TERM if nothing to do
 *                                  Others for other errors
 *
 *      CALLS
 *
 *      WARNING The low bit of ptda_cdllterm is used as a waiting bit.
 *              Thus, the counter is incremented by two to show a new
 *              session.  The zeroth entry of dllt_ahmod is used to
 *              store the handle of the module for which DosFreeModule
 *              was called.
 *
 *      EFFECTS
 */

int ldrSetupDLLTerm( VMHOB hmte )
{
    int        cref;            /* Reference count */
    dllterm_t *pdt;             /* Pointer to data buffer */
    int        rc;

    if ( rc = ldrEnumDLLTerm( SSToDS(&pdt) ) )
        return(rc);

    // Remember who started this
    pdt->dllt_ahmod[0] = hmte;

    // Insert new level at head of list
    pdt->dllt_pdlltnext = pTSDCur->TSDpDLLTerm;

    // Save pointer in TSD
    pTSDCur->TSDpDLLTerm = pdt;

    // Get user's registers
    KMQueryAllClientRegisters( pTCBCur, &pdt->dllt_regs );

    // If DosFreeModule from ring 3 - MUST call DLL terms from ring 3.
    if ( ( pdt->dllt_regs.kmreg_ss & SEL_RPLMASK ) == SEL_RPL3 )
    {
        // Get user's return ESP for future
        KMQueryClientReturnESP( pTCBCur, &pdt->dllt_regs.kmreg_esp );

        ldrDispatchDLLTerm();

        // One more active session. Yes, +2 ... the low bit is used as a flag.
        pPTDACur->ptda_cdllterm += 2;

        // Mark module as free
        cref = ldrDecDLDRecord( hmte,
                                pPTDACur,
                                FALSE );

        ldrAssert( cref == 0 );
    }

    else  // Else invalid ring for termination
    {
        // Delete new level from list
        pTSDCur->TSDpDLLTerm = pdt->dllt_pdlltnext;
        VMFreeKHB( VM_HKH_PUB_SWAPRW, pdt );
        rc = ERROR_INVALID_DLL_INIT_RING;
    }

    return(rc);

}

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


/***EP  LDRFreeModule - Detach the module from this process
 *
 *      The worker routine for DosFreeModule. Called by the API worker to
 *      do the actual work. Also called by Libinit to free the module in case
 *      of initialisation errors.
 *
 *      If the last reference to this module goes away, then the MTE is freed
 *      through garbage collection.
 *
 *      ENTRY           hMTE    Module handle to free
 *                      Force   if TRUE, then TKLibiCheckRef is not called.
 *
 *      EXIT            error code (NO_ERROR, if successful)
 */

int LDRFreeModule( register VMHOB  hMTE,
                   int             Force )
{
    ulong_t             cref;
    int                 rc;
    register ldrmte_t  *pmte;    // pointer to module table entry
    ldrdld_t           *pdld;    // pointer to the demand load record
    ldrdld_t          **ppdld;   // pointer to the previous pointer

    // optimisation for tasking - we might get called frequently with hMTE = 0
    if (!hMTE)
        return( ERROR_INVALID_HANDLE );

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

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

    // Check the module handle is valid.
    if ( !(pmte = ldrValidateMteHandle( hMTE ) ) )
    {
        rc = ERROR_INVALID_HANDLE;
        goto ldrFreeModuleExit;
    }

    // If this is a device driver, force the free right here and now, and
    // the return. The loader used to be called by sysinit to "free" a
    // device driver, when all that was wanted was to close the file. This
    // has been changed to close the file after the load, and sysinit
    // only calls us to free a device driver when it really wants it freed.
    // So, in this case we will free the device drivers memory.

    // The above assumed that we were only called by sysinit, thus it
    // allowed device drivers to be unloaded from other sources, so
    // now we are more careful and check the force flag before allowing
    // a device driver to be freed.  77939 ceb

    if ( pmte->mte_flags1 & (DEVDRVMOD + FSDMOD) )
    {
        // Check if this is not from sysinit - fail  77939 ceb
        if (!Force)
        {
           rc = ERROR_INVALID_HANDLE;
           goto ldrFreeModuleExit;
        }

        // Be optimistic - we can, after, all do nothing if this fails
        rc = NO_ERROR;

        // Defect 79535
        //Zero out ptda_module (was filled in with mte_handle for
        //lie list processing) so we don't trap in _ldrmarkingraph later
        pPTDACur->ptda_module = 0;

        // blast the mte out of the various mte structures
        ldrUnlinkMTE(pmte);

        // and blast the mte into the bit bucket - this also frees the
        // storage for the code/data
        ldrFreeMTE(pmte);

        // and blast the dld record in the ptda
        pdld = pPTDACur->ptda_pldrdldHead;
        ppdld = &pPTDACur->ptda_pldrdldHead;
        while ( (pdld) && (pdld->ldrdld_hmte!=hMTE) )
        {
            ppdld = &pdld->ldrdld_pldrdldNext;
            pdld = pdld->ldrdld_pldrdldNext;
        }
        if (pdld)
        {
            *ppdld = pdld->ldrdld_pldrdldNext;
            VMFreeKHB( VM_HKH_PUB_RESRW, pdld );
        }

        // and blast the dld record in the Kernel Demand Load reference list
        if (pdld = pldrdldHeadKernel)
        {
            ppdld = &pldrdldHeadKernel;
            while (pdld->ldrdld_hmte!=hMTE)
            {
                ppdld = &pdld->ldrdld_pldrdldNext;
                pdld = pdld->ldrdld_pldrdldNext;
                if (!pdld)  // not in the list, so all done
                    goto ldrFreeModuleExit;
            }
            *ppdld = pdld->ldrdld_pldrdldNext;
            VMFreeKHB( VM_HKH_PUB_RESRW, pdld );
        }

        goto ldrFreeModuleExit;

    } // end of if device driver

    // If the handle we have to free is handle to the DOSCALLS MTE,
    // we replace the DOSCALLS hMTE with the DOSCALL1 hMTE.  This is
    // because when the user loaded DOSCALLS, we attached to DOSCALL1.
    if (hMTE == DosModHdl)
        hMTE = hMteDoscall1;

    // Check that the module was DosLoadModule-ed ... can not DosFreeModule
    // a DLL that was only imported.
    if ( rc = ldrWasLoadModuled( hMTE, pPTDACur, SSToDS(&cref) ) )
        goto ldrFreeModuleExit;

    // If not the last attachment, decrement the useage count and return
    if (cref > 1)
    {
        ldrDecDLDRecord( hMTE, pPTDACur, TRUE );  // decrement the count
        goto ldrFreeModuleExit;
    }

    // Check if this MTE is recorded for libinit. If it is then we
    // cannot free this MTE. See tklibi.c.
    if (    !Force
         && ( (rc = TKLibiCheckRef(hMTE)) != NO_ERROR)  )
        goto ldrFreeModuleExit;

    // Find all the modules to be detached, but do not actually do the detach.
    // Marks the graph. Sets ldrcDLLDetach.
    ldrFindDetach( hMTE, pPTDACur, 0 );

    // Check to see if any exit lists are in the way and we can not therefore
    // do the free; then set up the DosDebug notification packet if required.
    // TKCheckExitListRef will, by callback to the loader, determine whether
    // or not to return the infamous ERROR_INVALID_ACCESS return code.
    if (    ( rc = TKCheckExitListRef() ) == NO_ERROR
         && ( rc = TKLibiSetupUnload(ldrcDLLDetach) ) == NO_ERROR )
    {

        switch ( rc = ldrSetupDLLTerm( hMTE ) )
        {

            // No DLL termination to do
            case ERROR_NO_DLL_TERM:

                // Blast the dld record. Bad news if the returned ref count > 0.
                // If this was the only process with a reference to it, then
                // the garbage collection will zap the module on the way out
                // of the loader semaphore.
                cref = ldrDecDLDRecord( hMTE,
                                        pPTDACur,
                                        TRUE );
                ldrAssert(cref == 0);

                // Free references to module in LDT
                ldrLibDetach();

#ifdef INCDEBUG
                // Notify debugger all modules freed
                TKLibiUnloadDone();
#endif

                // At this point, the only failure would have been in the
                // ldrDecDLDRecord above ... and that would be an internal error.
                rc = NO_ERROR;
                break;

            // Termination set up successfully
            // Defect 68448 - Virginia Romero - OS/2 CHANGE TEAM
            // Defect 74177 - V.Romero
            case NO_ERROR:
            /*  if ( tkDeleteExitListRoutine(hMTE, TK_TYPEDT) == NO_ERROR )   75809 */
            /*     pmte->mte_flags2 &= ~MTEDLLONEXTLST;                       75809 */
                break;

            // Some genuine error
            default:
                // Clean up unload record and pass the return code back.
                TKLibiCleanupUnload();
                break;

        }
    }

ldrFreeModuleExit:

    // All done - clear the graph to be sure nice and tidy for the next one.
    ldrClearGraph();

    // Release the semaphores and get out.
    LDRClearSem();
    TKClearKernelSem();

    // If libinit has delayed freeing of the module, return NO_ERROR.
    // See task\tklibi.c
    //
    // Dangerous !! If the DLL has not yet gone away, do not tell the caller
    // that it has.
    //
//  if (rc == ERROR_DELAY_FREE)
//      rc = NO_ERROR;

    // Added to stop system if HPFS chkdsk is about to corrupt the disk.
    //
    // Note - stop both retail and debug systems. This is a last ditch panic
    //        to prevent HPFS disk corruption.
    //
    if (    (DDInitTime)
         && (rc==ERROR_INVALID_ACCESS) )
    {
        while (TRUE)
        {
            panic("Invalid free at init time.");
            _asm hlt
            _asm hlt
        }
    }

    return (rc);
}

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

/***EP  VR32DLLTerm - DLL termination dispatcher
 *
 *      Set up for dispatch to the next DLL termination routine.
 *
 *      ENTRY   sef             - System entry frame
 *
 *      EXIT    NO_ERROR
 */

int cdecl VR32DLLTerm( SEF sef )
{
    // Kill process if erroneous call
    if ( pTSDCur->TSDpDLLTerm == NULL )
        return( TKUsrMemGPFault(0) );

    // Set up for next dispatch
    ldrDispatchDLLTerm();

    // 84537 - return INIT_ROUTINE_FAILED error code if applicable
    if (pTCBCur->TCBLibiFlags & INIT_ROUTINE_FAILED) {
//      pTCBCur->TCBLibiFlags &= ~INIT_ROUTINE_FAILED;    133454
        return(ERROR_INIT_ROUTINE_FAILED);
    }
    return(NO_ERROR);

    sef;
}




/***EP  VR32ICacheModule - DosReplaceModule worker
 *
 *  Worker for DosReplaceModule.
 *
 *  Make sure that the module name string is accessible and then copy
 *  it into resident memory for future use.  Then call ldrForceResModule
 *  to do the rest of DosReplaceModule.
 *
 *  int VR32ICacheModule(SEF sef, UCHAR *pszmodulename)
 *
 *  ENTRY   sef - system entry frame
 *          pszmodulename - pointer to the name of the module to cache
 *
 *  EXIT    0 - Successful
 *          Otherwise error code from allocKRHB or ldrForceResModule.
 *
 */

int cdecl VR32ICacheModule( THUNKSEF sef,
                            register UCHAR *pszmodulename )
{
    int           cbnamelen;            // Length of the module name
    register int  rc;                   // Return code
    PUCHAR        pszmodname;           // Resident copy of module name
    struct curdir dummycds;

    // Determine the length of the module name.  Allocate memory from the
    // resident heap for the module name.  Copy the module name into the
    // memory.
    TKFuBuffLenz( SSToDS(&cbnamelen),
                  pszmodulename,
                  512,
                  TK_UM_FATAL );
    if ( rc = allocKRHB( cbnamelen,
                         (ULONG)LDRCACHEOWNER,
                         SSToDS(&pszmodname) ) )
        return(rc);
    TKFuBuffz( NULL,
               pszmodname,
               pszmodulename,
               cbnamelen,
               TK_UM_FATAL );

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

    // Initialize ptda_pCDS
    pTCBCur->TCBpTmpCDS  = (USHORT)&dummycds;

    // Cache the module and close the file.
    rc = ldrForceResModule( pszmodname,
                            cbnamelen );

    // tidy up
    freeKRHB(pszmodname);

    TKClearKernelSem();

    return(rc);

    sef;
}




/***LP  ldrForceResModule - cache and close a module so it can be replaced
 *
 *  This routine is called to cache a module and close its file so that the
 *  file can be replaced.  ldrForceResModule does the work for the
 *  DosReplaceModule API.
 *
 *  int ldrForceResModule(UCHAR *pszmodname, int cbnamelen)
 *
 *  ENTRY   pszmodname - pointer to the name of the module to cache
 *          cbnamelen - length of the module name string
 *
 *  EXIT    0 - Successful
 *          Otherwise, the error code.
 */

int ldrForceResModule( UCHAR *pszmodname,
                       int cbnamelen )
{
    int                 rc;
             ldrmte_t  *pmte;     // Pointer to module's mte
    register ldrsmte_t *psmte;    // Pointer to swappable mte
    unsigned long       FileSz;   // size of the file to be cached
    VMAC                ac;

    // we need the loader semaphore
    if ( rc = LDRGetSem() )
        return(rc);

    if ( rc = ldrFindModule( pszmodname,
                             (ushort_t)cbnamelen,
                             CLASS_ALL,
                             SSToDS(&pmte) ) )
    {
        goto ldrForceResModuleExit;
    }

    if ( !pmte )
    {
        // could not find the module
        rc = ERROR_FILE_NOT_FOUND;
        goto ldrForceResModuleExit;
    }
    psmte = pmte->mte_swapmte;

    // Is the file already closed ? Note return error at request of install.
    if ( SFN_NULL == (short)pmte->mte_sfn )
    {
        rc = ERROR_MODULE_IN_USE;
        goto ldrForceResModuleExit;
    }

    if ( rc = SftFileSize( pmte->mte_sfn,
                           SSToDS(&FileSz) ) )
        goto ldrForceResModuleExit;

    // Create a cache and then copy the file into the cache
    if ( rc = VMAllocMem( FileSz,
                          FileSz,
                          VMS_COMMIT | VMAC_ARENASYS | PG_R | PG_W | PG_SWAPPABLE,
                          HOBNULL,
                          (VMHOB)LDRCACHEOWNER,
                          HOBNULL,
                          0,
                          0,
                          SSToDS(&ac) ) )
    {
        goto ldrForceResModuleExit;
    }
    psmte->smte_pfilecache = ac.ac_va;

    if (rc = ldrRead( pmte->mte_sfn,
                      0,                        // start at offset 0
                      (uchar_t *)psmte->smte_pfilecache,
                      0,
                      FileSz,                   // entire file
                      pmte ) )
    {
#ifdef LDRSTRICT
        ldrAssert( VMFreeMem( psmte->smte_pfilecache, 0, 0 ) );
#else
        VMFreeMem( psmte->smte_pfilecache, 0, 0 );
#endif
        psmte->smte_pfilecache = 0;
        goto ldrForceResModuleExit;
    }

    // Close the file. Set the mte_sfn to SFN_NULL.
    rc = ldrClose( pmte->mte_sfn );
    pmte->mte_sfn = SFN_NULL;


ldrForceResModuleExit:

    LDRClearSem();
    return(rc);
}





/***LP  ldrRegisterDLLTerm - ldrRunGraph subroutine to register DLL
 *                           termination routines in ExitList
 *
 *  This function decrements cDLLTerm each time it is called with a
 *  pointer to an MTE of a module requiring DLL termination notification.
 *  It also calls tkAddExitListRoutine to add the DLL's termination
 *  routine to the ExitList.
 *
 *  ldrRegisterDLLTerm (pmte)
 *
 *  ENTRY   pmte            - MTE pointer
 *  RETURN  ulong_t         - NO_ERROR (always successful)
 *
 *  CALLS   tkAddExitListRoutine
 *
 *  EFFECTS Decrements cDLLTerm; registers DLLs' term routines in ExitList.
 */

// BUGBUG ... from TKLOCAL.H ... pray no one changes TKLOCAL.H ...
#define TK_TYPEDT 2

ulong_t ldrRegisterDLLTerm( ldrmte_t *pmte )
{
    ptr_t handle;

    if (pmte->mte_flags1 & MTEDLLTERM)
    {
        handle.ptr_flat = (ulong_t)pmte->mte_handle;

        // Register the termination routine into the exitlist list.
        // Priority of 0x100 means that we will do termination before
        // exitlist. 0x200 means we will do exitlist before termination.
        // #74177  - V.R. CHANGE TEAM
        // #75809  - L.M. CHANGE TEAM
        /* if ( tkAddExitListRoutine( handle, TK_TYPEDT, 0x200 ) == NO_ERROR ) 75809 */
        /*  pmte->mte_flags2 |= MTEDLLONEXTLST;                                75809 */
        tkAddExitListRoutine( handle, TK_TYPEDT, 0x200 );                   /* 75809 */
        cDLLTerm--;
    }
    return(NO_ERROR);
}



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

/***EP  LDRDLLExitList - register DLL termination routines in ExitList
 *
 *  This routine is called to register any DLL termination routines
 *  before ExitLists for this process.
 *
 *  LDRDLLExitList()
 *
 *  ENTRY   NONE
 *  RETURN  NONE
 *
 *  CALLS   ldrValidateMteHandle, ldrRunGraph, ldrRegisterDLLTerm, and
 *          ldrClearGraph
 */

void LDRDLLExitList()
{
    register ldrmte_t *pmte;
    register VMHOB     hobmte;
    ulong_t            tflags;
    ushort_t           UseCount;

    if (DDInitTime)
        return;

    if ( !( hobmte = pPTDACur->ptda_module ) )
        return;

    pmte = ldrpMTEFromHandle( hobmte );

    if (TKTakeKernelSem() != NO_ERROR)
        return;

    if (LDRGetSem() != NO_ERROR)
    {
        TKClearKernelSem();
        return;
    }

    // We need to trick ldrFindDetach into thinking that the module
    // for this process is really in the process of being detached.
    // To do this, we decrement mte_usecnt and, if it is zero, we set
    // ptda_module to 0 and call ldrFindDetach.  When done, we reset
    // both ptda_module and mte_usecnt back to their original values.

    UseCount = pmte->mte_usecnt;
    pmte->mte_usecnt = 0;

    tflags = pmte->mte_flags1;
    pPTDACur->ptda_module = HOBNULL;   // zero process module
    ldrFindDetach( hobmte, pPTDACur, 1 );
    pPTDACur->ptda_module = hobmte;    // reset process module

    // Register any found DLL termination routines.
    if (cDLLTerm > 0)
    {
        // do the registering
        ldrRunGraph( FALSE, USED, ldrRegisterDLLTerm );

        // check that all routines were registered
        ldrAssert( cDLLTerm == 0 );
    }

    // reset all the bits again
    ldrClearGraph();
    pmte->mte_flags1 = tflags;
    pmte->mte_usecnt = UseCount;

    LDRClearSem();
    TKClearKernelSem();

    return;
}


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

/***EP  VRLdrDirtyWorker - You do not want to know !
 *
 *      Two parameters - command to process
 *                     - pointer, meaning defined by command
 *
 *      Returns        - rc
 */
int cdecl VRLdrDirtyWorker( SEF       sef,
                            ulong_t   Command,
                            void     *pData )
{
    return( Ldr32DirtyWorker( Command, pData ) );

    sef;
}



/***EP  Ldr32DirtyWorker - You do not want to know !
 *
 *      Two parameters - command to process
 *                     - pointer, meaning defined by command
 *
 *      Returns        - rc
 */
int cdecl Ldr32DirtyWorker( ulong_t   Command,
                            void     *pData )
{
    register ldrmte_t *pmte;

    switch (Command)
    {
        // SysInit to call to say specific PDD/IFS init complete
        case LDRDWDDIFSDONE:

            // Reset the module handle in the ptda to switch off the version
            // lie for PDDs and IFSs.
            pPTDACur->ptda_module = 0;

            break;

        // SysInit to call to say all PDD/IFS init complete
        case LDRDWSYIDONE:

            // Close any IFS files. PDDs were closed in ldrrun.
            for (pmte = mte_h; pmte != NULL; pmte = (ldrmte_t *)pmte->mte_link)
            {
                if (    (pmte->mte_flags1 & FSDMOD)
                     || (pmte->mte_flags1 & FSHMOD) )
                {
                    ldrClose( pmte->mte_sfn );
                    pmte->mte_sfn = SFN_NULL;
                }
            }

            break;

        // Command not supported
        default:
            return(ERROR_BAD_COMMAND);

    }

    return(NO_ERROR);

    pData;
}



/*
 *  End of swappable code
 */

#pragma code_seg()

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




/***EP  LDRINITInfo - Return object info for given object number
 *
 *      For the passed MTE handle and object number return the object
 *      flags and selector.
 *
 *      ENTRY   hobmte                  - handle to an mte
 *              lobjnum                 - object number to return info on
 *                                        (This is a one base value)
 *              pobjsel                 - pointer to return object selector
 *              ptype                   - pointer to type of segment
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 */

int LDRINITInfo( VMHOB  hobmte,   /* handle to an mte */
                 ULONG  lobjnum,  /* object number */
                 SEL   *pobjsel,  /* pointer to return selector */
                 UCHAR *ptype )   /* pointer to code 0 or data 1 */
{
    register ldrmte_t   *pmte;          /* pointer to a module table entry */
    register ldrsmte_t  *psmte;         /* pointer to swappable mte */

    // map handle -assume valid for retail
#ifdef LDRSTRICT
    if ( !(pmte = ldrValidateMteHandle(hobmte) ) )
        return( ERROR_INVALID_HANDLE );
#else
    pmte = ldrpMTEFromHandle(hobmte);
#endif

    psmte = pmte->mte_swapmte;

    if (lobjnum > psmte->smte_objcnt)    /* check if object number valid */
        return(ERROR_INVALID_PARAMETER);

    lobjnum--;                      /* make zero based */

    if (ldrIsNE(pmte))              /* handle 16-bit ste entry */
    {
        *ptype = (UCHAR) (((ldrste_t *) psmte->smte_objtab)[lobjnum].ste_flags
                                                                    & STE_DATA);
        *pobjsel = ((ldrste_t *) psmte->smte_objtab)[lobjnum].ste_selector;
    }
    else                            /* handle 32-bit ote entry */
    {
        *ptype = (UCHAR) ((((ldrote_t *)
                 psmte->smte_objtab)[lobjnum].ote_flags & OBJWRITE) >> 1);
        *pobjsel = (SEL) (((ldrote_t *)
                 psmte->smte_objtab)[lobjnum].ote_selector);
    }

    return(NO_ERROR);
}



/***EP  LDRINITSetGDT_DPL0  - setup DPL=0 for GDT selectors in the passed mte
 *
 *      ENTRY   hobmte                  - handle to mte
 *
 *      EXIT    In each entry in the segment table in MTE,
 *                      DPL set to 0 for the GDT selector in ste_selector
 *                      ste_seghdl and ste_selector set to 0
 *
 *      Note:   This function should not be called for the mini-FSD!
 *
 *      PSEUDOCODE
 *
 *              for each entry in segment table do
 *              {
 *                set DPL=0 in the GDT selector;
 *                set ste_seghdl = 0;    * so that DOSFREEMODULE will not
 *                                         cause this segment to be freed
 *                set ste_selector = 0;  * so that DOSFREEMODULE will not
 *                                         cause this selector to be freed
 *              }
 */
void LDRINITSetGDT_DPL0( VMHOB hobmte )
{
    register ldrmte_t  *pmte;
    register ldrsmte_t *psmte;
    register ULONG      i;
    SEL                 sel;
    struct gate        *pdesc;

    // map handle to linear address
    pmte = ldrpMTEFromHandle(hobmte);

    psmte = pmte->mte_swapmte;

#ifdef LDRSTRICT
    if (IsMiniIFS && (pmte->mte_flags1 & FSDMOD))
        panic("LDRINITSetGDT_DPL0: Called for mini-fsd");
#endif

    for (i = 0; i < psmte->smte_objcnt; i++)
    {
        /*
         * set DPL = 0 in the GDT selector and turn the
         * conforming bit off for code segments.  NOTE we rely
         * on the fact that the ExpandDown bit for data (which
         * is overlaid with the Conforming bit for code) is never
         * on, so turning it off for a data segment is a noop and
         * will not break anything.
         */
        if (ldrIsNE(pmte))
            sel = ((ldrste_t *) psmte->smte_objtab)[i].ste_selector;
        else
            sel = ((ldrote_t *) psmte->smte_objtab)[i].ote_selector;

        if (!(sel & SEL_LDTBIT))
        {
            pdesc = (struct gate *) ((ULONG) SELGDTLaddr + (ULONG) sel);
            pdesc->g_access &= ~(D_PRIV+D_CONFORM);

#ifdef LDRSTRICT
            ldrAssert(D_DPL0 == 0);
#endif

            /*
             * clear seghdl and selector in ste only for
             * device drivers and FSDs.
             * Do not do this when RAS is enabled, since the MTE is
             * kept around.
             */
#if RAS
            if ((pmte->mte_flags1 & (DEVDRVMOD | FSDMOD)) &&
                      RASselSTDA == 0)
#else
            if (pmte->mte_flags1 & (DEVDRVMOD | FSDMOD))
#endif /* RAS */
            {
                if (ldrIsNE(pmte))
                {
                    // Keep sel around to make debug and dump analysis easier
                    // ((ldrste_t *) psmte->smte_objtab)[i].ste_selector = 0;
                    ((ldrste_t *) psmte->smte_objtab)[i].ste_seghdl = 0;
                }
                else
                {
                    // Keep sel around to make debug and dump analysis easier
                    // ((ldrote_t *) psmte->smte_objtab)[i].ote_selector = 0;
                    ((ldrote_t *) psmte->smte_objtab)[i].ote_handle = 0;
                }
            }
        } else {
            VMFreeMem( SELSelToLa((ulong_t)sel, pPTDACur) , 0 , 0);
        }
    }
}

#pragma code_seg()
