868 lines
28 KiB
C
868 lines
28 KiB
C
/*
|
|
* NE segment loading
|
|
*
|
|
* Copyright 1993 Robert J. Amstadt
|
|
* Copyright 1995 Alexandre Julliard
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include "wine/winbase16.h"
|
|
#include "neexe.h"
|
|
#include "global.h"
|
|
#include "task.h"
|
|
#include "selectors.h"
|
|
#include "callback.h"
|
|
#include "file.h"
|
|
#include "module.h"
|
|
#include "stackframe.h"
|
|
#include "debugtools.h"
|
|
#include "xmalloc.h"
|
|
#include "toolhelp.h"
|
|
|
|
DECLARE_DEBUG_CHANNEL(dll)
|
|
DECLARE_DEBUG_CHANNEL(fixup)
|
|
DECLARE_DEBUG_CHANNEL(module)
|
|
DECLARE_DEBUG_CHANNEL(segment)
|
|
|
|
#define SEL(x) ((x)|1)
|
|
|
|
/***********************************************************************
|
|
* NE_GetRelocAddrName
|
|
*/
|
|
static const char *NE_GetRelocAddrName( BYTE addr_type, int additive )
|
|
{
|
|
switch(addr_type & 0x7f)
|
|
{
|
|
case NE_RADDR_LOWBYTE: return additive ? "BYTE add" : "BYTE";
|
|
case NE_RADDR_OFFSET16: return additive ? "OFFSET16 add" : "OFFSET16";
|
|
case NE_RADDR_POINTER32: return additive ? "POINTER32 add" : "POINTER32";
|
|
case NE_RADDR_SELECTOR: return additive ? "SELECTOR add" : "SELECTOR";
|
|
case NE_RADDR_POINTER48: return additive ? "POINTER48 add" : "POINTER48";
|
|
case NE_RADDR_OFFSET32: return additive ? "OFFSET32 add" : "OFFSET32";
|
|
}
|
|
return "???";
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* NE_LoadSegment
|
|
*/
|
|
BOOL NE_LoadSegment( NE_MODULE *pModule, WORD segnum )
|
|
{
|
|
SEGTABLEENTRY *pSegTable, *pSeg;
|
|
WORD *pModuleTable;
|
|
WORD count, i, offset, next_offset;
|
|
HMODULE16 module;
|
|
FARPROC16 address = 0;
|
|
HFILE hf;
|
|
DWORD res;
|
|
struct relocation_entry_s *rep, *reloc_entries;
|
|
BYTE *func_name;
|
|
int size;
|
|
char* mem;
|
|
|
|
char buffer[256];
|
|
int ordinal, additive;
|
|
unsigned short *sp;
|
|
|
|
pSegTable = NE_SEG_TABLE( pModule );
|
|
pSeg = pSegTable + segnum - 1;
|
|
|
|
if (pSeg->flags & NE_SEGFLAGS_LOADED) /* already loaded ? */
|
|
return TRUE;
|
|
|
|
if (!pSeg->filepos) return TRUE; /* No file image, just return */
|
|
|
|
pModuleTable = NE_MODULE_TABLE( pModule );
|
|
|
|
hf = NE_OpenFile( pModule );
|
|
TRACE_(module)("Loading segment %d, hSeg=%04x, flags=%04x\n",
|
|
segnum, pSeg->hSeg, pSeg->flags );
|
|
SetFilePointer( hf, pSeg->filepos << pModule->alignment, NULL, SEEK_SET );
|
|
if (pSeg->size) size = pSeg->size;
|
|
else size = pSeg->minsize ? pSeg->minsize : 0x10000;
|
|
mem = GlobalLock16(pSeg->hSeg);
|
|
if (pModule->flags & NE_FFLAGS_SELFLOAD && segnum > 1)
|
|
{
|
|
/* Implement self-loading segments */
|
|
SELFLOADHEADER *selfloadheader;
|
|
DWORD oldstack;
|
|
WORD old_hSeg, new_hSeg;
|
|
HFILE hFile32;
|
|
HFILE16 hFile16;
|
|
|
|
selfloadheader = (SELFLOADHEADER *)
|
|
PTR_SEG_OFF_TO_LIN(SEL(pSegTable->hSeg),0);
|
|
oldstack = NtCurrentTeb()->cur_stack;
|
|
old_hSeg = pSeg->hSeg;
|
|
NtCurrentTeb()->cur_stack = PTR_SEG_OFF_TO_SEGPTR(pModule->self_loading_sel,
|
|
0xff00 - sizeof(STACK16FRAME));
|
|
|
|
TRACE_(dll)("CallLoadAppSegProc(hmodule=0x%04x,hf=0x%04x,segnum=%d\n",
|
|
pModule->self,hf,segnum );
|
|
DuplicateHandle( GetCurrentProcess(), hf, GetCurrentProcess(), &hFile32,
|
|
0, FALSE, DUPLICATE_SAME_ACCESS );
|
|
hFile16 = FILE_AllocDosHandle( hFile32 );
|
|
new_hSeg = Callbacks->CallLoadAppSegProc(selfloadheader->LoadAppSeg,
|
|
pModule->self, hFile16,
|
|
segnum );
|
|
TRACE_(dll)("Ret CallLoadAppSegProc: hSeg = 0x%04x\n",new_hSeg);
|
|
_lclose16( hFile16 );
|
|
if (SEL(new_hSeg) != SEL(old_hSeg)) {
|
|
/* Self loaders like creating their own selectors;
|
|
* they love asking for trouble to Wine developers
|
|
*/
|
|
if (segnum == pModule->dgroup) {
|
|
memcpy(PTR_SEG_OFF_TO_LIN(SEL(old_hSeg),0),
|
|
PTR_SEG_OFF_TO_LIN(SEL(new_hSeg),0),
|
|
pSeg->minsize ? pSeg->minsize : 0x10000);
|
|
FreeSelector16(SEL(new_hSeg));
|
|
pSeg->hSeg = old_hSeg;
|
|
TRACE_(module)("New hSeg allocated for dgroup segment:Old=%d,New=%d\n",
|
|
old_hSeg, new_hSeg);
|
|
} else {
|
|
FreeSelector16(SEL(pSeg->hSeg));
|
|
pSeg->hSeg = new_hSeg;
|
|
}
|
|
}
|
|
|
|
NtCurrentTeb()->cur_stack = oldstack;
|
|
}
|
|
else if (!(pSeg->flags & NE_SEGFLAGS_ITERATED))
|
|
ReadFile(hf, mem, size, &res, NULL);
|
|
else {
|
|
/*
|
|
The following bit of code for "iterated segments" was written without
|
|
any documentation on the format of these segments. It seems to work,
|
|
but may be missing something. If you have any doc please either send
|
|
it to me or fix the code yourself. gfm@werple.mira.net.au
|
|
*/
|
|
char* buff = xmalloc(size);
|
|
char* curr = buff;
|
|
ReadFile(hf, buff, size, &res, NULL);
|
|
while(curr < buff + size) {
|
|
unsigned int rept = *((short*) curr)++;
|
|
unsigned int len = *((short*) curr)++;
|
|
for(; rept > 0; rept--) {
|
|
char* bytes = curr;
|
|
unsigned int byte;
|
|
for(byte = 0; byte < len; byte++)
|
|
*mem++ = *bytes++;
|
|
}
|
|
curr += len;
|
|
}
|
|
free(buff);
|
|
}
|
|
|
|
pSeg->flags |= NE_SEGFLAGS_LOADED;
|
|
if (!(pSeg->flags & NE_SEGFLAGS_RELOC_DATA))
|
|
return TRUE; /* No relocation data, we are done */
|
|
|
|
ReadFile(hf, &count, sizeof(count), &res, NULL);
|
|
if (!count) return TRUE;
|
|
|
|
TRACE_(fixup)("Fixups for %.*s, segment %d, hSeg %04x\n",
|
|
*((BYTE *)pModule + pModule->name_table),
|
|
(char *)pModule + pModule->name_table + 1,
|
|
segnum, pSeg->hSeg );
|
|
TRACE_(segment)("Fixups for %.*s, segment %d, hSeg %04x\n",
|
|
*((BYTE *)pModule + pModule->name_table),
|
|
(char *)pModule + pModule->name_table + 1,
|
|
segnum, pSeg->hSeg );
|
|
|
|
reloc_entries = (struct relocation_entry_s *)xmalloc(count * sizeof(struct relocation_entry_s));
|
|
if (!ReadFile( hf, reloc_entries, count * sizeof(struct relocation_entry_s), &res, NULL) ||
|
|
(res != count * sizeof(struct relocation_entry_s)))
|
|
{
|
|
WARN_(fixup)("Unable to read relocation information\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Go through the relocation table one entry at a time.
|
|
*/
|
|
rep = reloc_entries;
|
|
for (i = 0; i < count; i++, rep++)
|
|
{
|
|
/*
|
|
* Get the target address corresponding to this entry.
|
|
*/
|
|
|
|
/* If additive, there is no target chain list. Instead, add source
|
|
and target */
|
|
additive = rep->relocation_type & NE_RELFLAG_ADDITIVE;
|
|
rep->relocation_type &= 0x3;
|
|
|
|
switch (rep->relocation_type)
|
|
{
|
|
case NE_RELTYPE_ORDINAL:
|
|
module = pModuleTable[rep->target1-1];
|
|
ordinal = rep->target2;
|
|
address = NE_GetEntryPoint( module, ordinal );
|
|
if (!address)
|
|
{
|
|
NE_MODULE *pTarget = NE_GetPtr( module );
|
|
if (!pTarget)
|
|
WARN_(module)("Module not found: %04x, reference %d of module %*.*s\n",
|
|
module, rep->target1,
|
|
*((BYTE *)pModule + pModule->name_table),
|
|
*((BYTE *)pModule + pModule->name_table),
|
|
(char *)pModule + pModule->name_table + 1 );
|
|
else
|
|
{
|
|
ERR_(fixup)("No implementation for %.*s.%d, setting to 0xdeadbeef\n",
|
|
*((BYTE *)pTarget + pTarget->name_table),
|
|
(char *)pTarget + pTarget->name_table + 1,
|
|
ordinal );
|
|
address = (FARPROC16)0xdeadbeef;
|
|
}
|
|
}
|
|
if (TRACE_ON(fixup))
|
|
{
|
|
NE_MODULE *pTarget = NE_GetPtr( module );
|
|
TRACE_(fixup)("%d: %.*s.%d=%04x:%04x %s\n", i + 1,
|
|
*((BYTE *)pTarget + pTarget->name_table),
|
|
(char *)pTarget + pTarget->name_table + 1,
|
|
ordinal, HIWORD(address), LOWORD(address),
|
|
NE_GetRelocAddrName( rep->address_type, additive ) );
|
|
}
|
|
break;
|
|
|
|
case NE_RELTYPE_NAME:
|
|
module = pModuleTable[rep->target1-1];
|
|
func_name = (char *)pModule + pModule->import_table + rep->target2;
|
|
memcpy( buffer, func_name+1, *func_name );
|
|
buffer[*func_name] = '\0';
|
|
func_name = buffer;
|
|
ordinal = NE_GetOrdinal( module, func_name );
|
|
address = NE_GetEntryPoint( module, ordinal );
|
|
|
|
if (ERR_ON(fixup) && !address)
|
|
{
|
|
NE_MODULE *pTarget = NE_GetPtr( module );
|
|
ERR_(fixup)("No implementation for %.*s.%s, setting to 0xdeadbeef\n",
|
|
*((BYTE *)pTarget + pTarget->name_table),
|
|
(char *)pTarget + pTarget->name_table + 1, func_name );
|
|
}
|
|
if (!address) address = (FARPROC16) 0xdeadbeef;
|
|
if (TRACE_ON(fixup))
|
|
{
|
|
NE_MODULE *pTarget = NE_GetPtr( module );
|
|
TRACE_(fixup)("%d: %.*s.%s=%04x:%04x %s\n", i + 1,
|
|
*((BYTE *)pTarget + pTarget->name_table),
|
|
(char *)pTarget + pTarget->name_table + 1,
|
|
func_name, HIWORD(address), LOWORD(address),
|
|
NE_GetRelocAddrName( rep->address_type, additive ) );
|
|
}
|
|
break;
|
|
|
|
case NE_RELTYPE_INTERNAL:
|
|
if ((rep->target1 & 0xff) == 0xff)
|
|
{
|
|
address = NE_GetEntryPoint( pModule->self, rep->target2 );
|
|
}
|
|
else
|
|
{
|
|
address = (FARPROC16)PTR_SEG_OFF_TO_SEGPTR( SEL(pSegTable[rep->target1-1].hSeg), rep->target2 );
|
|
}
|
|
|
|
TRACE_(fixup)("%d: %04x:%04x %s\n",
|
|
i + 1, HIWORD(address), LOWORD(address),
|
|
NE_GetRelocAddrName( rep->address_type, additive ) );
|
|
break;
|
|
|
|
case NE_RELTYPE_OSFIXUP:
|
|
/* Relocation type 7:
|
|
*
|
|
* These appear to be used as fixups for the Windows
|
|
* floating point emulator. Let's just ignore them and
|
|
* try to use the hardware floating point. Linux should
|
|
* successfully emulate the coprocessor if it doesn't
|
|
* exist.
|
|
*/
|
|
TRACE_(fixup)("%d: TYPE %d, OFFSET %04x, TARGET %04x %04x %s\n",
|
|
i + 1, rep->relocation_type, rep->offset,
|
|
rep->target1, rep->target2,
|
|
NE_GetRelocAddrName( rep->address_type, additive ) );
|
|
continue;
|
|
}
|
|
|
|
offset = rep->offset;
|
|
|
|
/* Apparently, high bit of address_type is sometimes set; */
|
|
/* we ignore it for now */
|
|
if (rep->address_type > NE_RADDR_OFFSET32)
|
|
{
|
|
char module[10];
|
|
GetModuleName16( pModule->self, module, sizeof(module) );
|
|
ERR_(fixup)("WARNING: module %s: unknown reloc addr type = 0x%02x. Please report.\n",
|
|
module, rep->address_type );
|
|
}
|
|
|
|
if (additive)
|
|
{
|
|
sp = PTR_SEG_OFF_TO_LIN( SEL(pSeg->hSeg), offset );
|
|
TRACE_(fixup)(" %04x:%04x\n", offset, *sp );
|
|
switch (rep->address_type & 0x7f)
|
|
{
|
|
case NE_RADDR_LOWBYTE:
|
|
*(BYTE *)sp += LOBYTE((int)address);
|
|
break;
|
|
case NE_RADDR_OFFSET16:
|
|
*sp += LOWORD(address);
|
|
break;
|
|
case NE_RADDR_POINTER32:
|
|
*sp += LOWORD(address);
|
|
*(sp+1) = HIWORD(address);
|
|
break;
|
|
case NE_RADDR_SELECTOR:
|
|
/* Borland creates additive records with offset zero. Strange, but OK */
|
|
if (*sp)
|
|
ERR_(fixup)("Additive selector to %04x.Please report\n",*sp);
|
|
else
|
|
*sp = HIWORD(address);
|
|
break;
|
|
default:
|
|
goto unknown;
|
|
}
|
|
}
|
|
else /* non-additive fixup */
|
|
{
|
|
do
|
|
{
|
|
sp = PTR_SEG_OFF_TO_LIN( SEL(pSeg->hSeg), offset );
|
|
next_offset = *sp;
|
|
TRACE_(fixup)(" %04x:%04x\n", offset, *sp );
|
|
switch (rep->address_type & 0x7f)
|
|
{
|
|
case NE_RADDR_LOWBYTE:
|
|
*(BYTE *)sp = LOBYTE((int)address);
|
|
break;
|
|
case NE_RADDR_OFFSET16:
|
|
*sp = LOWORD(address);
|
|
break;
|
|
case NE_RADDR_POINTER32:
|
|
*(FARPROC16 *)sp = address;
|
|
break;
|
|
case NE_RADDR_SELECTOR:
|
|
*sp = SELECTOROF(address);
|
|
break;
|
|
default:
|
|
goto unknown;
|
|
}
|
|
if (next_offset == offset) break; /* avoid infinite loop */
|
|
if (next_offset >= GlobalSize16(pSeg->hSeg)) break;
|
|
offset = next_offset;
|
|
} while (offset != 0xffff);
|
|
}
|
|
}
|
|
|
|
free(reloc_entries);
|
|
return TRUE;
|
|
|
|
unknown:
|
|
WARN_(fixup)("WARNING: %d: unknown ADDR TYPE %d, "
|
|
"TYPE %d, OFFSET %04x, TARGET %04x %04x\n",
|
|
i + 1, rep->address_type, rep->relocation_type,
|
|
rep->offset, rep->target1, rep->target2);
|
|
free(reloc_entries);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* NE_LoadAllSegments
|
|
*/
|
|
BOOL NE_LoadAllSegments( NE_MODULE *pModule )
|
|
{
|
|
int i;
|
|
SEGTABLEENTRY * pSegTable = (SEGTABLEENTRY *) NE_SEG_TABLE(pModule);
|
|
|
|
if (pModule->flags & NE_FFLAGS_SELFLOAD)
|
|
{
|
|
HFILE hf;
|
|
HFILE16 hFile16;
|
|
/* Handle self-loading modules */
|
|
SELFLOADHEADER *selfloadheader;
|
|
HMODULE16 hselfload = GetModuleHandle16("WPROCS");
|
|
DWORD oldstack;
|
|
WORD saved_hSeg = pSegTable[pModule->dgroup - 1].hSeg;
|
|
|
|
TRACE_(module)("%.*s is a self-loading module!\n",
|
|
*((BYTE*)pModule + pModule->name_table),
|
|
(char *)pModule + pModule->name_table + 1);
|
|
if (!NE_LoadSegment( pModule, 1 )) return FALSE;
|
|
selfloadheader = (SELFLOADHEADER *)
|
|
PTR_SEG_OFF_TO_LIN(SEL(pSegTable->hSeg), 0);
|
|
selfloadheader->EntryAddrProc = NE_GetEntryPoint(hselfload,27);
|
|
selfloadheader->MyAlloc = NE_GetEntryPoint(hselfload,28);
|
|
selfloadheader->SetOwner = NE_GetEntryPoint(GetModuleHandle16("KERNEL"),403);
|
|
pModule->self_loading_sel = SEL(GLOBAL_Alloc(GMEM_ZEROINIT, 0xFF00, pModule->self, FALSE, FALSE, FALSE));
|
|
oldstack = NtCurrentTeb()->cur_stack;
|
|
NtCurrentTeb()->cur_stack = PTR_SEG_OFF_TO_SEGPTR(pModule->self_loading_sel,
|
|
0xff00 - sizeof(STACK16FRAME) );
|
|
|
|
DuplicateHandle( GetCurrentProcess(), NE_OpenFile(pModule),
|
|
GetCurrentProcess(), &hf, 0, FALSE, DUPLICATE_SAME_ACCESS );
|
|
hFile16 = FILE_AllocDosHandle( hf );
|
|
TRACE_(dll)("CallBootAppProc(hModule=0x%04x,hf=0x%04x)\n",
|
|
pModule->self,hFile16);
|
|
Callbacks->CallBootAppProc(selfloadheader->BootApp, pModule->self,hFile16);
|
|
TRACE_(dll)("Return from CallBootAppProc\n");
|
|
_lclose16(hf);
|
|
/* some BootApp procs overwrite the segment handle of dgroup */
|
|
pSegTable[pModule->dgroup - 1].hSeg = saved_hSeg;
|
|
NtCurrentTeb()->cur_stack = oldstack;
|
|
|
|
for (i = 2; i <= pModule->seg_count; i++)
|
|
if (!NE_LoadSegment( pModule, i )) return FALSE;
|
|
}
|
|
else
|
|
{
|
|
for (i = 1; i <= pModule->seg_count; i++)
|
|
if (!NE_LoadSegment( pModule, i )) return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* NE_FixupSegmentPrologs
|
|
*
|
|
* Fixup exported functions prologs of one segment
|
|
*/
|
|
void NE_FixupSegmentPrologs(NE_MODULE *pModule, WORD segnum)
|
|
{
|
|
SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
|
|
ET_BUNDLE *bundle;
|
|
ET_ENTRY *entry;
|
|
WORD dgroup, num_entries, sel = SEL(pSegTable[segnum-1].hSeg);
|
|
BYTE *pSeg, *pFunc;
|
|
|
|
TRACE_(module)("(%d);\n", segnum);
|
|
|
|
if (pSegTable[segnum-1].flags & NE_SEGFLAGS_DATA)
|
|
{
|
|
pSegTable[segnum-1].flags |= NE_SEGFLAGS_LOADED;
|
|
return;
|
|
}
|
|
|
|
if (!pModule->dgroup) return;
|
|
|
|
if (!(dgroup = SEL(pSegTable[pModule->dgroup-1].hSeg))) return;
|
|
|
|
pSeg = PTR_SEG_OFF_TO_LIN(sel, 0);
|
|
|
|
bundle = (ET_BUNDLE *)((BYTE *)pModule+pModule->entry_table);
|
|
|
|
do {
|
|
TRACE_(module)("num_entries: %d, bundle: %p, next: %04x, pSeg: %p\n", bundle->last - bundle->first, bundle, bundle->next, pSeg);
|
|
if (!(num_entries = bundle->last - bundle->first))
|
|
return;
|
|
entry = (ET_ENTRY *)((BYTE *)bundle+6);
|
|
while (num_entries--)
|
|
{
|
|
/*TRACE(module, "entry: %p, entry->segnum: %d, entry->offs: %04x\n", entry, entry->segnum, entry->offs);*/
|
|
if (entry->segnum == segnum)
|
|
{
|
|
pFunc = ((BYTE *)pSeg+entry->offs);
|
|
TRACE_(module)("pFunc: %p, *(DWORD *)pFunc: %08lx, num_entries: %d\n", pFunc, *(DWORD *)pFunc, num_entries);
|
|
if (*(pFunc+2) == 0x90)
|
|
{
|
|
if (*(WORD *)pFunc == 0x581e) /* push ds, pop ax */
|
|
{
|
|
TRACE_(module)("patch %04x:%04x -> mov ax, ds\n", sel, entry->offs);
|
|
*(WORD *)pFunc = 0xd88c; /* mov ax, ds */
|
|
}
|
|
|
|
if (*(WORD *)pFunc == 0xd88c)
|
|
{
|
|
if ((entry->flags & 2)) /* public data ? */
|
|
{
|
|
TRACE_(module)("patch %04x:%04x -> mov ax, dgroup [%04x]\n", sel, entry->offs, dgroup);
|
|
*pFunc = 0xb8; /* mov ax, */
|
|
*(WORD *)(pFunc+1) = dgroup;
|
|
}
|
|
else
|
|
if ((pModule->flags & NE_FFLAGS_MULTIPLEDATA)
|
|
&& (entry->flags & 1)) /* exported ? */
|
|
{
|
|
TRACE_(module)("patch %04x:%04x -> nop, nop\n", sel, entry->offs);
|
|
*(WORD *)pFunc = 0x9090; /* nop, nop */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
entry++;
|
|
}
|
|
} while ( (bundle->next)
|
|
&& (bundle = ((ET_BUNDLE *)((BYTE *)pModule + bundle->next))) );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* PatchCodeHandle
|
|
*
|
|
* Needed for self-loading modules.
|
|
*/
|
|
DWORD WINAPI PatchCodeHandle16(HANDLE16 hSeg)
|
|
{
|
|
WORD segnum;
|
|
WORD sel = SEL(hSeg);
|
|
NE_MODULE *pModule = NE_GetPtr(FarGetOwner16(sel));
|
|
SEGTABLEENTRY *pSegTable = NE_SEG_TABLE(pModule);
|
|
|
|
TRACE_(module)("(%04x);\n", hSeg);
|
|
|
|
/* find the segment number of the module that belongs to hSeg */
|
|
for (segnum = 1; segnum <= pModule->seg_count; segnum++)
|
|
{
|
|
if (SEL(pSegTable[segnum-1].hSeg) == sel)
|
|
{
|
|
NE_FixupSegmentPrologs(pModule, segnum);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return MAKELONG(hSeg, sel);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NE_FixupPrologs
|
|
*
|
|
* Fixup the exported functions prologs.
|
|
*/
|
|
void NE_FixupPrologs( NE_MODULE *pModule )
|
|
{
|
|
WORD segnum;
|
|
|
|
TRACE_(module)("(%04x)\n", pModule->self );
|
|
|
|
if (pModule->flags & NE_FFLAGS_SELFLOAD)
|
|
NE_FixupSegmentPrologs(pModule, 1);
|
|
else
|
|
for (segnum=1; segnum <= pModule->seg_count; segnum++)
|
|
NE_FixupSegmentPrologs(pModule, segnum);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NE_GetDLLInitParams
|
|
*/
|
|
static VOID NE_GetDLLInitParams( NE_MODULE *pModule,
|
|
WORD *hInst, WORD *ds, WORD *heap )
|
|
{
|
|
SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
|
|
|
|
if (!(pModule->flags & NE_FFLAGS_SINGLEDATA))
|
|
{
|
|
if (pModule->flags & NE_FFLAGS_MULTIPLEDATA || pModule->dgroup)
|
|
{
|
|
/* Not SINGLEDATA */
|
|
ERR_(dll)("Library is not marked SINGLEDATA\n");
|
|
exit(1);
|
|
}
|
|
else /* DATA NONE DLL */
|
|
{
|
|
*ds = 0;
|
|
*heap = 0;
|
|
}
|
|
}
|
|
else /* DATA SINGLE DLL */
|
|
{
|
|
if (pModule->dgroup) {
|
|
*ds = SEL(pSegTable[pModule->dgroup-1].hSeg);
|
|
*heap = pModule->heap_size;
|
|
}
|
|
else /* hmm, DLL has no dgroup,
|
|
but why has it NE_FFLAGS_SINGLEDATA set ?
|
|
Buggy DLL compiler ? */
|
|
{
|
|
*ds = 0;
|
|
*heap = 0;
|
|
}
|
|
}
|
|
|
|
*hInst = *ds ? *ds : pModule->self;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* NE_InitDLL
|
|
*
|
|
* Call the DLL initialization code
|
|
*/
|
|
static BOOL NE_InitDLL( TDB* pTask, NE_MODULE *pModule )
|
|
{
|
|
SEGTABLEENTRY *pSegTable;
|
|
WORD hInst, ds, heap;
|
|
CONTEXT86 context;
|
|
|
|
pSegTable = NE_SEG_TABLE( pModule );
|
|
|
|
if (!(pModule->flags & NE_FFLAGS_LIBMODULE) ||
|
|
(pModule->flags & NE_FFLAGS_WIN32)) return TRUE; /*not a library*/
|
|
|
|
/* Call USER signal handler for Win3.1 compatibility. */
|
|
TASK_CallTaskSignalProc( USIG16_DLL_LOAD, pModule->self );
|
|
|
|
if (!pModule->cs) return TRUE; /* no initialization code */
|
|
|
|
|
|
/* Registers at initialization must be:
|
|
* cx heap size
|
|
* di library instance
|
|
* ds data segment if any
|
|
* es:si command line (always 0)
|
|
*/
|
|
|
|
memset( &context, 0, sizeof(context) );
|
|
|
|
NE_GetDLLInitParams( pModule, &hInst, &ds, &heap );
|
|
|
|
ECX_reg(&context) = heap;
|
|
EDI_reg(&context) = hInst;
|
|
DS_reg(&context) = ds;
|
|
ES_reg(&context) = ds; /* who knows ... */
|
|
|
|
CS_reg(&context) = SEL(pSegTable[pModule->cs-1].hSeg);
|
|
EIP_reg(&context) = pModule->ip;
|
|
EBP_reg(&context) = OFFSETOF(NtCurrentTeb()->cur_stack) + (WORD)&((STACK16FRAME*)0)->bp;
|
|
|
|
|
|
pModule->cs = 0; /* Don't initialize it twice */
|
|
TRACE_(dll)("Calling LibMain, cs:ip=%04lx:%04lx ds=%04lx di=%04x cx=%04x\n",
|
|
CS_reg(&context), EIP_reg(&context), DS_reg(&context),
|
|
DI_reg(&context), CX_reg(&context) );
|
|
Callbacks->CallRegisterShortProc( &context, 0 );
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* NE_CallDllEntryPoint
|
|
*
|
|
* Call the DllEntryPoint of DLLs with subsystem >= 4.0
|
|
*/
|
|
|
|
static void NE_CallDllEntryPoint( NE_MODULE *pModule, DWORD dwReason )
|
|
{
|
|
WORD hInst, ds, heap;
|
|
FARPROC16 entryPoint;
|
|
WORD ordinal;
|
|
CONTEXT86 context;
|
|
LPBYTE stack = (LPBYTE)CURRENT_STACK16;
|
|
|
|
if (!(pModule->flags & NE_FFLAGS_BUILTIN) && pModule->expected_version < 0x0400) return;
|
|
if (!(ordinal = NE_GetOrdinal( pModule->self, "DllEntryPoint" ))) return;
|
|
if (!(entryPoint = NE_GetEntryPoint( pModule->self, ordinal ))) return;
|
|
|
|
memset( &context, 0, sizeof(context) );
|
|
|
|
NE_GetDLLInitParams( pModule, &hInst, &ds, &heap );
|
|
|
|
DS_reg(&context) = ds;
|
|
ES_reg(&context) = ds; /* who knows ... */
|
|
|
|
CS_reg(&context) = HIWORD(entryPoint);
|
|
EIP_reg(&context) = LOWORD(entryPoint);
|
|
EBP_reg(&context) = OFFSETOF( NtCurrentTeb()->cur_stack )
|
|
+ (WORD)&((STACK16FRAME*)0)->bp;
|
|
|
|
*(DWORD *)(stack - 4) = dwReason; /* dwReason */
|
|
*(WORD *) (stack - 6) = hInst; /* hInst */
|
|
*(WORD *) (stack - 8) = ds; /* wDS */
|
|
*(WORD *) (stack - 10) = heap; /* wHeapSize */
|
|
*(DWORD *)(stack - 14) = 0; /* dwReserved1 */
|
|
*(WORD *) (stack - 16) = 0; /* wReserved2 */
|
|
|
|
TRACE_(dll)("Calling DllEntryPoint, cs:ip=%04lx:%04lx\n",
|
|
CS_reg(&context), EIP_reg(&context));
|
|
|
|
Callbacks->CallRegisterShortProc( &context, 16 );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* NE_InitializeDLLs
|
|
*
|
|
* Recursively initialize all DLLs (according to the order in which
|
|
* they where loaded).
|
|
*/
|
|
void NE_InitializeDLLs( HMODULE16 hModule )
|
|
{
|
|
TDB* pTask = (TDB*)GlobalLock16(GetCurrentTask());
|
|
NE_MODULE *pModule;
|
|
HMODULE16 *pDLL;
|
|
|
|
if (!(pModule = NE_GetPtr( hModule ))) return;
|
|
assert( !(pModule->flags & NE_FFLAGS_WIN32) );
|
|
|
|
if (pModule->dlls_to_init)
|
|
{
|
|
HGLOBAL16 to_init = pModule->dlls_to_init;
|
|
pModule->dlls_to_init = 0;
|
|
for (pDLL = (HMODULE16 *)GlobalLock16( to_init ); *pDLL; pDLL++)
|
|
{
|
|
NE_InitializeDLLs( *pDLL );
|
|
}
|
|
GlobalFree16( to_init );
|
|
}
|
|
NE_InitDLL( pTask, pModule );
|
|
NE_CallDllEntryPoint( pModule, DLL_PROCESS_ATTACH );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* NE_Ne2MemFlags
|
|
*
|
|
* This function translates NE segment flags to GlobalAlloc flags
|
|
*/
|
|
static WORD NE_Ne2MemFlags(WORD flags)
|
|
{
|
|
WORD memflags = 0;
|
|
#if 1
|
|
if (flags & NE_SEGFLAGS_DISCARDABLE)
|
|
memflags |= GMEM_DISCARDABLE;
|
|
if (flags & NE_SEGFLAGS_MOVEABLE ||
|
|
( ! (flags & NE_SEGFLAGS_DATA) &&
|
|
! (flags & NE_SEGFLAGS_LOADED) &&
|
|
! (flags & NE_SEGFLAGS_ALLOCATED)
|
|
)
|
|
)
|
|
memflags |= GMEM_MOVEABLE;
|
|
memflags |= GMEM_ZEROINIT;
|
|
#else
|
|
memflags = GMEM_ZEROINIT | GMEM_FIXED;
|
|
#endif
|
|
return memflags;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* NE_CreateInstance
|
|
*
|
|
* If lib_only is TRUE, handle the module like a library even if it is a .EXE
|
|
*/
|
|
HINSTANCE16 NE_CreateInstance( NE_MODULE *pModule, HINSTANCE16 *prev,
|
|
BOOL lib_only )
|
|
{
|
|
SEGTABLEENTRY *pSegment;
|
|
int minsize;
|
|
HINSTANCE16 hNewSeg;
|
|
|
|
if (pModule->dgroup == 0)
|
|
{
|
|
if (prev) *prev = pModule->self;
|
|
return pModule->self;
|
|
}
|
|
|
|
pSegment = NE_SEG_TABLE( pModule ) + pModule->dgroup - 1;
|
|
if (prev) *prev = SEL(pSegment->hSeg);
|
|
|
|
/* if it's a library, create a new instance only the first time */
|
|
if (pSegment->hSeg)
|
|
{
|
|
if (pModule->flags & NE_FFLAGS_LIBMODULE) return SEL(pSegment->hSeg);
|
|
if (lib_only) return SEL(pSegment->hSeg);
|
|
}
|
|
|
|
minsize = pSegment->minsize ? pSegment->minsize : 0x10000;
|
|
if (pModule->ss == pModule->dgroup) minsize += pModule->stack_size;
|
|
minsize += pModule->heap_size;
|
|
hNewSeg = GLOBAL_Alloc( NE_Ne2MemFlags(pSegment->flags), minsize,
|
|
pModule->self, FALSE, FALSE, FALSE );
|
|
if (!hNewSeg) return 0;
|
|
pSegment->hSeg = hNewSeg;
|
|
pSegment->flags |= NE_SEGFLAGS_ALLOCATED;
|
|
|
|
/* a HINSTANCE is the selector of the DSEG */
|
|
return (HINSTANCE16)SEL(hNewSeg);
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* NE_AllocateSegment (WPROCS.26)
|
|
*
|
|
* MyAlloc() function for self-loading apps.
|
|
*/
|
|
DWORD WINAPI NE_AllocateSegment( WORD wFlags, WORD wSize, WORD wElem )
|
|
{
|
|
WORD size = wSize << wElem;
|
|
HANDLE16 hMem = 0;
|
|
|
|
if (wSize || (wFlags & NE_SEGFLAGS_MOVEABLE))
|
|
hMem = GlobalAlloc16( NE_Ne2MemFlags(wFlags), size);
|
|
|
|
if ( ((wFlags & 0x7) != 0x1) && /* DATA */
|
|
((wFlags & 0x7) != 0x7) ) /* DATA|ALLOCATED|LOADED */
|
|
{
|
|
WORD hSel = SEL(hMem);
|
|
WORD access = SelectorAccessRights16(hSel,0,0);
|
|
|
|
access |= 2<<2; /* SEGMENT_CODE */
|
|
SelectorAccessRights16(hSel,1,access);
|
|
}
|
|
if (size)
|
|
return MAKELONG( hMem, SEL(hMem) );
|
|
else
|
|
return MAKELONG( 0, hMem );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* NE_CreateSegments
|
|
*/
|
|
BOOL NE_CreateSegments( NE_MODULE *pModule )
|
|
{
|
|
SEGTABLEENTRY *pSegment;
|
|
int i, minsize, seg_count;
|
|
|
|
assert( !(pModule->flags & NE_FFLAGS_WIN32) );
|
|
|
|
pSegment = NE_SEG_TABLE( pModule );
|
|
|
|
if (pModule->flags & NE_FFLAGS_SELFLOAD)
|
|
seg_count = 1;
|
|
else
|
|
seg_count = pModule->seg_count;
|
|
for (i = 1; i <= seg_count; i++, pSegment++)
|
|
{
|
|
minsize = pSegment->minsize ? pSegment->minsize : 0x10000;
|
|
if (i == pModule->ss) minsize += pModule->stack_size;
|
|
/* The DGROUP is allocated by NE_CreateInstance */
|
|
if (i == pModule->dgroup) continue;
|
|
pSegment->hSeg = GLOBAL_Alloc( NE_Ne2MemFlags(pSegment->flags),
|
|
minsize, pModule->self,
|
|
!(pSegment->flags & NE_SEGFLAGS_DATA),
|
|
(pSegment->flags & NE_SEGFLAGS_32BIT) != 0,
|
|
FALSE /*pSegment->flags & NE_SEGFLAGS_READONLY*/ );
|
|
if (!pSegment->hSeg) return FALSE;
|
|
pSegment->flags |= NE_SEGFLAGS_ALLOCATED;
|
|
}
|
|
|
|
pModule->dgroup_entry = pModule->dgroup ? pModule->seg_table +
|
|
(pModule->dgroup - 1) * sizeof(SEGTABLEENTRY) : 0;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* IsSharedSelector (KERNEL.345)
|
|
*/
|
|
BOOL16 WINAPI IsSharedSelector16( HANDLE16 selector )
|
|
{
|
|
/* Check whether the selector belongs to a DLL */
|
|
NE_MODULE *pModule = NE_GetPtr( selector );
|
|
if (!pModule) return FALSE;
|
|
return (pModule->flags & NE_FFLAGS_LIBMODULE) != 0;
|
|
}
|