mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1962 lines
57 KiB
1962 lines
57 KiB
#include <os2tile.h>
|
|
|
|
#include "ldrextrn.h"
|
|
|
|
#define TRC_C_SUC_ret 0
|
|
#define TRC_C_LIB_ret -8
|
|
|
|
VOID
|
|
ldrClearAllMteFlag(
|
|
IN ULONG Flags
|
|
)
|
|
{
|
|
ldrmte_t *pmte;
|
|
|
|
//
|
|
// Clear specific flags of all modules
|
|
//
|
|
pmte = mte_h;
|
|
while (pmte != NULL) {
|
|
pmte->mte_mflags &= ~Flags;
|
|
pmte = pmte->mte_link;
|
|
}
|
|
}
|
|
|
|
//
|
|
// This is set to interface the assembly routine
|
|
// _ldrSetDescInfo in i386\ldrstart.asm
|
|
// Such that it is immune to kernel changes
|
|
//
|
|
|
|
NTSTATUS
|
|
SetLDT(
|
|
IN HANDLE ProcessHandle,
|
|
IN PROCESSINFOCLASS ProcessInformationClass,
|
|
IN PVOID ProcessInformation,
|
|
IN ULONG ProcessInformationLength
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER(ProcessInformationClass);
|
|
return NtSetInformationProcess (ProcessHandle,
|
|
ProcessLdtInformation,
|
|
ProcessInformation,
|
|
ProcessInformationLength);
|
|
|
|
}
|
|
|
|
/***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)
|
|
*
|
|
*/
|
|
|
|
int ldrEachObjEntry(
|
|
ULONG objnum, /* object number to search on */
|
|
register ldrmte_t *pmte, /* pointer to object table entry */
|
|
int (*pworker)(ldret_t *pet, ulong_t *pentry,
|
|
ldrmte_t *pmte, ldrlv_t *plv),/* pointer to worker routine */
|
|
ldrlv_t *plv /* pointer to local vars */
|
|
)
|
|
{
|
|
register ldrsmte_t *psmte; /* pointer to swappable mte */
|
|
USHORT etobj; /* object number from entry table */
|
|
ULONG pentry; /* pointer to entry table entry */
|
|
struct ExpHdr *pexpdir; /* pointer to export directory */
|
|
ULONG i;
|
|
int rc;
|
|
|
|
/*
|
|
* validate mte pointer
|
|
*/
|
|
if (!fMTEValid(pmte))
|
|
ASSERT("Eachobjentry: Invalid MTE");
|
|
|
|
psmte = pmte->mte_swapmte;
|
|
|
|
if (ldrIsNE(pmte)) { /* process 16-bit module */
|
|
register ldret_t *pet; /* pointer to entry table */
|
|
|
|
/*
|
|
* does module contain an entry table
|
|
*/
|
|
if ((pet = (ldret_t *) (psmte->smte_enttab)) == 0)
|
|
return(NO_ERROR);
|
|
|
|
while (TRUE) {
|
|
if (pet->et_cnt == 0) /* check for end of table */
|
|
return(NO_ERROR);
|
|
|
|
if (pet->et_type == EMPTY) { /* check for empty bundle */
|
|
(ULONG) pet += (ULONG) ldrSkipEnts(pmte, pet->et_type,
|
|
(UCHAR) pet->et_cnt);
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* skip over count and type fields
|
|
*/
|
|
pentry = (ULONG) pet +
|
|
(ULONG) ldrSkipEnts(pmte, pet->et_type, 0);
|
|
|
|
/*
|
|
* call routine for each entry
|
|
*/
|
|
for (i = 1; i <= (ULONG) pet->et_cnt; i++) {
|
|
if (pet->et_type == B16MOVABLE)
|
|
etobj = (USHORT) (((ldrcte_t *) pentry)->cte_obj);
|
|
else
|
|
etobj = pet->et_type;
|
|
if ((objnum == 0) || (objnum == (ULONG) etobj)) {
|
|
/*
|
|
* call worker routine
|
|
*/
|
|
if ((rc = pworker(pet, (PULONG) pentry, pmte,
|
|
plv)) != NO_ERROR)
|
|
return(rc);
|
|
}
|
|
if (pet->et_type == B16MOVABLE)
|
|
pentry += sizeof(ldrcte_t);
|
|
else
|
|
pentry += sizeof(ldrent_t);
|
|
}
|
|
(ULONG) pet += pentry - (ULONG) pet;
|
|
|
|
}
|
|
}
|
|
else { /* 32-bit module */
|
|
register PULONG peat; /* pointer to export addr tb entry */
|
|
|
|
pexpdir = (struct ExpHdr *) psmte->smte_expdir;
|
|
peat = (ULONG *) ((ULONG) pexpdir + pexpdir->exp_eat);
|
|
i = pexpdir->exp_eatcnt;
|
|
for (; i--; peat++) {
|
|
|
|
/*
|
|
* call worker routine
|
|
*/
|
|
if ((rc = pworker(NULL, peat, pmte, plv)) != NO_ERROR)
|
|
return(rc);
|
|
}
|
|
}
|
|
|
|
return(NO_ERROR);
|
|
|
|
}
|
|
|
|
|
|
/***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 pmte - pointer to mte for this module
|
|
* 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(pmte, type, count)
|
|
ldrmte_t *pmte;
|
|
uchar_t type;
|
|
uchar_t count;
|
|
{
|
|
if (ldrIsNE(pmte)) /* check for 16-bit module */
|
|
switch (type) {
|
|
|
|
case B16EMPTY: /* unused bundle */
|
|
return(sizeof(ldrempty_t));
|
|
|
|
case B16MOVABLE: /* movable object */
|
|
return(sizeof(ldrempty_t) + sizeof(ldrcte_t) * count);
|
|
|
|
default: /* fixed object */
|
|
return(sizeof(ldrempty_t) + sizeof(ldrent_t) * count);
|
|
}
|
|
else { /* 32-bit module */
|
|
ldrAssert(FALSE); /* should not get here */
|
|
}
|
|
}
|
|
|
|
|
|
/***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(pet, pentry, pmte, plv)
|
|
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;
|
|
|
|
UNREFERENCED_PARAMETER(plv);
|
|
|
|
if (ldrIsNE(pmte)) { /* 16-bit module */
|
|
|
|
flags = ((ldrent_t *) pentry)->ent_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_mflags & LIBRARYMOD) ||
|
|
(pmte->mte_mflags & LIBRARYMOD && flags & EF_GDATA)) {
|
|
|
|
/*
|
|
* Remove offset and object number from entry table bundle
|
|
*/
|
|
if (pet->et_type == B16MOVABLE) {
|
|
offset = ((ldrcte_t *) pentry)->cte_off;
|
|
obj = ((ldrcte_t *) pentry)->cte_obj;
|
|
}
|
|
else {
|
|
offset = ((ldrent_t *) pentry)->ent_off;
|
|
obj = pet->et_type;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* check for movable entry
|
|
*/
|
|
if (pet->et_type != B16MOVABLE)
|
|
return(NO_ERROR);
|
|
/*
|
|
* init callgate selector to 0
|
|
*/
|
|
((ldrcte_t *)pentry)->cte_sel = 0;
|
|
|
|
}
|
|
|
|
else { /* 32-bit module */
|
|
/*
|
|
* We do not need to do anything for 32-bit modules
|
|
*/
|
|
return(ERROR_BAD_EXE_FORMAT);
|
|
}
|
|
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
|
|
/***LP ldrEditProlog - Edit prolog for shared data segment
|
|
*
|
|
* 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 special hard coded prologs to functions
|
|
* - modify the prologs
|
|
*/
|
|
|
|
int ldrEditProlog(pet, pentry, pmte, plv)
|
|
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;
|
|
USHORT SharedDataSeg;
|
|
PUCHAR laddr;
|
|
ldrsmte_t *psmte;
|
|
ldrste_t *pste;
|
|
ULONG tmp_opcodes;
|
|
|
|
UNREFERENCED_PARAMETER(plv);
|
|
|
|
flags = ((ldrent_t *) pentry)->ent_flags;
|
|
//
|
|
// Check for prolog editing
|
|
//
|
|
if ((flags & EF_EXPORT) == 0) {
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
//#if DBG
|
|
// DbgPrint("OS2LDR: ldrEditProlog: Processing %s\n", (PCHAR)pmte->mte_modname + 1);
|
|
//#endif
|
|
/*
|
|
* Remove offset and object number from entry table bundle
|
|
*/
|
|
if (pet->et_type == B16MOVABLE) {
|
|
//
|
|
// This is a moveable entry table entry
|
|
//
|
|
offset = ((ldrcte_t *) pentry)->cte_off;
|
|
obj = ((ldrcte_t *) pentry)->cte_obj;
|
|
}
|
|
else if (pet->et_type == B16ABSOLUTE) {
|
|
//
|
|
// This is an absolute entry table entry
|
|
//
|
|
return(NO_ERROR);
|
|
}
|
|
else {
|
|
//
|
|
// This is a fixed entry table entry
|
|
//
|
|
offset = ((ldrent_t *) pentry)->ent_off;
|
|
obj = pet->et_type;
|
|
}
|
|
|
|
psmte = pmte->mte_swapmte;
|
|
if (psmte->smte_autods == 0) {
|
|
return(NO_ERROR);
|
|
}
|
|
pste = ldrNumToSte(pmte, psmte->smte_autods);
|
|
SharedDataSeg = pste->ste_selector | 7;
|
|
if (SharedDataSeg == 0) {
|
|
ASSERT(FALSE);
|
|
#if DBG
|
|
DbgPrint("OS2LDR: Strange DLL: %s, Segment %d\n",
|
|
(PCHAR)pmte->mte_modname + 1, obj);
|
|
#endif
|
|
}
|
|
pste = ldrNumToSte(pmte, obj);
|
|
laddr = (PCHAR)SELTOFLAT(pste->ste_selector) + offset;
|
|
tmp_opcodes = (*(PULONG)laddr)&0x00FFFFFF;
|
|
if ((tmp_opcodes == 0x90D88C) || // MOV AX,DS + NOP
|
|
(tmp_opcodes == 0x90581e)) // PUSH DS + POP AX + NOP
|
|
{
|
|
//#if DBG
|
|
// DbgPrint("OS2LDR: Updating addr %x\n", laddr);
|
|
//#endif
|
|
*laddr++ = (UCHAR)0xB8;
|
|
*(PUSHORT)laddr = SharedDataSeg;
|
|
}
|
|
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
/***LP ldrGetCallGate - Create a call gate for ring 2 entries
|
|
*
|
|
* 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 ldrGetCallGate(pet, pentry, pmte, plv)
|
|
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 */
|
|
{
|
|
ldrste_t *pste;
|
|
UCHAR flags;
|
|
ULONG CallGateOffset;
|
|
PR2CallInfo pR2CallEntry;
|
|
|
|
UNREFERENCED_PARAMETER(plv);
|
|
|
|
flags = ((ldrent_t *) pentry)->ent_flags;
|
|
//
|
|
// Check for valid entry for callgate
|
|
//
|
|
if (((flags & EF_EXPORT) == 0) ||
|
|
(pet->et_type != B16MOVABLE)
|
|
) {
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
//#if DBG
|
|
// DbgPrint("OS2LDR: ldrGetCallGate: Processing %s\n", (PCHAR)pmte->mte_modname + 1);
|
|
//#endif
|
|
//
|
|
// Allocate a call gate and place it in the entry
|
|
//
|
|
CallGateOffset = ldrAllocateCallGate();
|
|
if (CallGateOffset == -1) {
|
|
#if DBG
|
|
KdPrint(("OS2LDR: cannot allocate call gate\n"));
|
|
#endif
|
|
return(ERROR_INVALID_CALLGATE);
|
|
}
|
|
|
|
((ldrcte_t *) pentry)->cte_sel = (ushort_t)CallGateOffset;
|
|
pR2CallEntry = (PR2CallInfo)(R2XFER_BASE + CallGateOffset);
|
|
pR2CallEntry->R2CallNearInst = 0xE8;
|
|
pR2CallEntry->R2CommonEntry = (USHORT)(0 - (CallGateOffset + 3));
|
|
pR2CallEntry->R2BytesToCopy = (((ldrcte_t *) pentry)->cte_flags & 0xF8) >> 2;
|
|
pR2CallEntry->R2EntryPointOff = ((ldrcte_t *) pentry)->cte_off;
|
|
pste = ldrNumToSte(pmte, ((ldrcte_t *) pentry)->cte_obj);
|
|
pR2CallEntry->R2EntryPointSel = pste->ste_selector | 7; // force ring 3
|
|
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
|
|
/***LP ldrFreeCallGate - Free the call gate stubs for ring 2 entries
|
|
*
|
|
* 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
|
|
* - mark the call gate entry as free
|
|
*/
|
|
|
|
int ldrFreeCallGate(pet, pentry, pmte, plv)
|
|
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 */
|
|
{
|
|
UCHAR flags;
|
|
ULONG CallGateOffset;
|
|
PR2CallInfo pR2CallEntry;
|
|
|
|
UNREFERENCED_PARAMETER(plv);
|
|
|
|
flags = ((ldrent_t *) pentry)->ent_flags;
|
|
//
|
|
// Check for valid entry for callgate
|
|
//
|
|
if (((flags & EF_EXPORT) == 0) ||
|
|
(pet->et_type != B16MOVABLE)
|
|
) {
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
//#if DBG
|
|
// DbgPrint("OS2LDR: ldrFreeCallGate: Processing %s\n", (PCHAR)pmte->mte_modname + 1);
|
|
//#endif
|
|
//
|
|
// Free the call gate
|
|
//
|
|
CallGateOffset = ((ldrcte_t *) pentry)->cte_sel;
|
|
if ((CallGateOffset == 0) ||
|
|
(CallGateOffset == 0x3fcd)
|
|
) {
|
|
#if DBG
|
|
KdPrint(("OS2LDR: OK to ignore non-initialized call gate 0x%x when app loading fails\n",
|
|
CallGateOffset));
|
|
#endif
|
|
return(NO_ERROR);
|
|
}
|
|
ldrDeallocateCallGate(CallGateOffset);
|
|
pR2CallEntry = (PR2CallInfo)(R2XFER_BASE + CallGateOffset);
|
|
pR2CallEntry->R2CallNearInst = 0;
|
|
pR2CallEntry->R2CommonEntry = 0;
|
|
pR2CallEntry->R2BytesToCopy = 0;
|
|
pR2CallEntry->R2EntryPointOff = 0;
|
|
pR2CallEntry->R2EntryPointSel = 0;
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
|
|
/***LP ldrSetLoaded - set MTEPROCESSED bit in the module flags for all mtes.
|
|
*
|
|
*
|
|
* ENTRY none
|
|
*
|
|
* EXIT none
|
|
*
|
|
* This procedure performs the following steps
|
|
*
|
|
*/
|
|
|
|
void ldrSetLoaded()
|
|
{
|
|
register ldrmte_t *pmte; /* pointer to a module table entry */
|
|
|
|
/*
|
|
* scan list of mtes til end of list is found
|
|
*/
|
|
for (pmte = mte_h; pmte != NULL; pmte = (ldrmte_t *)pmte->mte_link) {
|
|
/*
|
|
* check if mte valid yet
|
|
*/
|
|
if (pmte->mte_mflags & MTEVALID)
|
|
pmte->mte_mflags |= MTEPROCESSED; /* mark as loaded */
|
|
pmte->mte_mflags &= ~MTENEWMOD;
|
|
}
|
|
}
|
|
|
|
|
|
/***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(pmte, objnum)
|
|
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);
|
|
}
|
|
|
|
|
|
/***LP LDRStop - stop in kernel debugger
|
|
*
|
|
* If the global DBG is TRUE print a debug message
|
|
*
|
|
* LDRStop (id, pmte)
|
|
*
|
|
* ENTRY id - identifier of caller (ignored)
|
|
* pmte - mte pointer or NULL
|
|
* RETURN NONE
|
|
*
|
|
* CALLS DbgUserBreakPoint
|
|
*
|
|
* EFFECTS NONE
|
|
*/
|
|
void LDRStop(id, pmte)
|
|
int id;
|
|
void *pmte;
|
|
|
|
{
|
|
UNREFERENCED_PARAMETER(id);
|
|
UNREFERENCED_PARAMETER(pmte);
|
|
|
|
#if DBG
|
|
DbgPrint("ldrStop\n");
|
|
// DbgUserBreakPoint();
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
/***LP ldrFindMTEForHandle - Find MTE for given handle
|
|
*
|
|
* ENTRY handle - 16-bit handle
|
|
*
|
|
* EXIT pointer to mte or 0 for not present
|
|
*/
|
|
ldrmte_t *ldrFindMTEForHandle(hmte)
|
|
USHORT hmte;
|
|
|
|
{
|
|
ldrmte_t *pmte;
|
|
|
|
pmte = mte_h;
|
|
|
|
while (pmte != 0) {
|
|
if (pmte->mte_handle == hmte)
|
|
break;
|
|
pmte = pmte->mte_link;
|
|
}
|
|
|
|
return(pmte);
|
|
|
|
}
|
|
|
|
/***LP ldrFindSegForHandleandNum - Given a Handle and a seg number,
|
|
* return the selector
|
|
*
|
|
*/
|
|
USHORT ldrFindSegForHandleandNum(inmte, handle, segnum)
|
|
USHORT inmte;
|
|
USHORT handle;
|
|
USHORT segnum;
|
|
|
|
{
|
|
ldrmte_t *pmte;
|
|
ldrste_t *pste;
|
|
|
|
|
|
if (inmte){
|
|
pmte = ldrFindMTEForHandle(inmte);
|
|
}
|
|
else {
|
|
pmte = ldrFindMTEForHandle(handle);
|
|
if (pmte == NULL) {
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
pste = ldrNumToSte(pmte, segnum);
|
|
|
|
if (pste == NULL){
|
|
return(0);
|
|
}
|
|
|
|
return(pste->ste_selector);
|
|
|
|
}
|
|
|
|
// ldrFindDLDForHandle - Find the DLD which points to the
|
|
// requested handle.
|
|
//
|
|
// ENTRY pmte_prog - MTE ptr of the process MTE
|
|
// handle - Handle of module
|
|
//
|
|
// EXIT Pointer to the DLD if the module belongs to the process
|
|
// NULL otherwise
|
|
//
|
|
ldrdld_t *
|
|
ldrFindDLDForHandle(
|
|
ldrmte_t *pmte_prog,
|
|
USHORT handle
|
|
)
|
|
{
|
|
ldrdld_t *pdld;
|
|
|
|
pdld = pmte_prog->mte_dldchain;
|
|
while (pdld != NULL) {
|
|
if ((pdld->Cookie == (ULONG)CurrentThread->Process) &&
|
|
(pdld->dld_mteptr->mte_handle == handle)) {
|
|
break;
|
|
}
|
|
pdld = pdld->dld_next;
|
|
}
|
|
return(pdld);
|
|
}
|
|
|
|
// ModuleIsAttachedToProcess - Verify that a module that was statically
|
|
// loaded at process start belongs to the
|
|
// current procee
|
|
//
|
|
// ENTRY pmte_prog - MTE ptr of the process MTE
|
|
// pmte - MTE ptr of the module MTE
|
|
//
|
|
// EXIT TRUE if the module belongs to the process
|
|
// FALSE otherwise
|
|
//
|
|
BOOLEAN
|
|
ModuleIsAttachedToProcess(
|
|
ldrmte_t *pmte_cur,
|
|
ldrmte_t *pmte
|
|
)
|
|
{
|
|
ldrmte_t **ppmte;
|
|
ULONG lindex;
|
|
ULONG i;
|
|
|
|
if (pmte_cur == pmte) {
|
|
return(TRUE);
|
|
}
|
|
pmte_cur->mte_mflags |= INGRAPH;
|
|
|
|
ppmte = (ldrmte_t **) pmte_cur->mte_modptrs;
|
|
for (i = 1; i <= pmte_cur->mte_impmodcnt; i++) {
|
|
/*
|
|
* It is required for 16-bit modules to load the
|
|
* referneced module in reverse order.
|
|
*/
|
|
lindex = pmte_cur->mte_impmodcnt-i;
|
|
|
|
//
|
|
// Check if the referenced module has already been processed.
|
|
// Processing the modules is done in reverse order.
|
|
//
|
|
if ((ppmte[lindex]->mte_mflags & INGRAPH) == 0) {
|
|
if (ModuleIsAttachedToProcess(ppmte[lindex], pmte)) {
|
|
return(TRUE);
|
|
}
|
|
}
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
// ModuleLoadedForProcess - Verify that a loaded module belongs
|
|
// to the current procee
|
|
//
|
|
// ENTRY pmte_prog - MTE ptr of the process MTE
|
|
// pmte - MTE ptr of the module MTE
|
|
//
|
|
// EXIT TRUE if the module belongs to the process
|
|
// FALSE otherwise
|
|
//
|
|
BOOLEAN
|
|
ModuleLoadedForProcess(
|
|
ldrmte_t *pmte_prog,
|
|
ldrmte_t *pmte
|
|
)
|
|
{
|
|
ldrdld_t *pdld;
|
|
|
|
ldrClearAllMteFlag(INGRAPH); // Clear INGRAPH flag. We might start
|
|
// recursive search for module.
|
|
|
|
pdld = pmte_prog->mte_dldchain;
|
|
while (pdld != NULL) {
|
|
if (pdld->Cookie == (ULONG)CurrentThread->Process) {
|
|
if(pdld->dld_mteptr == pmte) {
|
|
return(TRUE);
|
|
}
|
|
// Check if module was attached to the module
|
|
// that was loaded dynamically.
|
|
else {
|
|
if (ModuleIsAttachedToProcess(pdld->dld_mteptr, pmte)) {
|
|
return(TRUE);
|
|
}
|
|
}
|
|
}
|
|
pdld = pdld->dld_next;
|
|
}
|
|
|
|
return (ModuleIsAttachedToProcess(pmte_prog, pmte));
|
|
}
|
|
|
|
// ldrCreateDLDRecord - Create a DLD record for the new module
|
|
//
|
|
// ENTRY pmte_prog - MTE ptr of the process MTE
|
|
// pmte - MTE ptr of the module MTE
|
|
// DLDExists - Pointer to BOOLEAN variable which is set to
|
|
// TRUE if the module was already loaded by this program
|
|
// FALSE if the module is a new module for the program
|
|
//
|
|
// EXIT rc - return value indicating the success o fthe function
|
|
//
|
|
APIRET
|
|
ldrCreateDldRecord(
|
|
ldrmte_t *pmte_prog, /* Pointer to process MTE */
|
|
ldrmte_t *pmte, /* Pointer to new loaded module's MTE */
|
|
BOOLEAN *DLDExisted /* Flag indicating if the DLD already existed */
|
|
)
|
|
{
|
|
ldrdld_t *pdld;
|
|
|
|
pdld = pmte_prog->mte_dldchain;
|
|
while (pdld != NULL) {
|
|
if ((pdld->Cookie == (ULONG)CurrentThread->Process) &&
|
|
(pdld->dld_mteptr == pmte)) {
|
|
//
|
|
// A DLD which references the newly loaded module was found
|
|
//
|
|
pdld->dld_usecnt++;
|
|
*DLDExisted = TRUE;
|
|
return(NO_ERROR);
|
|
}
|
|
pdld = pdld->dld_next;
|
|
}
|
|
//
|
|
// The newly loaded module is not referenced yet in the DLD chain.
|
|
// Create and link a new record into the chain.
|
|
//
|
|
pdld = RtlAllocateHeap(LDRNEHeap, 0, sizeof(ldrdld_t));
|
|
if (pdld == NULL) {
|
|
*DLDExisted = FALSE;
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
pdld->dld_usecnt = 1;
|
|
pdld->dld_mteptr = pmte;
|
|
pdld->Cookie = (ULONG)CurrentThread->Process;
|
|
pdld->dld_next = pmte_prog->mte_dldchain;
|
|
pmte_prog->mte_dldchain = pdld;
|
|
*DLDExisted = FALSE;
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
/***EP LDRDosLoadModule - Load dynamic link library module.
|
|
*
|
|
* This routine loads a dynamic link library module and returns
|
|
* a handle for the library.
|
|
*
|
|
* ENTRY pszFailName - ptr to buffer for name if failure
|
|
* cbFileName - length of buffer for name if failure
|
|
* pszModName - ptr to module name
|
|
* phmod - ptr to module handle
|
|
*
|
|
* EXIT int - return code (NO_ERROR if successful)
|
|
* pexec_info structure set
|
|
*/
|
|
|
|
BOOLEAN
|
|
LDRDosLoadModule(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
{
|
|
P_LDRLOADMODULE_MSG a = &m->u.LdrLoadModule;
|
|
USHORT class;
|
|
ldrmte_t *pmte_prog; /* Pointer to process MTE */
|
|
ldrmte_t *pmte;
|
|
ldrmte_t *ptmte;
|
|
ldrdld_t *pdld;
|
|
BOOLEAN DLDExisted;
|
|
int rc;
|
|
|
|
#if DBG
|
|
IF_OL2_DEBUG ( TRACE ) {
|
|
DbgPrint("OS2LDR: DosLoadModule() was called\n");
|
|
}
|
|
#endif
|
|
|
|
CurrentThread = t;
|
|
|
|
//
|
|
// Set the fForceUnmap flag to TRUE so that ldrUnloadTagedModules()
|
|
// does unmap the app's freed segments from the app's address space.
|
|
//
|
|
fForceUnmap = TRUE;
|
|
|
|
//
|
|
// Clear the INGRAPH and USE flags of all modules so that we
|
|
// know that this module has already been processed
|
|
//
|
|
ldrClearAllMteFlag(INGRAPH | USED);
|
|
|
|
//
|
|
// Tag all referenced modules with the USED flag
|
|
// so that they are not loaded again
|
|
//
|
|
pmte = t->Process->ProcessMTE;
|
|
ldrTagModuleTree_USED(pmte);
|
|
for (pdld = pmte->mte_dldchain; pdld != NULL; pdld = pdld->dld_next) {
|
|
if (pdld->Cookie == (ULONG)CurrentThread->Process) {
|
|
ldrTagModuleTree_USED(pdld->dld_mteptr);
|
|
}
|
|
}
|
|
ldrClearAllMteFlag(INGRAPH);
|
|
|
|
//
|
|
// Init the Library Intialization routines data structure
|
|
//
|
|
pldrLibiRecord = (ldrlibi_t *)a->InitRecords.Buffer;
|
|
pldrLibiCounter = &a->NumOfInitRecords;
|
|
*pldrLibiCounter = 0;
|
|
|
|
//
|
|
// init the pointer to the error string
|
|
//
|
|
pErrText = &a->FailName;
|
|
|
|
/*
|
|
* Point to ldrLibPathBuf to contain the environment string
|
|
*/
|
|
strncpy(ldrLibPathBuf, a->LibPathName.Buffer, SizeOfldrLibPathBuf);
|
|
ldrLibPathBuf[SizeOfldrLibPathBuf-1] = '\0';
|
|
|
|
#ifndef DBCS // MSKK Aug.20.1993 V-AkihiS
|
|
/*
|
|
* Check for any meta characters
|
|
*/
|
|
if (strpbrk(a->ModuleName.Buffer, "*?") != NULL) {
|
|
m->ReturnedErrorValue = ERROR_INVALID_NAME;
|
|
return(TRUE);
|
|
}
|
|
|
|
if ((strchr(a->ModuleName.Buffer, '.') != NULL) &&
|
|
(strpbrk(a->ModuleName.Buffer, "\\/") == NULL)
|
|
) {
|
|
m->ReturnedErrorValue = ERROR_FILE_NOT_FOUND;
|
|
return(TRUE);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Check if module we are loading has any path characters
|
|
*/
|
|
if (strpbrk(a->ModuleName.Buffer, ":\\/.") == NULL)
|
|
class = CLASS_GLOBAL;
|
|
else
|
|
class = CLASS_SPECIFIC;
|
|
|
|
if ((rc = ldrGetModule(a->ModuleName.Buffer,
|
|
a->ModuleName.Length,
|
|
(char)EXT_LIBRARY,
|
|
class,
|
|
&pmte,
|
|
NULL,
|
|
NULL)) == NO_ERROR) {
|
|
pmte_prog = (ldrmte_t *)t->Process->ProcessMTE;
|
|
ASSERT(pmte_prog != NULL);
|
|
rc = ldrCreateDldRecord(pmte_prog, pmte, &DLDExisted);
|
|
if (rc != NO_ERROR) {
|
|
ldrUnloadTagedModules(t->Process);
|
|
m->ReturnedErrorValue = rc;
|
|
return(TRUE);
|
|
}
|
|
a->ModuleHandle = pmte->mte_handle;
|
|
//
|
|
// Increment the usecnt of the referenced modules
|
|
//
|
|
if (!DLDExisted) {
|
|
ptmte = mte_h;
|
|
while (ptmte != NULL) {
|
|
if ((ptmte->mte_mflags & INGRAPH) != 0) {
|
|
ptmte->mte_usecnt++;
|
|
}
|
|
ptmte = ptmte->mte_link;
|
|
}
|
|
}
|
|
/*
|
|
* get module startup parameters
|
|
*/
|
|
rc = ldrGetModParams(CurrentThread->Process->ProcessMTE,
|
|
(ldrrei_t *)&a->ExecInfo
|
|
);
|
|
#if DBG
|
|
IF_OL2_DEBUG ( MTE ) {
|
|
DbgPrint("\nDumping segmenst after DosLoadModule() processing\n");
|
|
ldrDisplaySegmentTable();
|
|
}
|
|
#endif
|
|
|
|
}
|
|
else {
|
|
ldrWriteErrTxt(rc);
|
|
ldrUnloadTagedModules(t->Process);
|
|
}
|
|
|
|
m->ReturnedErrorValue = rc;
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/***EP LDRDosGetProcAddr - Get dynamic link procedure address
|
|
*
|
|
*
|
|
* ENTRY hmte Module handle returned by DOSLOADMODULE
|
|
* pchname ASCIIZ name of procedure.
|
|
* paddress Ptr to where the address should be returned.
|
|
*
|
|
* EXIT NO_ERROR paddress has the valid address
|
|
* ERROR_PROC_NOT_FOUND
|
|
*/
|
|
|
|
BOOLEAN
|
|
LDRDosGetProcAddr(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
{
|
|
P_LDRGETPROCADDR_MSG a = &m->u.LdrGetProcAddr;
|
|
ldrmte_t *pmte_prog; /* Pointer to process MTE */
|
|
ldrmte_t *pmte; /* Pointer to MTE */
|
|
struct taddr_s taddr;
|
|
int cch; /* Length of name */
|
|
ulong_t ulord;
|
|
int rc; /* Return code */
|
|
|
|
CurrentThread = t;
|
|
|
|
do { /* Dummy loop */
|
|
//
|
|
// Verify that the module belongs to the current process
|
|
//
|
|
pmte_prog = (ldrmte_t *)t->Process->ProcessMTE;
|
|
ASSERT(pmte_prog != NULL);
|
|
|
|
pmte = ldrFindMTEForHandle((USHORT)a->ModuleHandle);
|
|
if (pmte == NULL) {
|
|
rc = ERROR_INVALID_HANDLE;
|
|
break;
|
|
}
|
|
if (!ModuleLoadedForProcess(pmte_prog, pmte)) {
|
|
rc = ERROR_INVALID_HANDLE;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* See if name specified
|
|
*/
|
|
if (!a->ProcNameIsOrdinal) {
|
|
cch = a->ProcName.Length;
|
|
if (cch > MAX_PROC_LEN) {
|
|
rc = ERROR_INVALID_NAME;
|
|
break; /* Exit */
|
|
}
|
|
|
|
if (a->ProcName.Buffer[0] != '#') {
|
|
|
|
rc = ldrGetOrdNum(pmte,
|
|
a->ProcName.Buffer,
|
|
(PUSHORT) &ulord,
|
|
STRINGNULLTERM);
|
|
|
|
if (rc != NO_ERROR)
|
|
break; /* Exit if name not found */
|
|
}
|
|
|
|
else {
|
|
ulord = atol(&a->ProcName.Buffer[1]);
|
|
}
|
|
|
|
}
|
|
else {
|
|
ulord = a->OrdinalNumber;
|
|
}
|
|
|
|
memset(&taddr, 0, sizeof(taddr));
|
|
rc = ldrGetEntAddr((USHORT) ulord,
|
|
pmte,
|
|
&taddr,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (rc != NO_ERROR)
|
|
break; /* Break if error */
|
|
|
|
ldrAssert(taddr.toff < _64K);
|
|
taddr.toff += (ulong_t) taddr.tsel << WORDSHIFT;
|
|
a->ProcAddr = (ULONG) taddr.toff;
|
|
|
|
} while(FALSE); /* End dummy loop */
|
|
m->ReturnedErrorValue = rc; /* Return error code */
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/***EP LDRDosGetModName - Retrieves the filename of the specified module.
|
|
*
|
|
*
|
|
* ENTRY hMod module handle
|
|
* cbBuf number of bytes in buffer
|
|
* pchBuf pointer to buffer to receiving module name
|
|
*
|
|
* EXIT NO_ERROR
|
|
* ERROR_MOD_NOT_FOUND
|
|
*/
|
|
|
|
BOOLEAN
|
|
LDRDosGetModName(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
{
|
|
P_LDRGETMODULENAME_MSG a = &m->u.LdrGetModuleName;
|
|
ldrmte_t *pmte;
|
|
ldrsmte_t *psmte;
|
|
|
|
CurrentThread = t;
|
|
|
|
//
|
|
// Verify that a module with the specified handle does exist.
|
|
// The module can belong to any process in the system!
|
|
// (found while running the PM code)
|
|
//
|
|
pmte = ldrFindMTEForHandle((USHORT)a->ModuleHandle);
|
|
if (pmte == NULL) {
|
|
m->ReturnedErrorValue = ERROR_INVALID_HANDLE;
|
|
return(TRUE);
|
|
}
|
|
|
|
psmte = pmte->mte_swapmte;
|
|
if ((USHORT)(psmte->smte_pathlen + 1) > a->ModuleName.MaximumLength) {
|
|
m->ReturnedErrorValue = ERROR_BAD_LENGTH;
|
|
return TRUE;
|
|
}
|
|
memcpy(a->ModuleName.Buffer, (PCHAR)psmte->smte_path+14, psmte->smte_pathlen-14);
|
|
a->ModuleName.Buffer[psmte->smte_pathlen-14] = '\0';
|
|
a->ModuleName.Length = psmte->smte_pathlen-14;
|
|
m->ReturnedErrorValue = NO_ERROR;
|
|
return(TRUE);
|
|
}
|
|
|
|
/***EP ldrGetModName - Retrieves the filename of the specified module.
|
|
*
|
|
*
|
|
* ENTRY hMod module handle
|
|
* cbBuf number of bytes in buffer
|
|
* pchBuf pointer to buffer to receiving module name
|
|
*
|
|
*/
|
|
|
|
BOOLEAN
|
|
ldrGetModName(
|
|
ldrmte_t *inmte,
|
|
USHORT hmod,
|
|
PCHAR buf,
|
|
USHORT bc
|
|
)
|
|
{
|
|
ldrmte_t *pmte;
|
|
ldrsmte_t *psmte;
|
|
|
|
|
|
if (inmte){
|
|
pmte = inmte;
|
|
}
|
|
else {
|
|
pmte = ldrFindMTEForHandle(hmod);
|
|
if (pmte == NULL) {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
psmte = pmte->mte_swapmte;
|
|
|
|
|
|
if (bc <= (psmte->smte_pathlen-14)) {
|
|
return(FALSE);
|
|
}
|
|
memcpy(buf, (PCHAR)psmte->smte_path+14, psmte->smte_pathlen-14 );
|
|
buf[psmte->smte_pathlen-14] = '\0';
|
|
return(TRUE);
|
|
}
|
|
|
|
/***EP LDRDosGetModHandle - Retrieves the handle of a dynamic-link module.
|
|
*
|
|
*
|
|
* ENTRY pchname ASCIIZ name of procedure.
|
|
* paddress Ptr to variable receiving module handle
|
|
*
|
|
* EXIT NO_ERROR
|
|
* ERROR_MOD_NOT_FOUND
|
|
*/
|
|
|
|
BOOLEAN
|
|
LDRDosGetModHandle(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
{
|
|
P_LDRGETMODULEHANDLE_MSG a = &m->u.LdrGetModuleHandle;
|
|
ldrmte_t *pmte_prog; /* Pointer to process MTE */
|
|
ldrmte_t *pmte;
|
|
USHORT class;
|
|
ULONG rc;
|
|
|
|
CurrentThread = t;
|
|
|
|
/*
|
|
* Point to ldrLibPathBuf to contain the environment string
|
|
*/
|
|
strncpy(ldrLibPathBuf, a->LibPathName.Buffer, SizeOfldrLibPathBuf);
|
|
ldrLibPathBuf[SizeOfldrLibPathBuf-1] = '\0';
|
|
|
|
ldrUCaseString(a->ModuleName.Buffer, a->ModuleName.Length);
|
|
|
|
/*
|
|
* Check if module we are loading has any path characters
|
|
*/
|
|
if (strpbrk(a->ModuleName.Buffer, ":\\/.") == NULL)
|
|
class = CLASS_GLOBAL;
|
|
else
|
|
class = CLASS_SPECIFIC;
|
|
|
|
pmte = NULL;
|
|
if (((rc = ldrFindModule(a->ModuleName.Buffer, a->ModuleName.Length,
|
|
class,
|
|
&pmte)) != NO_ERROR) ||
|
|
(pmte == NULL)
|
|
) {
|
|
m->ReturnedErrorValue = ERROR_MOD_NOT_FOUND;
|
|
return(TRUE);
|
|
}
|
|
|
|
//
|
|
// Verify that the module belongs to the current process
|
|
//
|
|
pmte_prog = (ldrmte_t *)t->Process->ProcessMTE;
|
|
ASSERT(pmte_prog != NULL);
|
|
|
|
if (!ModuleLoadedForProcess(pmte_prog, pmte)) {
|
|
m->ReturnedErrorValue = ERROR_INVALID_HANDLE;
|
|
return(TRUE);
|
|
}
|
|
|
|
a->ModuleHandle = (ULONG) pmte->mte_handle;
|
|
m->ReturnedErrorValue = NO_ERROR;
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/***EP LDRDosFreeModule - Free a dynamic-link module.
|
|
*
|
|
*
|
|
* ENTRY hMod handle of module to free
|
|
*
|
|
* EXIT NO_ERROR
|
|
* ERROR_INVALID_HANDLE
|
|
*/
|
|
|
|
BOOLEAN
|
|
LDRDosFreeModule(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
{
|
|
P_LDRFREEMODULE_MSG a = &m->u.LdrFreeModule;
|
|
ldrmte_t *pmte_prog; /* Pointer to process MTE */
|
|
ldrmte_t *pmte;
|
|
ldrdld_t *pdld;
|
|
ldrdld_t *prev_pdld;
|
|
|
|
CurrentThread = t;
|
|
|
|
//
|
|
// Set the fForceUnmap flag to TRUE so that ldrUnloadTagedModules()
|
|
// does unmap the app's freed segments from the app's address space.
|
|
//
|
|
fForceUnmap = TRUE;
|
|
|
|
//
|
|
// Verify that the module belongs to the current process
|
|
//
|
|
pmte_prog = (ldrmte_t *)t->Process->ProcessMTE;
|
|
ASSERT(pmte_prog != NULL);
|
|
|
|
#if DBG
|
|
IF_OL2_DEBUG ( TRACE ) {
|
|
DbgPrint("OS2LDR: DosFreeModule was called\n");
|
|
}
|
|
#endif
|
|
|
|
pdld = pmte_prog->mte_dldchain;
|
|
prev_pdld = (ldrdld_t *)&pmte_prog->mte_dldchain;
|
|
while (pdld != NULL) {
|
|
pmte = pdld->dld_mteptr;
|
|
if ((pdld->Cookie == (ULONG)CurrentThread->Process) &&
|
|
(pmte->mte_handle == (USHORT)a->ModuleHandle)) {
|
|
#if DBG
|
|
IF_OL2_DEBUG ( TRACE ) {
|
|
DbgPrint("OS2LDR: Going to free module %.*s\n",
|
|
*(PCHAR)pmte->mte_modname,
|
|
(PCHAR)pmte->mte_modname + 1
|
|
);
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
prev_pdld = pdld;
|
|
pdld = pdld->dld_next;
|
|
}
|
|
if (pdld == NULL) {
|
|
m->ReturnedErrorValue = ERROR_INVALID_HANDLE;
|
|
return(TRUE);
|
|
}
|
|
|
|
pdld->dld_usecnt--;
|
|
if (pdld->dld_usecnt != 0) {
|
|
m->ReturnedErrorValue = NO_ERROR;
|
|
return(TRUE);
|
|
}
|
|
|
|
prev_pdld->dld_next = pdld->dld_next;
|
|
RtlFreeHeap(LDRNEHeap, 0, pdld);
|
|
|
|
//
|
|
// Clear the INGRAPH flag of all modules so that we
|
|
// know that this module has already been processed
|
|
//
|
|
ldrClearAllMteFlag(INGRAPH | USED);
|
|
|
|
//
|
|
// Tag all referenced modules with the USE flag
|
|
// so that they are not discarded
|
|
//
|
|
ldrTagModuleTree_USED(pmte_prog);
|
|
for (pdld = pmte_prog->mte_dldchain; pdld != NULL; pdld = pdld->dld_next) {
|
|
if (pdld->Cookie == (ULONG)CurrentThread->Process) {
|
|
ldrTagModuleTree_USED(pdld->dld_mteptr);
|
|
}
|
|
}
|
|
ldrClearAllMteFlag(INGRAPH);
|
|
|
|
//
|
|
// Tag all referenced modules with the INGRAPH flag
|
|
// The tagged modules will be then unloaded
|
|
//
|
|
ldrTagModuleTree(pmte);
|
|
|
|
//
|
|
// Decrement the usecnt of the marked modules
|
|
//
|
|
pmte = mte_h;
|
|
while (pmte != NULL) {
|
|
if ((pmte->mte_mflags & INGRAPH) != 0) {
|
|
pmte->mte_usecnt--;
|
|
}
|
|
pmte = pmte->mte_link;
|
|
}
|
|
|
|
ldrUnloadTagedModules(t->Process);
|
|
|
|
#if DBG
|
|
IF_OL2_DEBUG ( MTE ) {
|
|
DbgPrint("\nDumping segmenst after DosFreeModule() processing\n");
|
|
ldrDisplaySegmentTable();
|
|
}
|
|
#endif
|
|
|
|
m->ReturnedErrorValue = NO_ERROR;
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/***EP LDRGetResource - Retrieves the specified resource.
|
|
*
|
|
*
|
|
* ENTRY hmod module handle.
|
|
* idType resource type identifier.
|
|
* idName resource name identifier
|
|
* psel pointer to variable for resource selector
|
|
*
|
|
* EXIT NO_ERROR
|
|
* ERROR_CANT_FIND_RESOURCE
|
|
* ERROR_INVALID_MODULE
|
|
* ERROR_INVALID_SELECTOR
|
|
*/
|
|
|
|
BOOLEAN
|
|
LDRDosGetResource(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
{
|
|
P_LDRGETRESOURCE_MSG a = &m->u.LdrGetResource;
|
|
ldrmte_t *pmte;
|
|
ldrsmte_t *psmte;
|
|
ldrste_t *pste;
|
|
ULONG lrsrc;
|
|
ULONG rc;
|
|
ULONG ModHandle;
|
|
ULONG ViewSize;
|
|
ULONG Protect;
|
|
PVOID MemoryAddress;
|
|
NTSTATUS Status;
|
|
ULONG RegionSize;
|
|
HANDLE SectionHandle;
|
|
|
|
CurrentThread = t;
|
|
|
|
//
|
|
// Get resources from first loaded program
|
|
//
|
|
if (a->ModuleHandle == 0) {
|
|
pmte = (ldrmte_t *)t->Process->ProcessMTE;
|
|
ModHandle = pmte->mte_handle;
|
|
}
|
|
else {
|
|
ModHandle = a->ModuleHandle;
|
|
if ((pmte = ldrFindMTEForHandle((USHORT)ModHandle)) == NULL) {
|
|
m->ReturnedErrorValue = ERROR_INVALID_HANDLE;
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
psmte = pmte->mte_swapmte;
|
|
|
|
rc = ERROR_INVALID_PARAMETER;
|
|
|
|
for (lrsrc = 0; lrsrc < psmte->smte_rsrccnt; lrsrc++) {
|
|
|
|
if ((((ldrrsrc16_t *)psmte->smte_rsrctab)[lrsrc].ldrrsrc16_type
|
|
== (USHORT)a->ResourceType) &&
|
|
(((ldrrsrc16_t *)psmte->smte_rsrctab)[lrsrc].ldrrsrc16_name
|
|
== (USHORT)a->ResourceName) ) {
|
|
rc = NO_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rc == NO_ERROR) {
|
|
pste = ldrNumToSte(pmte,
|
|
(psmte->smte_objcnt - psmte->smte_rsrccnt + lrsrc + 1));
|
|
MemoryAddress = SELTOFLAT(pste->ste_selector);
|
|
SectionHandle = (HANDLE)pste->ste_seghdl;
|
|
//
|
|
// Map the resource into the client address space
|
|
// Any change in determinig the Protect value should be
|
|
// done in ldrste.c ldrAllocSegment() too.
|
|
//
|
|
if ((pste->ste_flags & STE_DATA) == 0) {
|
|
// This is a code segment
|
|
if ((pste->ste_flags & STE_ERONLY) != 0) {
|
|
// This is an execute only segment
|
|
Protect = PAGE_EXECUTE;
|
|
}
|
|
else {
|
|
Protect = PAGE_EXECUTE_READ;
|
|
}
|
|
}
|
|
else {
|
|
// This is a data segment
|
|
if ((pste->ste_flags & STE_SHARED) != 0) {
|
|
// This is a shared data segment
|
|
if ((pste->ste_flags & STE_ERONLY) != 0) {
|
|
// This is a read only segment
|
|
Protect = PAGE_READONLY;
|
|
}
|
|
else {
|
|
// This is a read/write segment
|
|
Protect = PAGE_READWRITE;
|
|
}
|
|
}
|
|
else {
|
|
// This is a non shared data segment
|
|
if ((pste->ste_flags & STE_ERONLY) != 0) {
|
|
// This is a read only segment
|
|
Protect = PAGE_READONLY;
|
|
}
|
|
else {
|
|
// This is a sizeable read/write segment
|
|
Protect = PAGE_EXECUTE_WRITECOPY;
|
|
}
|
|
}
|
|
}
|
|
|
|
a->ResourceSel = pste->ste_selector | 7;
|
|
a->ResourceAddr = (ULONG)((pste->ste_selector << 16) & 0xffff0000);
|
|
a->NumberOfSegments = 0;
|
|
// This should be performed in a loop for huge resources (longer then a
|
|
// single segment)
|
|
for (;
|
|
((((ldrrsrc16_t *)psmte->smte_rsrctab)[lrsrc].ldrrsrc16_type
|
|
== (USHORT)a->ResourceType) &&
|
|
(((ldrrsrc16_t *)psmte->smte_rsrctab)[lrsrc].ldrrsrc16_name
|
|
== (USHORT)a->ResourceName) )
|
|
; lrsrc++) {
|
|
|
|
(a->NumberOfSegments)++;
|
|
pste = ldrNumToSte(pmte,
|
|
(psmte->smte_objcnt - psmte->smte_rsrccnt + lrsrc + 1));
|
|
MemoryAddress = SELTOFLAT(pste->ste_selector);
|
|
SectionHandle = (HANDLE)pste->ste_seghdl;
|
|
|
|
ViewSize = 0;
|
|
RegionSize = pste->ste_minsiz;
|
|
if (RegionSize == 0) {
|
|
RegionSize = _64K;
|
|
}
|
|
|
|
if (Protect == PAGE_EXECUTE_WRITECOPY) {
|
|
ViewSize = RegionSize;
|
|
}
|
|
|
|
Status = NtMapViewOfSection(SectionHandle,
|
|
CurrentThread->Process->ProcessHandle,
|
|
&MemoryAddress,
|
|
1,
|
|
RegionSize,
|
|
NULL,
|
|
&ViewSize,
|
|
ViewUnmap,
|
|
0,
|
|
Protect
|
|
);
|
|
//
|
|
// Don't check for error code as this section may be multiple
|
|
// mapped because DosFreeResource() does not unmap it.
|
|
//
|
|
if (!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
// DbgPrint("OS2LDR: ldrAllocSegment - Can't map section to client process %x at addr=%x, Status=%x\n",
|
|
// CurrentThread->Process->ProcessHandle, MemoryAddress, Status);
|
|
#endif
|
|
}
|
|
|
|
ldrSetDescInfo(pste->ste_selector, (ULONG)MemoryAddress,
|
|
pste->ste_flags, pste->ste_minsiz);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m->ReturnedErrorValue = rc;
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/***EP LDRDosFreeResource - Free a specified resource.
|
|
*
|
|
*
|
|
* ENTRY psel pointer to resource
|
|
*
|
|
* EXIT NO_ERROR
|
|
* ERROR_INVALID_SELECTOR
|
|
*/
|
|
|
|
BOOLEAN
|
|
LDRDosFreeResource(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
{
|
|
P_LDRFREERESOURCE_MSG a = &m->u.LdrFreeResource;
|
|
|
|
CurrentThread = t;
|
|
|
|
ldrClearAllMteFlag(INGRAPH | USED);
|
|
|
|
m->ReturnedErrorValue = NO_ERROR;
|
|
return(TRUE);
|
|
}
|
|
|
|
#if PMNT
|
|
BOOLEAN
|
|
LDRIdentifyCodeSelector(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
{
|
|
P_LDRIDENTIFYCODESELECTOR_MSG a = &m->u.LdrIdentifyCodeSelector;
|
|
ldrmte_t *pmte_prog; /* Pointer to process MTE */
|
|
ldrmte_t *pmte;
|
|
ldrdld_t *pdld;
|
|
ldrsmte_t *psmte;
|
|
ldrste_t *pste;
|
|
ULONG i;
|
|
|
|
CurrentThread = t;
|
|
|
|
//
|
|
// Verify that the module belongs to the current process
|
|
//
|
|
pmte_prog = (ldrmte_t *)t->Process->ProcessMTE;
|
|
ASSERT(pmte_prog != NULL);
|
|
|
|
//
|
|
// Clear the INGRAPH flag of all modules so that we
|
|
// know that this module has already been processed
|
|
//
|
|
ldrClearAllMteFlag(INGRAPH | USED);
|
|
|
|
//
|
|
// Tag all referenced modules with the INGRAPH flag
|
|
// The tagged modules will then be scanned for the desired selector
|
|
//
|
|
ldrTagModuleTree(pmte_prog);
|
|
for (pdld = pmte_prog->mte_dldchain; pdld != NULL; pdld = pdld->dld_next) {
|
|
ldrTagModuleTree(pdld->dld_mteptr);
|
|
}
|
|
|
|
//
|
|
// Scan the marked modules for one containing the resized selector
|
|
//
|
|
pmte = mte_h;
|
|
while (pmte != NULL)
|
|
{
|
|
if ((pmte->mte_mflags & INGRAPH) != 0)
|
|
{
|
|
psmte = pmte->mte_swapmte;
|
|
pste = (ldrste_t *)psmte->smte_objtab;
|
|
|
|
for (i = 1; i <= psmte->smte_objcnt; i++, pste++)
|
|
{
|
|
if (((ULONG)(pste->ste_selector | 7) == a->sel))
|
|
{
|
|
a->segNum = (USHORT)i;
|
|
a->mte = pmte->mte_handle;
|
|
memcpy( a->ModName.Buffer,
|
|
(PCHAR)psmte->smte_path+14,
|
|
psmte->smte_pathlen-14);
|
|
a->ModName.Buffer[psmte->smte_pathlen-14] = '\0';
|
|
|
|
return(TRUE);
|
|
}
|
|
}
|
|
}
|
|
pmte = pmte->mte_link;
|
|
}
|
|
|
|
// Selector not found. Return default values
|
|
a->segNum = 1;
|
|
a->mte = 0;
|
|
strcpy( a->ModName.Buffer, "UNKNOWN");
|
|
|
|
m->ReturnedErrorValue = NO_ERROR;
|
|
return(TRUE);
|
|
}
|
|
#endif // PMNT
|
|
|
|
/***EP LDRDosQAppType - return file's exe type
|
|
*
|
|
* ENTRY pszModName - pointer to ASCII module name
|
|
* pulType - pointer to put type
|
|
*
|
|
* EXIT int - return code (NO_ERROR if successful)
|
|
*/
|
|
|
|
BOOLEAN
|
|
LDRDosQAppType(
|
|
IN POS2_THREAD t,
|
|
IN POS2_API_MSG m
|
|
)
|
|
{
|
|
P_LDRQAPPTYPE_MSG a = &m->u.LdrQAppType;
|
|
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
LARGE_INTEGER ByteOffset;
|
|
ULONG ulNewHdrOff; /* Offset hdr offset */
|
|
ULONG ulCopied; /* Number of bytes copied */
|
|
ULONG ulNeeded; /* Number of bytes needed */
|
|
ULONG usoff; /* Offset to new exe header */
|
|
struct e32_exe *pe32;
|
|
struct e32_exe *pe32temp;
|
|
ldrlv_t lv; /* local variables */
|
|
PCHAR ptmp;
|
|
int rc;
|
|
NTSTATUS Status;
|
|
|
|
#define NOTSPECIFIED 0x0000
|
|
#define NOTWINDOCOMPAT 0x0001
|
|
#define WINDOWCOMPAT 0x0002
|
|
#define WINDOWAPI 0x0003
|
|
#define BOUND 0x0008
|
|
#define DYNAMICLINK 0x0010
|
|
#define DOSFORMAT 0x0020
|
|
|
|
CurrentThread = t;
|
|
|
|
/*
|
|
* Point to ldrLibPathBuf to contain the environment string
|
|
*/
|
|
strncpy(ldrLibPathBuf, a->PathName.Buffer, SizeOfldrLibPathBuf);
|
|
ldrLibPathBuf[SizeOfldrLibPathBuf-1] = '\0';
|
|
|
|
/*
|
|
* Check if the App we are loading has any path characters
|
|
*/
|
|
if (strpbrk(a->AppName.Buffer, ":\\/") == NULL) {
|
|
lv.lv_class = CLASS_GLOBAL;
|
|
}
|
|
else {
|
|
lv.lv_class = CLASS_SPECIFIC;
|
|
}
|
|
|
|
if ((rc = ldrOpenPath(a->AppName.Buffer,
|
|
(USHORT)a->AppName.Length,
|
|
&lv,
|
|
NULL)) != NO_ERROR) {
|
|
//
|
|
// If file was not found, check if the file name has no extension.
|
|
// If it does not have, try again with the extension .EXE
|
|
//
|
|
|
|
UCHAR NewPathWithExt[MAXPATHLEN];
|
|
|
|
memcpy(NewPathWithExt, a->AppName.Buffer, a->AppName.Length);
|
|
NewPathWithExt[a->AppName.Length] = '\0';
|
|
ptmp = strrchr(NewPathWithExt, '\\');
|
|
if (ptmp == NULL) {
|
|
ptmp = strrchr(NewPathWithExt, '/');
|
|
if (ptmp == NULL) {
|
|
ptmp = strrchr(NewPathWithExt, ':');
|
|
if (ptmp == NULL) {
|
|
ptmp = NewPathWithExt;
|
|
}
|
|
}
|
|
}
|
|
ptmp = strchr(ptmp, '.');
|
|
if (ptmp != NULL) {
|
|
//
|
|
// The file has an extension. So, the error returned
|
|
// previously by ldrOpenPath() is valid
|
|
//
|
|
m->ReturnedErrorValue = rc;
|
|
return(TRUE);
|
|
}
|
|
strcpy(&NewPathWithExt[a->AppName.Length], ".EXE");
|
|
rc = ldrOpenPath(NewPathWithExt,
|
|
(USHORT)(a->AppName.Length + 4),
|
|
&lv,
|
|
NULL);
|
|
}
|
|
if (rc != NO_ERROR) {
|
|
m->ReturnedErrorValue = rc;
|
|
return(TRUE);
|
|
}
|
|
|
|
pe32 = (struct e32_exe *) pheaderbuf;
|
|
|
|
/*
|
|
* Start read at beginning of file
|
|
*/
|
|
ByteOffset.LowPart = 0;
|
|
ByteOffset.HighPart = 0;
|
|
|
|
if ((Status = NtReadFile( lv.lv_sfn,
|
|
0,
|
|
0,
|
|
0,
|
|
&IoStatusBlock,
|
|
pe32,
|
|
512,
|
|
&ByteOffset,
|
|
0 )) != STATUS_SUCCESS) {
|
|
NtClose(lv.lv_sfn);
|
|
m->ReturnedErrorValue = ERROR_FILE_NOT_FOUND;
|
|
return(TRUE);
|
|
}
|
|
|
|
/*
|
|
* validate old (MZ) signature in header
|
|
*/
|
|
if (((struct exe_hdr *) pe32)->e_magic != EMAGIC) {
|
|
NtClose(lv.lv_sfn);
|
|
m->ReturnedErrorValue = ERROR_INVALID_EXE_SIGNATURE;
|
|
return(TRUE);
|
|
}
|
|
|
|
usoff = ((struct exe_hdr *) pe32)->e_lfarlc;
|
|
|
|
/*
|
|
* get pointer to (NE) or (LE) exe header
|
|
*/
|
|
ulNewHdrOff =
|
|
lv.lv_new_exe_off = ((struct exe_hdr *) pe32)->e_lfanew;
|
|
|
|
/*
|
|
* check if we read at least up to the (NE) or (LE) header
|
|
*/
|
|
if (ulNewHdrOff < IoStatusBlock.Information) {
|
|
|
|
/*
|
|
* assume we are reading a 32-bit module
|
|
*/
|
|
ulNeeded = sizeof(struct e32_exe);
|
|
pe32temp = (struct e32_exe *) ((ULONG) pe32 + ulNewHdrOff);
|
|
|
|
if ((ulNewHdrOff < (IoStatusBlock.Information -
|
|
sizeof(pe32->e32_magic))) &&
|
|
(*(short *) (pe32temp->e32_magic) == NEMAGIC))
|
|
ulNeeded = sizeof(struct new_exe);
|
|
|
|
ulCopied = min(IoStatusBlock.Information - ulNewHdrOff, ulNeeded);
|
|
|
|
memcpy(pe32, (PVOID) ((ULONG) pe32 + ulNewHdrOff), ulCopied);
|
|
|
|
if (ulNeeded -= ulCopied) {
|
|
if ((Status = NtReadFile( lv.lv_sfn,
|
|
0,
|
|
0,
|
|
0,
|
|
&IoStatusBlock,
|
|
(PCHAR) pe32 + ulCopied,
|
|
ulNeeded,
|
|
&ByteOffset,
|
|
0 )) != STATUS_SUCCESS) {
|
|
NtClose(lv.lv_sfn);
|
|
m->ReturnedErrorValue = ERROR_FILE_NOT_FOUND;
|
|
return(TRUE);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
/*
|
|
* read in new header to size of 32-bit mte plus a ote entry
|
|
*/
|
|
ByteOffset.LowPart = (ULONG)((struct exe_hdr *)pe32)->e_lfanew;
|
|
ByteOffset.HighPart = 0;
|
|
|
|
if ((Status = NtReadFile( lv.lv_sfn,
|
|
0,
|
|
0,
|
|
0,
|
|
&IoStatusBlock,
|
|
(PCHAR) pe32,
|
|
sizeof(struct e32_exe)+sizeof(ldrote_t),
|
|
&ByteOffset,
|
|
0 )) != STATUS_SUCCESS) {
|
|
NtClose(lv.lv_sfn);
|
|
m->ReturnedErrorValue = ERROR_FILE_NOT_FOUND;
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
Status = NtClose(lv.lv_sfn);
|
|
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrint(("OS2SRV: Failed to close the file opened for DosQApptype(), Status=%x\n", Status));
|
|
}
|
|
#endif
|
|
|
|
/* Verify that this is a protect-mode exe. (Check this before
|
|
* checking MTE signature for 1.2 error code compatability.)
|
|
*/
|
|
if (usoff != 0x40) {
|
|
a->AppType = DOSFORMAT;
|
|
m->ReturnedErrorValue = NO_ERROR;
|
|
return(TRUE);
|
|
}
|
|
|
|
/*
|
|
* validate as 16-bit signature or 32-bit signature
|
|
*/
|
|
if (!(*(short *) (pe32->e32_magic) == NEMAGIC)) {
|
|
m->ReturnedErrorValue = ERROR_INVALID_EXE_SIGNATURE;
|
|
return(TRUE);
|
|
}
|
|
|
|
a->AppType = 0;
|
|
if (((struct new_exe *)pe32)->ne_flags & 0x800) {
|
|
a->AppType |= BOUND;
|
|
}
|
|
if (((struct new_exe *)pe32)->ne_flags & 0x8000) {
|
|
a->AppType |= DYNAMICLINK;
|
|
}
|
|
if ((((struct new_exe *)pe32)->ne_flags & 0x700) == 0x100) {
|
|
a->AppType |= NOTWINDOCOMPAT;
|
|
}
|
|
if ((((struct new_exe *)pe32)->ne_flags & 0x700) == 0x200) {
|
|
a->AppType |= WINDOWCOMPAT;
|
|
}
|
|
if ((((struct new_exe *)pe32)->ne_flags & 0x700) == 0x300) {
|
|
a->AppType |= WINDOWAPI;
|
|
}
|
|
|
|
m->ReturnedErrorValue = NO_ERROR;
|
|
return(TRUE);
|
|
}
|
|
|
|
BOOLEAN
|
|
LDRModifySizeOfSharedSegment(
|
|
IN POS2_THREAD t,
|
|
IN ULONG Sel,
|
|
IN ULONG NewLimit
|
|
)
|
|
{
|
|
ldrmte_t *pmte_prog; /* Pointer to process MTE */
|
|
ldrmte_t *pmte;
|
|
ldrdld_t *pdld;
|
|
ldrsmte_t *psmte;
|
|
ldrste_t *pste;
|
|
ULONG i;
|
|
|
|
CurrentThread = t;
|
|
|
|
//
|
|
// Verify that the module belongs to the current process
|
|
//
|
|
pmte_prog = (ldrmte_t *)t->Process->ProcessMTE;
|
|
ASSERT(pmte_prog != NULL);
|
|
|
|
#if DBG
|
|
IF_OL2_DEBUG ( TRACE ) {
|
|
DbgPrint("OS2LDR: LDRModifySizeOfSharedSegment was called\n");
|
|
}
|
|
#endif
|
|
//
|
|
// Clear the INGRAPH flag of all modules so that we
|
|
// know that this module has already been processed
|
|
//
|
|
ldrClearAllMteFlag(INGRAPH | USED);
|
|
|
|
//
|
|
// Tag all referenced modules with the INGRAPH flag
|
|
// The tagged modules will then be scanned for the desired selector
|
|
//
|
|
ldrTagModuleTree(pmte_prog);
|
|
for (pdld = pmte_prog->mte_dldchain; pdld != NULL; pdld = pdld->dld_next) {
|
|
ldrTagModuleTree(pdld->dld_mteptr);
|
|
}
|
|
|
|
//
|
|
// Scan the marked modules for one containing the resized selector
|
|
//
|
|
pmte = mte_h;
|
|
while (pmte != NULL) {
|
|
if ((pmte->mte_mflags & INGRAPH) != 0) {
|
|
psmte = pmte->mte_swapmte;
|
|
pste = (ldrste_t *)psmte->smte_objtab;
|
|
|
|
for (i = 1; i <= psmte->smte_objcnt; i++, pste++) {
|
|
if (((ULONG)(pste->ste_selector | 7) == Sel) &&
|
|
((pste->ste_flags & STE_SHARED) != 0)
|
|
) {
|
|
pste->ste_minsiz = (ushort_t)(NewLimit + 1);
|
|
return(TRUE);
|
|
}
|
|
}
|
|
}
|
|
pmte = pmte->mte_link;
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
VOID
|
|
ldrReturnProgramAndLibMTE(
|
|
IN POS2_PROCESS Process,
|
|
OUT USHORT *ProgramMTE,
|
|
OUT USHORT *LibMTE,
|
|
OUT USHORT *Cmd
|
|
)
|
|
{
|
|
LinkMTE *pMte;
|
|
|
|
//
|
|
// Get the program MTE.
|
|
//
|
|
pMte = ((LinkMTE *)Process->LinkMte)->NextMTE;
|
|
|
|
*ProgramMTE = pMte->MTE;
|
|
*LibMTE = 0;
|
|
*Cmd= TRC_C_SUC_ret;
|
|
|
|
while ((pMte != NULL) && (!(pMte->NeedToTransfer))) {
|
|
pMte = pMte->NextMTE;
|
|
}
|
|
if (pMte != NULL) {
|
|
*LibMTE = pMte->MTE;
|
|
*Cmd = (USHORT)TRC_C_LIB_ret;
|
|
pMte->NeedToTransfer = FALSE;
|
|
((LinkMTE *)Process->LinkMte)->NeedToTransfer--;
|
|
}
|
|
|
|
return;
|
|
}
|