/*static char *SCCSID = "@(#)ldrfixup.c 6.9 92/03/16";*/
#define SCCSID  "@(#)ldrfixup.c 6.9 92/03/16"
/***    OS/2 Object and segment loading routines
 *
 *      IBM/Microsoft Confidential
 *
 *      All Rights Reserved
 *
 *      The loading routines handle the fixup of
 *      .EXE file format objects and segments.
 *
 *      Defect  Date            Programmer      Description
 *      ------  ----            ----------      -----------
 *      123858  10/30/1995      DBH             Fix fixups for LX-format device
 *                                              and file drivers which use
 *                                              16:16 addresses.
 *
 *      143688  11/13/1995      DBH             Regression; back out 123858    
 *
 *      143833  12/06/1995      DBH             Re-fix 123858, this time 
 *                                              checking to see if pointer to
 *                                              be fixed up is a 16-bit offset.
 */

#define FNO_NUM FNO_LDRFIXUP_C

#define INCL_ERROR_H            /* for bseerr.h */
#define INCL_ERROR2_H           /* for bseerr.h */
#define FOR_EXEHDR      1
#define INCL_TYPES

#include <osconst.h>
#include <ostypes.h>
#include <basedef.h>
#include <fnomem.h>
#include <osmac.h>
#include <mi.h>
#include <bseerr.h>
#include <setjmp.h>
#include <dossymc.h>
#include <seldesc.h>
#include <sigdef.h>
#include <except.h>
#include <pgexport.h>
#include <tkexport.h>
#include <vmexport.h>
#include <selxport.h>
#include <infoseg.h>
#include <curdir.h>
#include <dirent.h>
#include <find.h>
#include <screen.h>
#include <sem.h>
#include <tkptda.h>
#include <newexe.h>
#include <exe386.h>
#include <ldrxport.h>
#include <ldrvars.h>
#include <vmowner.h>
#include <mvdmexp.h>
#include <ordinals.h>
#include <kutil.h>
#include <vmlocal.h>
#include <sysentry.h>

extern struct FarPtr EndFSHMTEENTRY;    /* End of FSHMOD entry table */

#define SIZEOFNRBUF 258             /* 1 length + 255 string + 2 ordinal */
uchar_t nrbuf[SIZEOFNRBUF];

int ldrNoMemGetOrdNum( ldrmte_t *pmte, uchar_t *pprocnam, ushort_t *pentord );


#ifdef LDRSTRICT
  /* 
   * ichain prefix refers to Internal Chaining.
   */
    ulong_t ichain_print=0, ichain_cphyspg; /* debug use only */
#endif

/*
 *  Beginning of swappable code
 */

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



/***LP  ldrEnum32bitRelRecs - Enumerate the 32-bit relocation fixup records
 *
 *      Process each of the fixup records found in the fixup record
 *      table for the logical page number that is passed to this procedure.
 *      Logical page number is the actual index into the Object Page Map Table
 *      as well as the index into the Fixup Page Table.
 *
 *      ENTRY   pmte                    - pointer to module table entry
 *              lobjnum                 - object number
 *              cphyspg                 - index into fixup page table (0 based)
 *              lsrcaddr                - linear address to page to fixup
 *              lsrcobj                 - true linear address of page to fixup
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 *
 *   This procedure performs the following steps:
 *
 *      ldrSetupSrcErrTxt()
 *      compute starting fixup record address
 *      compute address of end of fixup records for page
 *      while (fixup record address != end of fixup records)
 *          get the target address
 *          apply target address to source fixup location
 *          compute size of fixup record
 *          increment pointer to next fixup record
 */

int ldrEnum32bitRelRecs( register ldrmte_t *pmte,     // pointer to module table entry
                         ulong_t            lobjnum,  // number of object table entry
                         ulong_t            cphyspg,  // index into fixup page table
                         ulong_t            lsrcaddr, // pointer to page to fixup
                         ulong_t            lsrcobj,  // true address of source
                         ptda_t            *pptda )
{
    register ldrfrt_t *pfrt;    // pointer to current fixup record
    register ldrfrt_t *pfrtend; // pointer to end of fixup records
    struct  taddr_s    taddr;
    ldrefrt_t          efrt;    // expanded fixup record
    uchar_t           *p;       // pointer to source chain
    ldrsmte_t         *psmte;   // pointer to swappable mte
    int                rc;

   psmte = pmte->mte_swapmte;
#ifdef LDRSTRICT
    ichain_cphyspg = cphyspg;

    if (ichain_print == 1)
	printf("pg#=%4d pptda=%8x %s\n", ichain_cphyspg,
		(unsigned long)pptda,psmte->smte_path);
#endif


    /*
     * Setup source global error text
     */
    ldrSetupSrcErrTxt( (PCHAR)(psmte->smte_restab) + 1,
                       (USHORT)( *(PCHAR)(psmte->smte_restab) ) );

    /*
     * compute start and end pointers for relocation fixups, since the
     * fixup page table specifies an offset into the fixup record table
     * by indexing into the fixup page table with the physical page number.
     * And since C subscripts are zero based the physical page - 1 is the
     * index in to the fixup page table for that physical page.
     * ????? change the cphyspg to be a zero based index passed in (dcr1346)
     */
    pfrt = (ldrfrt_t *) (psmte->smte_frectab +
                                ((ulong_t *) psmte->smte_fpagetab)[cphyspg]);

    pfrtend = (ldrfrt_t *) (psmte->smte_frectab +
                              ((ulong_t *) psmte->smte_fpagetab)[cphyspg+1]);

    /* process each record for this page */

    while (pfrt < pfrtend)
    {

        ldrExpandRecord(pfrt, SSToDS(&efrt), SSToDS(&p));

        if ( rc = ldrGet32TgtAddr( pmte,
                                   lobjnum,
                                   SSToDS(&efrt),
                                   SSToDS(&taddr),
                                   pptda ) )
            return(rc);

        if ( rc = ldrApply32Fixups( lsrcaddr,
                                    SSToDS(&taddr),
                                    SSToDS(&efrt),
                                    SSToDS(&p),
                                    lsrcobj))
            return(rc);

        /*
         * p will point to the next fixup record after it returns from
         * ldrApply32Fixups
         */
        pfrt = (ldrfrt_t *) p;
    }

    return(NO_ERROR);
}


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

/***LP  ldrExpandRecord - Expand fixup record
 *
 *      Expand the fixup record pointed to by pfrt into the r32_expand
 *      structure.
 *
 *      ENTRY   pfrt - pointer to relocation record
 *              pefrt - pointer to place to expand record
 *              ppsrc - place to return pointer to source chain
 *
 *      EXIT    record expanded
 */

void    ldrExpandRecord( ldrfrt_t           *pfrt,
                         register ldrefrt_t *pefrt,
                         uchar_t            **ppsrc )
{
    register uchar_t *pfix;
    register uchar_t efr_flags;

    memset(pefrt, 0, sizeof(ldrefrt_t));        /* zero expanded record */
    pfix = (uchar_t *) pfrt;

    pefrt->efr_stype = *pfix++;                 /* move source type */
    efr_flags = pefrt->efr_flags = *pfix++;     /* move target flags */

    /*
     * move source offset
     */
    if (pefrt->efr_stype & NRCHAIN)
    {
       pefrt->efr_cchain = *pfix++;
    }
    else
    {
       pefrt->efr_srcoff = *(ushort_t *) pfix;
       pfix += sizeof(ushort_t);
    }

    /*
     * move target object or module ordinal
     */
    if (efr_flags & NR16OBJMOD)
    {
       pefrt->efr_tobj = *(ushort_t *) pfix;
       pfix += sizeof(ushort_t);
    }
    else
    {
       pefrt->efr_tobj = (ushort_t) *pfix++;
    }

    /*
     * Move offset for internal or imported reference, but skip offset
     * for internal selector fixups or internal entry table fixup:
     *
     * Move the offset when:
     *    - fixup is import by ordinal
     *    - fixup is import by name
     *    - fixup is internal reference && not a selector fixup
     * Skip the offset when:
     *    - fixup is entry table reference
     *    - fixup is internal reference && is a selector fixup
     */

    if ( (efr_flags & NRRTYP) != NRRENT
      && ((pefrt->efr_stype & NRSRCMASK) != NRSSEG
       || (efr_flags & NRRTYP) != NRRINT) )
    {
       if ( (efr_flags & NRRTYP) == NRRORD &&
            (efr_flags & NR8BITORD) )
       {
          pefrt->efr_tgt = (ulong_t) *pfix++;
       }
       else if (efr_flags & NR32BITOFF)
       {
          pefrt->efr_tgt = *(ulong_t *) pfix;
          pfix += sizeof(ulong_t);
       }
       else
       {
          pefrt->efr_tgt = (ulong_t) *(ushort_t *) pfix;
          pfix += sizeof(ushort_t);
       }
    }

    /*
     * move additive field
     */
    if (efr_flags & NRADD)
    {
       if (efr_flags & NR32BITADD)
       {
          pefrt->efr_add = *(ulong_t *) pfix;
          pfix += sizeof(ulong_t);
       }
       else
       {
          pefrt->efr_add = (ulong_t) *(ushort_t *) pfix;
          pfix += sizeof(ushort_t);
       }
    }

    if (pefrt->efr_stype & NRCHAIN)
    {
       pefrt->efr_srcoff = *(ushort_t *) pfix;
       pfix += sizeof(ushort_t);
    }

    *ppsrc = pfix;
}



/***LP  ldrGet32TgtAddr - Get target address for a fixup record
 *
 *      Process the single 32-bit fixup record passed by caller
 *      and return the target address.
 *
 *      ENTRY   pmte - pointer to source module table entry
 *              lobjnum - source object number
 *              pfrt - pointer to relocation record
 *              ptaddr - pointer to return target address (off,sel,flags)
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 *                                        and target address filled in
 *
 *   This procedure performs the following steps:
 *
 *      - Interpret the relocation record
 *      - For internal fixup get target object and offset from record
 *      - For import by name get ordinal by searching name table
 *      - From the ordinal, including import by ordinal, get target
 *        object and offset from entry table in target mte
 *
 *  ldrGet32TgtAddr(pmte, lobjnum, pfrt, ptaddr)
 *      if (target is internal)
 *          get target address from relocation record
 *      else if (target is imported by ordinal or name)
 *          get target module and ordinal via ldrGetImport
 *          forever
 *              ldrSetupTgtErrTxt()
 *              get entry address from target module entry table
 *              if (entry found for ordinal)
 *                  break
 *              if (target is not DOSCALLS or source is DOSCALL1)
 *                  return error
 *              otherwise try getting address from DOSCALL1 module
 *          InvTgtErrTxt()
 *      else
 *          return error
 */

int ldrGet32TgtAddr( ldrmte_t                *pmte,
                     ulong_t                  lobjnum,
                     register ldrefrt_t      *pefrt,  // pointer to current fixup record
                     register struct taddr_s *ptaddr,
                     ptda_t                  *pptda )
{
    uchar_t    tgt_flags;
    ldrote_t  *pote;
    ldrmte_t  *ptgtmte;
    ushort_t   entord;
    ulong_t    lflags;
    int        rc;

// 143833
    uchar_t    fix_stype;
// 143833


    if ((tgt_flags = (pefrt->efr_flags & (uchar_t) NRRTYP)) == NRRINT)
    {

        /* target is an internal reference */

        if ((pote = ldrNumToOte(pmte, pefrt->efr_tobj)) == 0)
        {
            ldrStop(0, NULL);
            return(ERROR_BAD_EXE_FORMAT);
        }

// 143833 ---------------------------------------------------------------------
// 143833 When dealing with device drivers and file system drivers, the
// 143833 internal addressing is 16:16 rather than 32-bit (except for mixed
// 143833 mode drivers that have 16:16 code and 0:32 data), even when the
// 143833 module format is LX.  When loading a device driver with 16:16 
// 143833 relocation targets, the selector offset should not be added to the
// 143833 target offset to get the fixed-up pointer.
// 143833
        fix_stype = pefrt->efr_stype & (uchar_t)NRSTYP;
        if ((pmte->mte_flags1 & (FSDMOD | DEVDRVMOD)) && 
           (fix_stype == NRSOFF)) {
          /* target reference is a 16-bit offset, selector offset not needed */
          ptaddr->toff = pefrt->efr_tgt;
        } else {
          ptaddr->toff = pote->ote_base + pefrt->efr_tgt;
        } /* endif */
// 143833
// 143833 ---------------------------------------------------------------------

        if (pefrt->efr_stype & NRALIAS)
        {
            if (!(pmte->mte_flags1 & (FSDMOD | DEVDRVMOD)))
            {
                ptaddr->tsel = SELLaToSel( ptaddr->toff,
                                           pptda )
                               | (ushort_t) ((pote->ote_flags & OBJIOPL)
                                                      ? (SEL_LDTBIT|SEL_RPL2)
                                                      : (SEL_LDTBIT|SEL_RPL3) );
            }
            else
            {
                if ((ptaddr->toff = pefrt->efr_tgt) > _64K)
                {
                    ldrStop(1, NULL);
                    return(ERROR_BAD_EXE_FORMAT);
                }
                ptaddr->tsel = pote->ote_selector;
            }
            ptaddr->toff &= (ulong_t) WORDMASK;
        }
        else
            ptaddr->tsel = ldrGetSelector(pmte, pote->ote_flags);

        return(NO_ERROR);
    }
    else if ((tgt_flags == NRRORD) || (tgt_flags == NRRNAM))
    {
        /*
         * Target is imported by ordinal or name.  Get target mte and
         * entry ordinal.
         */
        if ( rc = ldrGetImport( pmte,
                                pefrt->efr_tgt,
                                pefrt->efr_tobj,
                                tgt_flags == NRRNAM,
                                SSToDS(&ptgtmte),
                                SSToDS(&entord) ) )
            return(rc);
        /*
         * entord is the ordinal number for the procedure.
         * get entry address from entry table
         */

        /*
         * get source object flags.
         * First check that source object number exists.
         *
         * NOTE: -  When Demand loading is done for DM2 place a check here
         *          to see if object number is valid. Also a check must be
         *          done for each object to see if it is IOPL or a
         *          conforming object.  If true then turn the bit on in
         *          the MTE to flag that an IOPL or Conforming object is
         *          present in the module.  This is needed when demand
         *          loading of a page is done, if the flag in the MTE is
         *          set then the object number of the block number must
         *          be found.
         */
        if ((pote = ldrNumToOte(pmte, lobjnum + 1)) == 0)
        {
            ldrStop(2, NULL);
            return(ERROR_BAD_EXE_FORMAT);
        }

        lflags = (pefrt->efr_stype & NRALIAS) |
                 (pote->ote_flags & OBJCONFORM) |
                 (pote->ote_flags & OBJIOPL);

        while (TRUE)
        {
            ldrSetupTgtErrTxt((PCHAR) (ptgtmte->mte_swapmte->smte_restab) + 1,
                    (USHORT) (*(PCHAR) (ptgtmte->mte_swapmte->smte_restab)));

            if ( !(rc = ldrGetEntAddr( entord,
                                       ptgtmte,
                                       ptaddr,
                                       lflags,
                                       pmte,
                                       pptda ) ))
            {
                break;              /* found ordinal in entry table */
            }

            if ((ptgtmte->mte_flags1 & DOSMOD) == 0)
            {
                return(rc);
            }

            /*
             * not found in DOSCALLS, try getting address from DOSCALL1
             */
            if ((rc = ldrGetTgtMte(pefrt->efr_tobj,
                                   pmte,
                                   SSToDS(&ptgtmte))) != NO_ERROR)
            {
                return(rc);
            }

            if (ptgtmte == NULL)
            {
                ldrStop(3, NULL);
                return(ERROR_BAD_EXE_FORMAT);
            }
        }
        /*
         * The entry point must be exported unless this is
         * an internal reference through the entry table.
         */
        if ((ptgtmte != pmte) && !(ptaddr->tflags & E32EXPORT))
        {
            ldrStop(4, NULL);
            return(ERROR_BAD_EXE_FORMAT);
        }
        return(NO_ERROR);
    }
    /*
     * target is an internal entry table fixup
     */
    if (tgt_flags == NRRENT)
    {
        if ((pote = ldrNumToOte(pmte, lobjnum + 1)) == 0)
        {
            ldrStop(5, NULL);
            return(ERROR_BAD_EXE_FORMAT);
        }
        lflags = (pefrt->efr_stype & NRALIAS) |
                    (pote->ote_flags & OBJCONFORM) |
                    (pote->ote_flags & OBJIOPL);
        return( ldrGetEntAddr( pefrt->efr_tobj,
                               pmte,
                               ptaddr,
                               lflags,
                               pmte,
                               pptda ) );
    }
    /*
     * Invalid fixup type return error
     */
    ldrStop(6, NULL);
    return(ERROR_BAD_EXE_FORMAT);
}




/***LP  ldrApply32Fixups - Fixup source address.
 *
 *      Fixes up the source address defined by the source type.
 *
 *      ENTRY   psrcaddr - source base address of page
 *              ptaddr - pointer to return target address (off,sel,flags)
 *              pfrt - relocation record
 *              pchain - pointer to chain address
 *              lsrcobj - address of real source object
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 *
 *   This procedure performs the following steps:
 *
 *      - convert target address to byte stream based on source type
 *      - if additive fixup adjust target address
 *      - setup source count and pointer to source offset(s)
 *      - check that source offset is valid
 *      - for each source offset apply target for source type
 *
 *  ldApply32Fixups(psrcaddr, ptaddr, pfrt);
 *      if (source type is 16-bit selector)
 *          move selector value to low word of offset field
 *      if (source type is 16:16 pointer)
 *          move selector value to high word of offset field
 *      if (additive fixup)
 *          add additive value to target address
 *      if (source list)
 *          get source count
 *          set pointer to beginning of source list
 *      else
 *          set source count to 1
 *          set pointer to source offset value
 *      while (source count is not zero)
 *          get source offset
 *          verify source offset is within page limits for source type
 *          compute end address of object from base and length
 *          verify entire fixup is within objects byte size
 *          if self relative fixup compute relative offset
 *          set fixup within page accounting for page boundaries
 */

uchar_t TypLenTbl[] = {
        1,      /* type 0, 8-bit byte fixup */
        0,      /* type 1, not defined */
        2,      /* type 2, 16-bit selector fixup */
        4,      /* type 3, 32-bit 16:16 pointer fixup */
        0,      /* type 4, not defined */
        2,      /* type 5, 16-bit offset fixup */
        6,      /* type 6, 48-bit 16:32 pointer fixup */
        4,      /* type 7, 32-bit offset fixup */
        4       /* type 8, 32-bit self relative fixup */
};


int ldrApply32Fixups( ulong_t                 srcaddr,
                      register struct taddr_s *ptaddr,
                      ldrefrt_t               *pefrt,
                      uchar_t                **pchain,
                      ulong_t                  lsrcobj )
{
    ushort_t                csrc;
    register short          srcoff;
    register uchar_t        *ptgt;
    uchar_t                 stype;
    uchar_t                 typelen;
    ulong_t                 ltgttemp;

    /*
     * ichain prefix refers to Internal Chaining.
     */
    ulong_t ichain_flag=0;    /* True if the perfrt->efr_flags has NRICHAIN bit set           */
    ulong_t ichain_pgoff;     /* offset within the page where next internal chain is located     */
    ulong_t ichain_objoff;    /* low 20 bits of the offset within the object                     */
    ulong_t ichain_toff;      /* when added to ichain_objoff, provides full offset within object */
    ulong_t *ichain_ptr;      /* ptr to the internal chain value within the page being fixed up  */

    /*
     * convert target address to byte stream
     */
    if ((pefrt->efr_stype & NRSTYP) == NRSSEG)
        ptaddr->toff = (ulong_t) ptaddr->tsel;

    else if ((pefrt->efr_stype & NRSTYP) == NRSPTR)
        ((struct FarPtr *) &ptaddr->toff)->Segmt = ptaddr->tsel;

    if (pefrt->efr_flags & NRADD)
        /*
         * add additive value to target address and advance pointer
         */
        ptaddr->toff += pefrt->efr_add;

    if (pefrt->efr_stype & NRCHAIN)
         csrc = pefrt->efr_cchain;
    else csrc = 1;


    if ( pefrt->efr_flags & NRICHAIN)
	ichain_flag = 1;
    else
	ichain_flag = 0;


    srcoff = (short) pefrt->efr_srcoff;

    stype = pefrt->efr_stype & (uchar_t) NRSTYP;

    /*
     * save target addr if 32-bit offset, so it can be restored
     * for applying chained fixups
     */

    ltgttemp = ptaddr->toff;

    if (stype > sizeof(TypLenTbl))
    {
        ldrStop(0, 0);
        return(ERROR_BAD_EXE_FORMAT);
    }

    while (csrc--)
    {
        /*
         * ptgt is a temp pointer used to walk thru the target address array
         */
        ptgt = (char *) ptaddr;

        typelen = TypLenTbl[stype];

        /*
         * NOTE: we do not have the object at this point
         * if ((srcaddr + srcoff + typelen) > (objectbase + objectsize))
         * return(ERROR_BAD_EXE_FORMAT);
         */

        if (stype == NRSOFF32)
        {            /* compute target addr */
            ptaddr->toff =
                    ltgttemp - (lsrcobj + srcoff + (ulong_t) typelen);
        }

        for ( ; typelen--; srcoff++, ptgt++)
        {
            if (srcoff >= 0)
            {
                if (srcoff >= PAGESIZE)
                    break;

		/*
		 * typelen == (sizeof(ulong_t) -1) will be true only once for the head of
		 * an ichain.  Thus, the if stmt below will only be entered one time for
		 * the head of an ichain.  However, the for loop we are currently within will
		 * be executed sizeof(ulong_t) times because srcaddr is updated one byte at a time.
		 */
		if (ichain_flag && typelen == (sizeof(ulong_t)-1) && srcoff < (PAGESIZE - sizeof(ulong_t)))
		{
		    int max_count;
 		    max_count = PAGESIZE/sizeof(ulong_t);

		    ichain_ptr=(ulong_t *)((ulong_t) srcaddr + (ulong_t) srcoff);

  		    ichain_pgoff     = (*ichain_ptr & 0xFFF00000) >> 20;
		    ichain_objoff    = (*ichain_ptr & 0x000FFFFF);
		    ichain_toff      = ptaddr->toff - ichain_objoff;

		    *ichain_ptr = ichain_toff + ichain_objoff;
#ifdef LDRSTRICT
		    if ( ichain_print ==2 )
			printf("pg=%3d srcoff=%03x (%03x %05x) toff=%08x :: ",
			       ichain_cphyspg,srcoff,ichain_pgoff,
			       ichain_objoff, ichain_toff);
#endif
		    while(max_count-- > 0 &&
      			  ichain_pgoff < PAGESIZE - (sizeof(ulong_t) +1))
		    {
			ichain_ptr = (ulong_t *)((ulong_t) srcaddr + ichain_pgoff );

			ichain_pgoff  = (*ichain_ptr & 0xFFF00000) >> 20;
	     		ichain_objoff = (*ichain_ptr & 0x000FFFFF);

			*ichain_ptr = ichain_toff + ichain_objoff;
#ifdef LDRSTRICT
			if ( ichain_print ==2 )
				printf("(%03x %05x) ", ichain_pgoff,ichain_objoff);
#endif
		    }
#ifdef LDRSTRICT
		    if ( ichain_print ) printf("\n");
#endif
		}

		/*
		 * srcaddr is updated one byte at a time, even for an ichain head
		 */
                *((char *) ((ulong_t) srcaddr + (ulong_t) srcoff)) = *ptgt;
            }
        }

        /*
         * see if another source offset exist in chain if, so get it
         */
        if (csrc > 0)
        {
            srcoff = *((ushort_t *) *pchain);
            (*pchain) += sizeof(ushort_t);
        }
    }
    return(NO_ERROR);
}



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

/***LP  ldrIsInternalFixup - determine length and type of fixup record
 *
 *      ENTRY   pfixrec - pointer to beginning of fixup record (input)
 *              pfixlen - pointer to length of fixup record (output)
 *
 *      EXIT    int     - TRUE if internal fixup record
 *
 *      This procedure determines the length of the given fixup record
 *      and returns TRUE if the fixup record is of type NRRINT,  FALSE
 *      otherwise.
 */

int     ldrIsInternalFixup(rec, len)
uchar_t *rec;
ulong_t *len;
{
        register uchar_t fSrc = *rec;
        register uchar_t fTgt = *(rec+1);
        ulong_t          Size = 2 * sizeof(uchar_t);
        uchar_t          Type = fTgt & (uchar_t)NRRTYP;

#ifdef LDRSTRICT
        if (((fSrc & NRSTYP) == 0x01) ||
            ((fSrc & NRSTYP) == 0x04) ||
            ((fSrc & NRSTYP) >= 0x09) ||
            (fSrc & ~(NRSTYP | NRALIAS | NRCHAIN)))
            panic("ldrFixupRecordLength: source flags invalid");
        if (fTgt & ~(NRRTYP | NRADD | NR32BITOFF | NR32BITADD | NR16OBJMOD | NR8BITORD))
            panic("ldrFixupRecordLength: target flags invalid");
#endif

        // Fixup is list of 16-bit source offsets plus 8-bit list count
        if (fSrc & NRCHAIN)
            Size += (*(rec+2) * sizeof(ushort_t)) + sizeof(uchar_t);
        // Fixup is single 16-bit source offset
        else
            Size += sizeof(ushort_t);

        // Object number/module ordinal is 16-bit value
        if (fTgt & NR16OBJMOD)
            Size += sizeof(ushort_t);
        // Object number/module ordinal is 8-bit value
        else
            Size += sizeof(uchar_t);

        // Determine size of target offset (NRRINT), imported ordinal number
        // (NRRORD), or offset into import procedure name table (NRRNAM)
        if (((Type == NRRINT) && (!((fSrc & NRSTYP) == NRSSEG))) ||
            ((Type == NRRORD) && (!(fTgt & NR8BITORD))) ||
            (Type == NRRNAM))
        {
            // Offset/ordinal value is 32-bit value
            if (fTgt & NR32BITOFF)
                Size += sizeof(ulong_t);
            // Offset/ordinal value is 16-bit value
            else
                Size += sizeof(ushort_t);
        }

        // Check special case of 8-bit imported ordinal number
        if ((Type == NRRORD) && (fTgt & NR8BITORD))
            Size += sizeof(uchar_t);

        // Determine size of additive fixup value
        if (fTgt & NRADD)
        {
            // Additive fixup value is 32-bit value
            if (fTgt & NR32BITADD)
                Size += sizeof(ulong_t);
            // Additive fixup value is 16-bit value
            else
                Size += sizeof(ushort_t);
        }

        *len = Size;
        return (Type == NRRINT);
}

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


/***LP  ldrNumToOte - validate object number and return ote address
 *
 *      Given a object number, check if object exists in current mte,
 *      and return a pointer to the object table entry.
 *
 *      ENTRY   pmte - pointer to module table entry
 *              objnum - object number to check for
 *
 *      EXIT    pointer to object table entry for object
 *              or 0 for object not present
 */

ldrote_t *ldrNumToOte( register ldrmte_t *pmte,   // pointer to a module table entry
                       ulong_t           objnum ) // object number to check for
{
    register ldrsmte_t      *psmte;         /* pointer to swappable mte */

    psmte = pmte->mte_swapmte;

    if (objnum-- <= psmte->smte_objcnt) {
        if (ldrIsNE(pmte))                  /* is this a 16-bit module */
            return((ldrote_t *) &(((ldrste_t *)
                   psmte->smte_objtab)[objnum]));
        else
            return(&(((ldrote_t *)
                   psmte->smte_objtab)[objnum]));
    }
    return(0);
}



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

/***LP  ldrGetTgtMte - get target mte linear address
 *
 *      Get MTE of module referenced by ordinal number passed.  Use the
 *      ordinal number to index into the import module pointers table.
 *
 *      ENTRY   modord                  - ordinal number of module referenced
 *              pmte                    - pointer to mte
 *              pptgtmte                - pointer to target mte
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 *              ptgtmte                 - mte linear address
 *                                        if mte == 0 reference to DOSCALLS
 *
 *   This procedure performs the following steps:
 *
 *      - Check that module ordinal number is within module pointer table
 *      - Index into module pointer table and return linear address to mte
 */

int ldrGetTgtMte( ushort_t    modord,
                  ldrmte_t   *pmte,
                  ldrmte_t  **pptgtmte )
{
    modord--;                               /* normalize ordinal */

    if ((ulong_t) modord < pmte->mte_impmodcnt)
    {
        *pptgtmte = (ldrmte_t *) ( *(
                       ( (ulong_t *) ( ((char *)pmte) + sizeof(ldrmte_t) ) )
                                 + modord ) );

#ifdef LDRSTRICT
        if ( !(*pptgtmte) )
        {
            panic("tgt pmte 0");
        }
#endif

        return(NO_ERROR);
    }

    ldrStop( 0, 0 );
    return( ERROR_BAD_EXE_FORMAT );
}


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

/***LP  ldrGetOrdNum - get the ordinal number for procedure name
 *
 *      Given a procedure name and a module table, checks to see if
 *      the procedure is in the resident or non-resident table.  If the
 *      procedure is found in either of the tables, ldrGetOrdNum
 *      returns the corresponding ordinal number.  A 0 ordinal is
 *      returned if the procedure is not found in either of the tables.
 *
 *      ENTRY   pmte - pointer to module table entry
 *              pprocnam - pointer to procedure name
 *              pentord - address to return ordinal number
 *
 *      EXIT    if found
 *                  NO_ERROR
 *              else
 *                  ERROR_NOT_ENOUGH_MEMORY
 *                  ERROR_PROC_NOT_FOUND
 *
 *   This procedure performs the following steps:
 *
 *      - search resident name table looking for the procedure name
 *      - if (found or error) return
 *      - check if module DOSCALLS if true then return error because DOSCALLS
 *        has no non-resident name table
 *      - walk linked list of non-resident name tables in memory looking for
 *        this module's non-resident name table
 *      - if table is not in link list allocate memory, read non-resident
 *        name table and link into list
 *      - search non-resident name table object looking for name
 */

int ldrGetOrdNum( ldrmte_t *pmte,
                  uchar_t  *pprocnam,
                  ushort_t *pentord )
{
    struct  nonres_s         *pnrtemp;
    register struct nonres_s *pnr;
    int                       ctcyield;
    register ldrsmte_t       *psmte;
    int                       rc;

    psmte = pmte->mte_swapmte;

    pTCBCur->TCB_ProcNameBuf = pprocnam;
    /*
     * First, search the resident name table.
     */
    if ((rc = ldrGetOrdNumSub( (uchar_t *) psmte->smte_restab,
                               pprocnam,
                               pentord))
            != ERROR_PROC_NOT_FOUND)
        return(rc);

    /*
     * If the module is DOSCALLS module, no need to go on
     * because, there is no non-resident name table for DOSCALLS.
     */
    if (!(pmte->mte_flags1 & DOSMOD))
    {
        /*
         *  Request control of non-resident name tables
         */
        KSEMRequestMutex(hNonResNamTbl, -1);

        /*
         * The procedure name was not found in the resident name table,
         * need to search the non-resident name table.
         *
         * DosGroup:pNonResNamTbl holds the ptr to the first non-resident
         * name table in a link list of non-resident name tables.
         */
        pnr = pNonResNamTbl;
        ctcyield = TCYIELD_CNT;

        /*
         * Walk down the list of non-resident name table objects,
         * looking for the object that has the matching mte.
         */
        while (pnr)
        {                                   /* loop till end of list */
            if (ctcyield-- == 0)
            {
                TKTCYield();                /* go yield if needed */
                ctcyield = TCYIELD_CNT;
            }

            /* is this the desired non-res table? */
            if (pmte->mte_handle == pnr->nr_mte)
                break;

            pnr = pnr->nr_link;             /* check next table */
        }

        if (pnr == 0)
        {                      /* pnr is 0 if no match was found */

            /*
             * The non-resident name-table seg we are looking for was not
             * found in the linked list. Allocate memory and read in the
             * non-resident name table.
             */

            /*
             * For 32-bit modules a empty non-resident name table is
             * indicated by a zero table length.  This is not true for
             * 16-bit modules, a zero byte is placed at the end of the
             * table.  We have to read the 16-bit table.
             */
            if (ldrIsLX(pmte) && psmte->smte_cbnrestab == 0)
            {
                KSEMReleaseMutex(hNonResNamTbl);
                ldrStop(0, 0);
                return(ERROR_PROC_NOT_FOUND);
            }

            if ((rc = VMAllocKHB(VM_HKH_PUB_SWAPRW,
                            psmte->smte_cbnrestab + sizeof(struct nonres_s),
                            (VMHOB) LDRNONRESOWNER, NA, SSToDS(&pnrtemp)))
                            != NO_ERROR)
            {
                KSEMReleaseMutex(hNonResNamTbl);
                return(ldrNoMemGetOrdNum(pmte, pprocnam, pentord));
            }
            pnr = pnrtemp;
            pnr->nr_mte = pmte->mte_handle;
                                    /* set mte handle in nonres object */

            /*
             * read the non-resident name table from the file into non-
             * resident name table object.
             */
            if (rc = ldrRead( pmte->mte_sfn,
                              psmte->smte_nrestab,
                              &pnr->nr_names,
                              NULL,
                              psmte->smte_cbnrestab,
                              pmte ) )
            {
                VMFreeKHB(VM_HKH_PUB_SWAPRW, pnr);
                KSEMReleaseMutex(hNonResNamTbl);
                return(rc);
            }

            /*
             * Link the new non-resident name table into the list.
             */
            pnr->nr_link = pNonResNamTbl;
            pNonResNamTbl = pnr;
        }

        /*
         * Now check if the procname is in the non-resident name table.
         */
        if ((rc = ldrGetOrdNumSub( (uchar_t *) &pnr->nr_names,
                                   pprocnam,
                                   pentord))
                != ERROR_PROC_NOT_FOUND)
        {
            KSEMReleaseMutex(hNonResNamTbl);
            return(rc);
        }

        KSEMReleaseMutex(hNonResNamTbl);
    }

    ldrStop(1, 0);
    return(ERROR_PROC_NOT_FOUND);
}

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


/***LP  ldrNoMemGetOrdNum - get the ordinal number for procedure name when
 *                          VMAllocKHB returns out of memory.
 *
 *      For a given procedure name and a module table, this procedure will
 *      read the non-resident name table into a 258 byte buffer and compare the
 *      passed procedure name with the names in the buffer.  If the name
 *      is found the corresponding ordinal number will be returned.  A 0
 *      ordinal is returned if the procedure is not found.
 *
 *      ENTRY   pmte - pointer to module table entry
 *              pprocnam - pointer to procedure name
 *              pentord - address to return ordinal number
 *
 *      EXIT    if found
 *                  NO_ERROR
 *              else
 *                  ERROR_PROC_NOT_FOUND
 */

int ldrNoMemGetOrdNum( register ldrmte_t *pmte,
                       uchar_t           *pprocnam,
                       ushort_t          *pentord )
{
    uchar_t         *pnrbuf;        /* pointer to non-resident tb */
    int             len;            /* len of procedure name */
    ulong_t         cbnrestab;      /* byte count of non-resident */
    ulong_t         lindex = 0;     /* index into non-resident tb */
    ulong_t         cbread;         /* number of bytes read */
    register ldrsmte_t *psmte;      /* pointer to swappable mte */
    int             rc;

    psmte = pmte->mte_swapmte;

    cbnrestab = psmte->smte_cbnrestab;

    do
    {
        pnrbuf = nrbuf;                     /* point to beginning of buf */
        /*
         * Read in the largest possible procedure name + the length of
         * the name + the ordinal number for that name.
         */
        if ( rc = ldrRead( pmte->mte_sfn,
                           psmte->smte_nrestab + lindex,
                           pnrbuf,
                           NULL,
                           cbread = (  (cbnrestab > SIZEOFNRBUF)
                                       ? SIZEOFNRBUF
                                       : cbnrestab ),
                           pmte ) )
        {
            ldrStop(0, 0);
            return(rc);
        }

        /*
         * get length of first name in buffer.
         */
        len = (int) *pnrbuf;

        /*
         * check for valid name in buffer, the sum of the string len plus
         * the string size field plus the ordinal must be less or equal to
         * the number of bytes read.
         */
        if ((ulong_t) len + sizeof(uchar_t) + sizeof(ushort_t) > cbread)
        {
            ldrStop(1, 0);
            return(ERROR_PROC_NOT_FOUND);
        }

        /*
         * compare each name in buffer to procedure name we are looking
         * for, till we hit the zero terminator for the table or we
         * come to the end of the buffer.  The special case is when
         * a partial name is present in the buffer, we will re-read
         * the non-resident name table starting with this name and
         * continue the compare.
         */
        while (len != 0 && (ulong_t) ++len + sizeof(ushort_t) <= cbread)
        {
            lindex += (ulong_t) len + sizeof(ushort_t);
            cbread -= (ulong_t) len + sizeof(ushort_t);
            cbnrestab -= (ulong_t) len + sizeof(ushort_t);

            if (memcmp(pnrbuf, pprocnam, len) == 0)
            {
                /*
                 * The procedure names match return the ordinal
                 */
                *pentord = *(ushort_t *) (pnrbuf + len);
                return(NO_ERROR);
            }

            /*
             * The names don't match, point to the next procedure
             * name in the buffer if it exists.
             */
            pnrbuf += len + sizeof(ushort_t);
            if (cbread >= 1)
                len = (int) *pnrbuf;
            else
                len = SIZEOFNRBUF;
        }

    /*
     * We are finished when the only thing left in the buffer is
     * the zero terminator.
     */
    } while (cbnrestab > 1);

    ldrStop(2, 0);
    return(ERROR_PROC_NOT_FOUND);

}


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

/***LP  ldrGetOrdNumSub - get the ordinal number for procedure name
 *
 *      Given a procedure name and a resident or non-resident name table
 *      containing the procedures exported by a module, checks to see
 *      if the procedure is in the table.  If the procedure is found
 *      in the table, ldrGetOrdNumSub gets the corresponding ordinal
 *      number.  A 0 ordinal value is returned if the procedure is not
 *      found in the table.  The ordinal number for the module name string
 *      is set to zero, therefore matching strings with a 0 ordinal number
 *      are skipped.
 *
 *      ENTRY   pnt - pointer to resident or non-resident name table
 *              pprocnam - pointer to procedure name
 *              pentord - place to return ordinal
 *
 *      EXIT    if found
 *                  NO_ERROR
 *              else
 *                  ERROR_PROC_NOT_FOUND
 *                  ERROR_BAD_EXE_FORMAT
 *
 *   This procedure performs the following steps:
 *
 *      - compare procedure name to each entry in table
 *      - if ordinal number is 0 skip to next string
 *      - return ordinal number if found
 */

int ldrGetOrdNumSub( register uchar_t *pnt,
                     register uchar_t *pprocnam,
                     ushort_t         *pentord )
{
    register int len;
    ljmp_t lj;

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

    /* remove typeinfo from length */
    while ( (len = (int) *pnt) != 0 )
    {
                                  /* include string size field */
        if (memcmp(pnt, pprocnam, ++len) == 0)
            if ((*(ushort_t *) (pnt + len)) != 0)
            {   /* ord # is 0 for module name */
                /* return ordinal number */
                *pentord = *(ushort_t *)(pnt + len);
                TKClrFault();
                return(NO_ERROR);
            }
        pnt += len + sizeof(ushort_t);      /* skip to next string */
    }

    TKClrFault();
    // ldrStop(1, 0);
    return(ERROR_PROC_NOT_FOUND);   /* end of table, return not found */
}

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

/*
 * FlatSelTbl - Maps bits from the forwarder flags field into a flat
 * selector value.  The bits form an index from 0 to 7.
 */
SEL         *FlatSelTbl[] = {
            0,       /* type 0 */
            &R3DS,   /* type 1 */
            &R3CS,   /* type 2 */
            &R2DS,   /* type 3 */
            &R2CS,   /* type 4 */
            &R0DS,   /* type 5 */
            &R0CS,   /* type 6 */
            0};      /* type 7 */



/***LP  ldrGetEntAddr - get entry point address
 *
 *      For a given ordinal number, this routine locates the
 *      appropriate entry contained in the module's entry table.
 *      It returns a 48-bit pointer for the specified entry point.
 *
 *      ENTRY   entord -  ordinal number
 *              pmte - pointer to target mte
 *              ptaddr - pointer to return target address (off,sel,flags)
 *              lflags - source object iopl and conforming flags
 *              psrcmte - pointer to source mte
 *
 *      EXIT    entry point address
 *              global variable obj_ptr pointing to target segment
 *              global variable entry_ptr pointing to entry table entry
 *
 *   This procedure performs the following steps:
 *
 *      - Check for zero ordinal number
 *      - Normalize ordinal number
 *      - Checks and handles installable file system mte entry points
 *      - Checks and handles DOSCALLS mte entry points
 *      - Locates the correct bundle for the given ordinal
 *      - Creates the desired entry point address based on source and target
 */

int ldrGetEntAddr( ushort_t                 entord,
                   register ldrmte_t       *pmte,     // target mte
                   register struct taddr_s *ptaddr,
                   ulong_t                  lflags,
                   ldrmte_t                *psrcmte,  // source mte, may be 0
                   ptda_t                  *pptda )
{
    register ldret_t *pbndl;
    ldr32et_t        *pent;
    int               fcallgate;
    ulong_t           objnum;
    ldrsmte_t        *psmte;
    int               retcode;

    psmte = pmte->mte_swapmte;

    (ulong_t)pTCBCur->TCB_ProcNameBuf = (ulong_t)entord;

    if (entord == 0)
    {
        ldrStop(0, pmte);
        return(ERROR_INVALID_ORDINAL);
    }

    // Normalize object number
    --entord;

    // Assume FSHMOD, DOSMOD, MVDMMOD to save a bit of code.
    ptaddr->fflags = FWD_SPECIAL;

    // Check for FSH module
    if (pmte->mte_flags1 & FSHMOD)
    {
        register struct FarPtr *pfp;

        // Check if pointer in range
        pfp = (struct FarPtr *) psmte->smte_enttab + entord;
        if (pfp >= &EndFSHMTEENTRY)
        {
            ldrStop(1, pmte);
            return(ERROR_INVALID_ORDINAL);
        }

        ptaddr->toff = pfp->Offst;
        ptaddr->tsel = pfp->Segmt;
        ptaddr->tflags = E32EXPORT;
        return(NO_ERROR);
    }

    // Check for DOSCALLS module
    if (pmte->mte_flags1 & DOSMOD)
    {
        register struct dce_s *pdos;

        // If the requested NORMALIZED ordinal is ORD_T32EXITLIST - 1
        // or above and the source module is not DOSCALL1, or if the
        // ordinal is out of range, return ERROR_INVALID_ORDINAL.
        if ((entord >= ORD_T32EXITLIST - 1
           && (psrcmte == NULL || !(psrcmte->mte_flags1 & DOSLIB)))
           || entord*sizeof(struct dce_s) >= *((ushort_t *) psmte->smte_enttab))
        {
            ldrStop(2, pmte);
            return(ERROR_INVALID_ORDINAL);
        }

        pdos = (struct dce_s *) (psmte->smte_enttab + sizeof(ushort_t)) + entord;

        if (pdos->dce_flags == IS_DOSCALL1)
        {
            // ldrStop(3, pmte);
            return(ERROR_INVALID_ORDINAL);
        }

        if (pdos->dce_flags == IS_ABSOLUTE)
        {
            ptaddr->toff = pdos->dce_gate;
            ptaddr->tsel = 0;
        }

        else if (pdos->dce_flags == IS_GATE)
        {
            ptaddr->toff = 0;
            ptaddr->tsel = pdos->dce_gate;
        }

        else if (pdos->dce_flags == IS_INDABSOLUTE)
        {
            if (psrcmte && (psrcmte->mte_flags1 & MTE_RING0))
            {
                ptaddr->toff = ldrMteAux[pdos->dce_gate].adce_ring0;
            }
            else
            {
                ptaddr->toff = ldrMteAux[pdos->dce_gate].adce_ring3;
            }
            ptaddr->tsel = 0;
        }

        else
        {
            ldrAssert(pdos->dce_flags == IS_INDGATE);

            ptaddr->toff = 0;
            if (psrcmte && (psrcmte->mte_flags1 & MTE_RING0))
            {
                ptaddr->tsel = ldrMteAux[pdos->dce_gate].adce_ring0;
            }
            else
            {
                ptaddr->tsel = ldrMteAux[pdos->dce_gate].adce_ring3;
            }
        }
        ptaddr->tflags = E32EXPORT;
        return(NO_ERROR);
    }

    // Check for MVDM module
    if (pmte->mte_flags1 & MVDMMOD)
    {
        if ((entord * sizeof(ulong_t)) >= *((ushort_t *)psmte->smte_enttab))
        {
            ldrStop(4, pmte);
            return(ERROR_INVALID_ORDINAL);
        }

        // Get offset of procedure ordinal number (entord) from entry
        // table.  NOTE: The MVDM MTE has a special entry table made
        // up of: first word = size of table followed by 32-bit offset
        // to procedure entry points index by the ordinal number.
        ptaddr->toff = ((ulong_t *)(psmte->smte_enttab + sizeof(ushort_t)))[entord];
        ptaddr->tflags = E32EXPORT;
        return(NO_ERROR);
    }

    // Not a special case ( FSH / DOSMOD / MVDMMOD ), so do normal module work.

    // Clear forwarder flags
    ptaddr->fflags = 0;

    // Otherwise find entry point, loop to determine which bundle the
    // ordinal number belongs too.
    pbndl = (ldret_t *) psmte->smte_enttab;

    // find bundle ordinal is in
    while (TRUE)
    {
        // check for end of table
        if (pbndl->et_count == 0)
        {
            ldrStop(5, pmte);
            return(ERROR_INVALID_ORDINAL);
        }

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

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

    // check for empty bundle
    if (pbndl->et_type == EMPTY)
    {
        ldrStop(6, pmte);
        return(ERROR_INVALID_ORDINAL);
    }

    // assume not a callgate entry
    fcallgate = FALSE;

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

    // check for 16-bit module
    if (ldrIsNE(pmte))
    {
        register ldrste_t *pste;

        // indicate tiled object
        ptaddr->fflags |= FWD_ALIAS16;

        // check for movable entry
        if (pbndl->et_type == B16MOVABLE)
        {
            // get target object
            objnum = ((ldrcte_t *) pent)->cte_obj;

            // if absolute entry
            if (objnum == B16ABSOLUTE)
            {
                ptaddr->toff = (ulong_t) ((ldrcte_t *) pent)->cte_off;
                ptaddr->tsel = 0;
                ptaddr->tflags = pent->et_flags;
                return(NO_ERROR);
            }

            if (objnum > psmte->smte_objcnt)
            {
                ldrStop(7, pmte);
                return(ERROR_INVALID_SEGMENT_NUMBER);
            }

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

            // if target is ring 2 non-conforming and src is ring 3 or ring 2
            // conforming, then a callgate selector must be returned
            if ((pste->ste_flags & STE_SEGDPL) == STE_RING_2 &&
                    !(pste->ste_flags & STE_CONFORM))
            {
                // Set flags for forwarder chain.  Consider this a
                // callgate if target object is ring 2 nonconforming.
                ptaddr->fflags |= FWD_GATE16;
                ptaddr->cgsel = ((ldrcte_t *) pent)->cte_sel;

                // Finish privilege check, maybe set callgate selector.
                if ((lflags ^ OBJIOPL) & (OBJIOPL | OBJCONFORM))
                {
                    ptaddr->toff = 0;
                    ptaddr->tsel = ((ldrcte_t *) pent)->cte_sel;
                    fcallgate = TRUE;
                }
                else
                    // return offset and selector
                    ptaddr->toff = (ulong_t) ((ldrcte_t *) pent)->cte_off;
            }
            else
                // return offset and selector
                ptaddr->toff = (ulong_t) ((ldrcte_t *) pent)->cte_off;
        }

        // check for absolute entry
        else if (pbndl->et_type == B16ABSOLUTE)
        {
            ptaddr->toff = (ulong_t) ((ldrent_t *) pent)->ent_off;
            ptaddr->tsel = 0;
            ptaddr->tflags = pent->et_flags;
            return(NO_ERROR);
        }

        // entry is fixed
        else
        {
            if ((ulong_t) pbndl->et_type > psmte->smte_objcnt)
            {
                ldrStop(8, pmte);
                return(ERROR_INVALID_SEGMENT_NUMBER);
            }

            // get target object
            pste = (ldrste_t *) psmte->smte_objtab + pbndl->et_type - 1;
            ptaddr->toff = (ulong_t) ((ldrent_t *) pent)->ent_off;
            if ((pste->ste_flags & STE_SEGDPL) == STE_RING_2)
                ptaddr->fflags |= FWD_IOPL;
        }

        // do general setup for non-callgate
        if (!fcallgate)
        {
            if (lflags & NRALIAS)
                // Source 16-bit
                ptaddr->tsel = pste->ste_selector;

            else
            {
                // Source 32-bit
                ptaddr->toff += SELSelToLa( pste->ste_selector, pptda );
                             // + (ulong_t) ((ldrent_t *) pent)->ent_off; 53639
                ptaddr->tsel = ldrGetSelector( pmte,
                                               pste->ste_flags );
            }
        }
    }

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

        // Forwarder entry points do not have a meaningful et_objnum
        // field.  Otherwise, validate it and set variables.
        if ((pbndl->et_type & ~TYPEINFO) != ENTRYFWD)
        {
            objnum = pbndl->et_obj;
            if (objnum > psmte->smte_objcnt)
            {
                ldrStop(9, pmte);
                return(ERROR_INVALID_SEGMENT_NUMBER);
            }

            // point to ote if not absolute
            pote = (ldrote_t *) psmte->smte_objtab + objnum - 1;

            // Save flags for forwarder processing.
            if (pote->ote_flags & OBJALIAS16)
                ptaddr->fflags |= FWD_ALIAS16;
            if (pote->ote_flags & OBJIOPL)
                ptaddr->fflags |= FWD_IOPL;
        }
        else
            pote = NULL;

        switch (pbndl->et_type & ~TYPEINFO)
        {

            case ENTRY16:
                ptaddr->toff = (ulong_t) pent->et_16off;
                break;

            case GATE16:
                if(objnum == 0)
                {
                    ldrStop(10, pmte);
                    return(ERROR_INVALID_CALLGATE);
                }

                // if target is ring 2 non-conforming and src is ring 3 or
                // ring 2 conforming then a callgate selector must be
                // returned.
                if ((pote->ote_flags & OBJIOPL) &&
                    !(pote->ote_flags & OBJCONFORM))
                {

                    // Set flags for forwarder chain.  Consider this a callgate
                    // as long as target object is ring 2 nonconforming.
                    ptaddr->fflags |= FWD_GATE16;
                    ptaddr->cgsel = pent->et_cgsel;

                    // Finish privilege check & maybe set callgate sel.
                    if ((lflags ^ OBJIOPL) & (OBJIOPL | OBJCONFORM))
                    {
                        ptaddr->toff = 0;
                        ptaddr->tsel = pent->et_cgsel;
                        fcallgate = TRUE;
                    }
                    else
                        // return offset and selector
                        ptaddr->toff = (ulong_t) pent->et_cgoff;
                }
                else
                    // return offset and selector
                    ptaddr->toff = (ulong_t) pent->et_cgoff;
                break;

            case ENTRY32:
                ptaddr->toff = pent->et_32off;
                break;

            case ENTRYFWD:
                if (!(pent->et_flags & FWD_PROCESSED))
                {
                  // We have an unprocessed forwarder.  If not processing
                  // then return error code.  Else, process the chain.
                  if (pTCBCur->TCBMiscFlags & TMF_NoFwd)
                  {
                      // Return fwd address to ldrGetFwdEnt.
                      ptaddr->toff = (ulong_t) pent;    // Overload
                      ldrStop(11, pmte);
                      return(ERROR_LDR_FWD);
                  }
                  if ( retcode = ldrGetFwdEnt( pent, pmte, pptda ) )
                      return(retcode);
                }

                // Now we have a processed forwarder.  Set target address
                // values.
                ptaddr->tflags = E32EXPORT;
                ptaddr->fflags = pent->et_flags;

                // If target is FSHMOD, etc.then just use sel and offset.
                if (ptaddr->fflags & FWD_SPECIAL)
                {
                    ptaddr->toff = pent->et_value;
                    ptaddr->tsel = pent->et_modord;
                    return(NO_ERROR);
                }

                // If 286 call gate:  ldrGetFwdEnt guarantees that the
                // target object has IOPL.  So only check for source =
                // ring 3 or ring 2 non-conforming; if so then use
                // callgate selector and 0 offset.  Else treat as ENTRY16.
                if ((pent->et_flags & FWD_GATE16) &&
                      ((lflags ^ OBJIOPL) & (OBJIOPL | OBJCONFORM)))
                {
                    ptaddr->toff = 0;
                    ptaddr->tsel = pent->et_modord;
                    return(NO_ERROR);
                }

                // If 16:16 alias requested, then convert the address.
                if (lflags & NRALIAS)
                {
                    if (!(pent->et_flags & FWD_ALIAS16))
                    {
                        ldrStop(12, pmte);
                        return(ERROR_INVALID_SEGMENT_NUMBER);
                    }
                    ptaddr->tsel = SELLaToSel( pent->et_value,
                                               pptda )
                                   | (ushort_t) ((ptaddr->fflags & FWD_IOPL)
                                                    ? (SEL_LDTBIT | SEL_RPL2)
                                                    : (SEL_LDTBIT | SEL_RPL3) );
                    ptaddr->toff = (ushort_t) pent->et_value;
                }

                // Otherwise set up the linear address.
                else
                {
                    ptaddr->toff = pent->et_value;
                    ptaddr->tsel =
                               *(FlatSelTbl[(pent->et_flags >> FWD_SHIFT) & 7]);
                }
                return(NO_ERROR);

            default:
                ldrStop(13, pmte);
                return(ERROR_INVALID_PARAMETER);

        }

        // if absolute fixup
        if (objnum == 0)
        {
            ptaddr->tsel = 0;
            ptaddr->tflags = E32EXPORT;
            return(NO_ERROR);
        }

        // do general setup for non-callgate
        if (!(fcallgate))
        {
            if (lflags & NRALIAS)
                if (!(pote->ote_flags & OBJALIAS16))
                    lflags &= ~NRALIAS;              // ???? pd
                                                     // masm 6 sets obj flags
                                                     // differently from masm 5

            // is a 16:16 alias requested ?
            if (lflags & NRALIAS)
            {
                if (!(pote->ote_flags & OBJALIAS16))  // redundant ? masm 6
                {
                    ldrStop(14, pmte);
                    return(ERROR_INVALID_SEGMENT_NUMBER);
                }

                if (!(pmte->mte_flags1 & (FSDMOD | DEVDRVMOD)))
                {
                    ptaddr->tsel =  SELLaToSel( pote->ote_base,
                                                pptda )
                                    | (ushort_t) ((pote->ote_flags & OBJIOPL)
                                                    ? (SEL_LDTBIT | SEL_RPL2)
                                                    : (SEL_LDTBIT | SEL_RPL3) );
                }
                else
                {
                    if (ptaddr->toff > _64K)
                    {
                        ldrStop(15, 0);
                        return(ERROR_BAD_EXE_FORMAT);
                    }
                    ptaddr->tsel = pote->ote_selector;
                }
            }

            // setup linear address
            else
            {
                ptaddr->toff += pote->ote_base;
                ptaddr->tsel = ldrGetSelector(pmte, pote->ote_flags);
            }
        }
    }

    ptaddr->tflags = pent->et_flags;
    return(NO_ERROR);
}


#if 0

/***LP  ldrSkipEnts - skip the given number of entries in the entry table
 *
 *      For a given number, return the number of bytes to skip to the
 *      next bundle in the entry table.
 *
 *      ENTRY   fNE  - set to 1 if NE, reset to 0 if LX
 *              type - the type of bundle to skip
 *              count - the number of entries in this bundle
 *
 *      EXIT    count of byte to skip to get to desired entry
 *
 *   This procedure performs the following steps:
 *
 *      - determines the type of module
 *      - returns the number of bytes to skip based on entry type
 */

ulong_t ldrSkipEnts( uchar_t  fNE,
                     uchar_t  type,
                     uchar_t  count )
{
   if (fNE)                      /* 16-bit module */
   {
       switch (type)
       {

         case 0:                           /* unused bundle */
             return(sizeof(ldrempty_t));

         case 0xff:                                /* movable object */
             return(sizeof(ldrempty_t) + sizeof(ldrcte_t) * count);

         default:                          /* fixed object */
              return(sizeof(ldrempty_t) + sizeof(ldrent_t) * count);
       }
   }
   else                                    /* must be a 32-bit module */
   {
       switch (type & ~TYPEINFO)
       {

         case EMPTY:                               /* unused bundle */
             return(sizeof(ldrempty_t));

         case ENTRY16:                     /* 16-bit offset entry */
             return(sizeof(ldret_t) + sizeof(ldret16_t) * count);

         case GATE16:                      /* 286 call gate */
             return(sizeof(ldret_t) + sizeof(ldret16iopl_t) * count);

         case ENTRY32:                     /* 32-bit offset entry */
             return(sizeof(ldret_t) + sizeof(ldret32_t) * count);

         case ENTRYFWD:                       /* forwarder entry */
             return(sizeof(ldret_t) + sizeof(ldrfwd_t) * count);
        }
    }
}

#endif


/***LP  ldrGetSelector - return FLAT selector for target
 *
 *      For a given target mte and object table pointer (pote), check
 *      the object table flags and return the appropriate selector.
 *      The following selectors can be returned:
 *
 *      R3DS    - target is ring 3 data
 *      R3CS    - target is ring 3 code
 *      R2DS    - target is ring 2 data
 *      R2CS    - target is ring 2 code
 *      R0DS    - target is ring 0 data
 *      R0CS    - target is ring 0 code
 *
 *      ENTRY   pmte    pointer to the target module table entry
 *              lflags  object or segment flags
 *
 *      EXIT    FLAT selector for target
 *
 *   This procedure performs the following steps:
 *
 *      - use the pmte and pote flags to determine the needed seletor
 */

ushort_t ldrGetSelector( register ldrmte_t *pmte,
                         register ulong_t   lflags )
{

    if (ldrIsLX(pmte))                      /* 32-bit */
    {
        switch (((pmte->mte_flags1 & VDDMOD) >> LGS_VDDMODSHF) |
                 ((pmte->mte_flags1 & FSDMOD) >> LGS_FSDMODSHF) |
                 ((pmte->mte_flags1 & DEVDRVMOD) >> LGS_DEVDRVSHF) |
                 ((lflags & OBJEXEC) >> LGS_OBJEXECSHF) |
                 ((lflags & OBJIOPL) >> LGS_OBJIOPLSHF))
        {

            case 0:
//              if (pmte->mte_flags1 & MTEMODPROT)
//                  return (R3PDS);
                return(R3DS);

            case LGS_OBJEXEC:
                return((lflags & OBJCONFORM) ? R2CS : R3CS);

            case LGS_OBJIOPL:
                return(R2DS);

            case LGS_OBJIOPL | LGS_OBJEXEC:
                return(R2CS);

            case LGS_VDDMOD:
            case LGS_VDDMOD | LGS_OBJIOPL:
                return(R0DS);

            case LGS_VDDMOD | LGS_OBJEXEC | LGS_OBJIOPL:
            case LGS_VDDMOD | LGS_OBJEXEC:
                return(R0CS);

            case LGS_FSDMOD:
            case LGS_FSDMOD | LGS_OBJIOPL:
                return(R0DS);

            case LGS_FSDMOD | LGS_OBJEXEC | LGS_OBJIOPL:
            case LGS_FSDMOD | LGS_OBJEXEC:
                return(R0CSC);

        }
    }

    else                            /* 16-bit */
    {
        if ((ushort_t) lflags & STE_DATA)
        {
            if (pmte->mte_flags1 & DEVDRVMOD)
                return(R0DS);
            else if (((ushort_t) lflags & STE_SEGDPL) == STE_RING_2)
                return(R2DS);
            else
                return(R3DS);
        }
        else
        {
            if (pmte->mte_flags1 & DEVDRVMOD)
                return(R0DS);
            else if (((ushort_t) lflags & STE_SEGDPL) == STE_RING_2)
                return(R2CS);
            else
                return(R3CS);
        }
    }
}

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

/***LP  ldrGetGate - get a callgate from a 16-bit or 32-bit module
 *
 *      ENTRY   pet                     - pointer to entry table bundle
 *              pentry                  - pointer to entry table entry
 *              pmte                    - pointer to module table entry
 *              psel                    - place to return selector in
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 *
 */

ulong_t ldrGetGate( ldret_t  *pet,    // pointer to entry table bundle
                    ulong_t  *pentry, // pointer to entry table entry
                    ldrmte_t *pmte,   // pointer to module table entry
                    ushort_t *psel )  // pointer to return selector in
{
    ldrcte_t      *pcte;          /* pointer to a 16-bit iopl entry */
    ldret16iopl_t *p16iopl;       /* pointer to a 32-bit iopl entry */

    if (ldrIsNE(pmte))
    {
        pcte = (ldrcte_t *) pentry;     /* get 16-bit module callgate */
        *psel = pcte->cte_sel;
    }

    else                            /* 32-bit program */
    {

#ifdef LDRSTRICT
        if (pet->et_type != GATE16)               /* if '286 call gate' */
            panic ("ldrGetGate: 286 call gate in 386 module");
#endif

        p16iopl = (ldret16iopl_t *) pentry;   /* get 32-bit module callgate */
        *psel = p16iopl->et16iopl_call;
    }

    return( NO_ERROR );

    pet;
}



/***LD  ldrOrdinalMap - Alternate ordinals to map to if memory protection
 *                       is in effect.
 *      If memory protection is in effect (MEMMAN=PROTECT), the shared memory
 *      is allocated from the protected region (between 448 and 512 meg region,
 *      currently). All APIs related to memory allocation from the shared arena
 *      are remapped to their protected counterparts if the module in question
 *      is either:
 *      - a 16-bit DLL listed as protected in config.sys (PROTECT16=...)
 *      - a 32-bit DLL which has implicit protection bit set in its header
 */
struct ldrOrdinalMap
{
        ushort_t ord_from;      /* This ordinal is mapped to ... */
        ushort_t ord_to;        /* ... this ordinal */
} ldrOrdinalMap[] =
        {
                ORD_DOSALLOCSEG,                ORD_DOSALLOCPROTSEG,
                ORD_DOSALLOCSHRSEG,             ORD_DOSALLOCSHRPROTSEG,
                ORD_DOSALLOCHUGE,               ORD_DOSALLOCPROTHUGE,
                ORD_DOS32ALLOCSHAREDMEM,        ORD_DOS32ALLOCPROTECTEDMEM,
                0,                              0
        };

/***LP  ldrMapOrdinal - Map ordinal to an alternate ordinal if it is in the
 *                      list of shared memory APIs
 *
 *      ENTRY   ord     ordinal to map
 *
 *      EXIT    neword  Alternate ordinal
 *                      If the ordinal is in the list, then a corres. protect
 *                      memory API ordinal is returned, else neword = ord
 *
 */
ushort_t ldrMapOrdinal( register ushort_t  ord )
{
    register struct ldrOrdinalMap *plop;

    for (plop=ldrOrdinalMap; plop->ord_from ; plop++)
    {
                                            /* Scan ordinal table */
        if (plop->ord_from == ord)          /* Match found */
            return (plop->ord_to);
    }
    return (ord);                           /* ordinal not in list */
}


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

/***LP  ldrSelFwdFlg - convert flat selector into forwarder flags
 *
 *      Look up the selector in the table of flat selector addresses and
 *      if found, return the index shifted into the proper field.  Else
 *      return 0.
 *
 *      ENTRY   sel                     - flat selector
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 */

int ldrSelFwdFlg( ushort_t sel )
{
    register SEL **p;
    register SEL **pEnd;

    pEnd = &FlatSelTbl[sizeof(FlatSelTbl) / sizeof(SEL *)];
    for (p = FlatSelTbl; p < pEnd; ++p)
        if (*p && **p == sel)
            return((p - FlatSelTbl) << FWD_SHIFT);
    return(0);
}



/***LP  ldrGetFwdEnt - Get the target address of a forwarder entry
 *
 *      Process a chain of forwarders.  Traverse the chain to the end
 *      and get the target address.  Save a relatively small portion
 *      of the chain on the stack, starting with the head, and patch
 *      that portion with the target address.  The first is required,
 *      the rest are an optimization.
 *
 *      ENTRY   pmte - pointer to source module table entry
 *              pent - pointer to forwarder entry table entry
 *
 *      EXIT    int  - return code (NO_ERROR if successful)
 */
int ldrGetFwdEnt( register ldr32et_t *pent,
                  ldrmte_t           *pmte,
                  ptda_t             *pptda )
{
    ldrmte_t            *ptgtmte;           // Pointer to target mte
    ushort_t            entord;             // Entry ordinal number
    ldr32et_t           fwd;                // Processed forwarder
    struct taddr_s      taddr;              // Target address info block
    int                 retcode;            // Return code
    ldr32et_t           *pfwdTable[SMALLFWDMAX];
                                            // Small table of forwarder ptrs
    int                 cFwd;               // Count of forwarders on chain
    long                dummy;              // Dummy to force page fault

    cFwd = 0;
    for (;;)
    {
        /*
         * If room left in table, save this forwarder pointer.
         */
        if (cFwd < SMALLFWDMAX)
            pfwdTable[cFwd] = pent;
        /*
         * Use an arbitrarily high limit on the number of forwarders in a
         * chain.  This is an inexpensive way to check for circular chains.
         */
        if (++cFwd > LDRFWDMAX)
        {
            ldrStop(0, NULL);
            return(ERROR_BAD_EXE_FORMAT);     // BUGBUG: need new error code
        }
        /*
         * Get target mte and entry ordinal.
         */
        if ( retcode = ldrGetImport(pmte,
                                    pent->et_value,
                                    pent->et_modord,
                                    (pent->et_flags & FWD_ORDINAL) == 0,
                                    SSToDS(&ptgtmte),
                                    SSToDS(&entord) ) )
        {
            ldrStop(1, NULL);
            return(retcode);
        }
        /*
         * Tell ldrGetEntAddr() not to process forwarder chains since we
         * are doing it now.  Use lflags parameter of OBJIOPL to ensure
         * that non-callgate selector and offset will be returned if GATE16
         * entry point found.
         * This is a semi-recursive call to ldrGetEntAddr.  Adding a parameter
         * to ldrGetEntAddr is cleaner, but this is more convenient.
         */
        pTCBCur->TCBMiscFlags |= TMF_NoFwd;
        while (TRUE)
        {
            if ( !(retcode = ldrGetEntAddr(entord,
                                           ptgtmte,
                                           SSToDS(&taddr),
                                           OBJIOPL,
                                           pmte,
                                           pptda ) ))
            {
                break;          /* found ordinal in entry table */
            }
            if ((ptgtmte->mte_flags1 & DOSMOD) == 0)
            {
                break;          /* wasn't DOSCALLS */
            }
            /*
             * not found in DOSCALLS, try getting address from DOSCALL1
             */
            if ( retcode = ldrGetTgtMte(pent->et_modord,
                                        pmte,
                                        SSToDS(&ptgtmte) ) )
            {
                break;
            }
            if (ptgtmte == NULL)
            {
                retcode = ERROR_BAD_EXE_FORMAT;
                ldrStop(1, NULL);
                break;
            }
        }
        pTCBCur->TCBMiscFlags &= ~TMF_NoFwd;
        pmte = ptgtmte;
        /*
         * Break if the entry is anything other than an unprocessed forwarder.
         */
        if (retcode == NO_ERROR)
            break;
        if (retcode != ERROR_LDR_FWD)
            return(retcode);
        /*
         * Advance to the next forwarder in the chain.
         */
        pent = (ldr32et_t *) taddr.toff;
    }
    /*
     * Build forwarder from target address structure.
     */
    E32_EFLAGS(fwd) = taddr.fflags | (uchar_t) FWD_PROCESSED;
    /*
     * For all types of entry points, the 32-bit value is the target offset
     * value.  Interpretation will vary based on entry and fixup type.
     */
    E32_VALUE(fwd) = taddr.toff;
    /*
     * If target module is FSHMOD, DOSMOD, or MVDMMOVD then save selector
     * separately.
     */
    if (taddr.fflags & FWD_SPECIAL)
        E32_MODORD(fwd) = taddr.tsel;
    /*
     * If target entry is 286 callgate, then mark as call gate, save
     * call gate selector and flat selector.  ldrGetEntAddr() was
     * forced to save the linearized 16:16 selector and offset.
     */
    else if (taddr.fflags & FWD_GATE16)
    {
        E32_MODORD(fwd) = taddr.cgsel;
        E32_EFLAGS(fwd) |= ldrSelFwdFlg(taddr.tsel);
    }
    /*
     * Else, this a regular entry point.  Save the flat selector.
     */
    else
        E32_EFLAGS(fwd) |= ldrSelFwdFlg(taddr.tsel);
    /*
     * Fix up as much of the forwarder chain as we can.
     */
    if (cFwd > SMALLFWDMAX)
        cFwd = SMALLFWDMAX;
    while (cFwd--)
    {
        pent = pfwdTable[cFwd];
        /*
         * If the forwarder crosses a page boundary, load a DWORD which
         * crosses that boundary.  That will force both pages into memory
         * so it is impossible to fault on the next operation of writing
         * the fixed-up forwarder to memory.  If a fault occurs and this
         * thread is blocked after a partial write, then another loader
         * thread may encounter the same forwarder which would be garbage.
         */
        if (vmPageCeil(pent) < (ulong_t) pent + sizeof(*pent))
          dummy = * (volatile long *) (vmPageCeil(pent) - 2);
        /*
         * Write the fixed-up forwarder to memory.
         */
        *pent = fwd;
    }
    return(NO_ERROR);
}

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


/***LP  ldrGetImport - Get import information
 *
 *      Get the target module table entry and entry table ordinal number
 *      for a given import.
 *
 *      ENTRY   pmte                    - pointer to source mte
 *              value                   - names table offset or entry ordinal
 *              modord                  - import module ordinal
 *              fByname                 - TRUE if import by name
 *
 *      EXIT    pptgtmte                - ptr to pt to target mte
 *              pentord                 - pointer to entry ordinal
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 *
 *  This procecure performs the following steps:
 *
 *          if (target module is 0 or DOSCALL1)
 *              use DOSCALLS for target module
 *          if (target is imported by name)
 *              verify entire name is addressable
 *              if target name begins with '#'
 *                  convert ascii string to ordinal number
 *              else
 *                  forever
 *                      get ordinal number by searching for matching name
 *                      if (found)
 *                          break
 *                      if (target is not DOSCALLS or source is DOSCALL1)
 *                          return error
 *                      otherwise try search again with DOSCALL1 module
 *          else
 *              get ordinal number from relocation record
 */
int ldrGetImport( ldrmte_t  *pmte,
                  long       value,
                  ushort_t   modord,
                  int        fByName,
                  ldrmte_t **pptgtmte,
                  ushort_t  *pentord )
{
    char      *impnamoff;
    ldrsmte_t *psmte;
    int        rc;
    int        fDOSCALLS=FALSE; /* TRUE if target is DOSCALLS */

    psmte = pmte->mte_swapmte;

    if ( rc = ldrGetTgtMte(modord, pmte, pptgtmte) )
    {
        return(rc);
    }
    if (*pptgtmte == NULL || ((*pptgtmte)->mte_flags1 & DOSLIB))
    {
        *pptgtmte = &DosModMte;
        fDOSCALLS = TRUE;
    }
    if (fByName)
    {
        impnamoff = (char *) value;
        impnamoff += psmte->smte_impproc;
        if (impnamoff < (char *) psmte->smte_impproc ||
                impnamoff + (ulong_t) impnamoff[0] >=
                (char *) psmte->smte_fpagetab +
                psmte->smte_fixupsize)
        {
            ldrStop(0, NULL);
            return(ERROR_BAD_EXE_FORMAT);
        }
        if (impnamoff[1] == '#')
        {
            pTCBCur->TCB_ProcNameBuf = impnamoff;
            *pentord = (ushort_t) ldrAscToOrd(&impnamoff[2],impnamoff[0] - 1);
        }
        else
        {
            while (TRUE)
            {
                ldrSetupTgtErrTxt(
                   (PCHAR) ((*pptgtmte)->mte_swapmte->smte_restab) + 1,
                   (USHORT) (*(PCHAR) ((*pptgtmte)->mte_swapmte->smte_restab)));
                if ((rc = ldrGetOrdNum(*pptgtmte,
                                       impnamoff,
                                       pentord)) == NO_ERROR)
                {
                    break;              /* found the name */
                }
                if (((*pptgtmte)->mte_flags1 & DOSMOD) == 0)
                {
                    return(rc);         /* wasn't DOSCALLS */
                }
                if ( rc = ldrGetTgtMte(modord, pmte, pptgtmte) )
                {
                    return(rc);
                }
                if (*pptgtmte == NULL)
                {
                    ldrStop(1, NULL);
                    return(ERROR_BAD_EXE_FORMAT);
                }
                /* wasn't found in DOSCALLS, try searching DOSCALL1 */
                fDOSCALLS = FALSE;
            }                           /* end of while */
        }                               /* end of else */
    }
    else                              /* Else import by ordinal number */
    {
        *pentord = (ushort_t) value;
    }
//  if (fDOSCALLS && (pmte->mte_flags1 & MTEMODPROT))   // ?????
//  {
//      *pentord = ldrMapOrdinal(*pentord);
//  }
    return(NO_ERROR);
}


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


/***LP  ldrFreeCallGate - Free shared access to callgate for IOPL segment.
 *
 *      This routine actually removes the call gate by freeing its
 *      selector.
 *
 *      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
 *
 *      - get callgate from entry table entry
 *      - check if selector allocated
 *      - check if a GDT selector
 *      -   call GDTFree
 *      - else
 *      -   call LDTFreeShared
 *
 */

ulong_t ldrFreeCallGate( ldret_t  *pet,    // pointer to entry table bundle
                         ulong_t  *pentry, // pointer to entry table entry
                         ldrmte_t *pmte,   // pointer to module table entry
                         ldrlv_t  *plv )   // pointer to local vars
{
    ushort_t                sel;    /* selector from entry table entry */
    struct seldesc_s        desc;

    if (pet->et_type != 0xff)       /* if 'movable' */
        return(NO_ERROR);

                                    /* get callgate from entry table */
    ldrGetGate(pet, pentry, pmte, SSToDS(&sel));

    /*
     * check if selector is allocated and is a GDT selector then call
     * GDTFree else it must be a LDT selector so free shared selector.
     * If selector is not allocated don't do anything.
     */
    if (sel == 0)
        return(NO_ERROR);

    /* If this is an LDT selector and the LDT has been freed, or
     * the selector is not valid, do nothing and return NO_ERROR
     */

    if (((sel & TABLE_MASK) && pPTDACur->ptda_ldthandle == HOBNULL) ||
        SELGetDescInfo(sel,pPTDACur->ptda_handle,SSToDS(&desc)) !=
          NO_ERROR)
        return(NO_ERROR);       /* selector already freed by VMFreeTask */

    /* No need to call SELFreeLDT after VMFreeTask is called */

    if (!(sel & TABLE_MASK))                /* check for LDT selector */
        SELFreeGDT(sel);

    return(NO_ERROR);

    plv;
}




/***LP  ldrFreeLDTGate - Free LDT callgate for IOPL segment.
 *
 *      If this is an LDT callgate, this routine frees the LDT callgate
 *      selector from the LDT.
 *
 *      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
 *
 *      - get callgate from entry table entry
 *      - check if selector allocated
 *      - check if a GDT selector
 *      - check if attached
 *      - free LDT callgate
 *
 */

ulong_t ldrFreeLDTGate( ldret_t  *pet,    /* pointer to entry table bundle */
                        ulong_t  *pentry, /* pointer to entry table entry */
                        ldrmte_t *pmte,   /* pointer to module table entry */
                        ldrlv_t  *plv )   /* pointer to local vars */
{
    ushort_t                sel;
    struct seldesc_s        desc;

    if (pet->et_type != 0xff)       /* if 'movable' */
        return(NO_ERROR);

    /*
     *      get callgate sel from entry table
     */
    ldrGetGate (pet, pentry, pmte, SSToDS (& sel));
    if ((sel != 0) && (sel & TABLE_MASK))
    {

#ifdef LDRSTRICT
        if ( SELGetDescInfo( sel, pPTDACur->ptda_handle, SSToDS(&desc) ) )
            panic ("ldrFreeLDTGate:SELGetDescInfo");
#else
        SELGetDescInfo( sel, pPTDACur->ptda_handle, SSToDS(&desc) );
#endif

        if (desc.desc_acc & D_GATE3)
            SELFreeLDT (sel, 1, pPTDACur->ptda_handle);
    }
    return(NO_ERROR);

    plv;
}


#if 0

/***LP  ldrIsModuleProtected - Check to see if the module name exists in the
 *                             PROTECT16 list
 *
 *      As per DCR 402, some 16-bit DLLs can be specified as protected in
 *      config.sys PROTECT16 list. This routine scans that list and returns
 *      TRUE if the module name exists in it.
 *
 *      ENTRY   NONE            Module name string is taken from pPTDACur->ptda_pLdrBuf
 *                              This is because we cannot expect the format of
 *                              the string otherwise. The ModName passed to
 *                              GetMte is not necessarily zero terminated. Also
 *                              it may or may not be fully qualified, infact
 *                              it most often is partially qualified. The
 *                              following are the possible formats:
 *                              - FOOBAR.DLL\0
 *                              - FOOBAR\0
 *                              - YUK\FOOBAR\0
 *                              - FOOBAR\04FBAR\0
 *                              The last one is the tricky one to handle. We can
 *                              get away by looking at ptda_pLdrBuf instead because
 *                              at this point it has the canonicalised pathname.
 *
 *      EXIT    TRUE            if module is in the list
 *              FALSE           Otherwise
 */
int ldrIsModuleProtected()
{

    return( 0 );

#if 0

    char            *pname; /* Pointer to unqualified module name */
    register char   *p, *q; /* Scratch pointers for scanning */
    register int    i;
    int             len;

    if (pchProt16List == NULL)      /* Detect early failure */
        return (FALSE);

    /*
     * Since we know that the string at ptda_pLdrBuf has a ".DLL" extension,
     * we need to match only the file-name with the path and extension
     * stripped off
     */
    pname = LdrGetModIndx(SSToDS(pPTDACur->ptda_pLdrBuf), SSToDS(&len));
    ldrUCaseString(pname, len);
    /*
     * pname now points to string of the form:
     * pppppppp.dll
     * pchProt16List points to a string of the form:
     * NaaaaaMbbbbbbPccccQdddddddReeeeeeee
     * where N,M,P,Q,R are lengths of the strings following
     * In this case N=5,M=6,P=4,Q=7,R=8
     * We try to match the two strings for the specified len.
     */
    for (p = pname, q = pchProt16List; ; p = pname, q += i)
    {
        if ((i = (int)*q++) == 0)           /* end of list ? */
            return (FALSE);                 /* Yes, match failed */
        if (ldrMatchStringsN(p, q, len))
            return (TRUE);
    }

#endif

}
#endif


/*
 *  End of swappable code
 */

#pragma code_seg()
