| /* |
| * NE segment loading |
| * |
| * Copyright 1993 Robert J. Amstadt |
| * Copyright 1995 Alexandre Julliard |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "config.h" |
| #include "wine/port.h" |
| |
| #include <assert.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <fcntl.h> |
| #ifdef HAVE_UNISTD_H |
| # include <unistd.h> |
| #endif |
| #include <ctype.h> |
| #include <string.h> |
| |
| #include "wine/winbase16.h" |
| #include "wownt32.h" |
| #include "winternl.h" |
| #include "wine/library.h" |
| #include "kernel16_private.h" |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(fixup); |
| WINE_DECLARE_DEBUG_CHANNEL(dll); |
| WINE_DECLARE_DEBUG_CHANNEL(module); |
| |
| /* |
| * Relocation table entry |
| */ |
| struct relocation_entry_s |
| { |
| BYTE address_type; /* Relocation address type */ |
| BYTE relocation_type; /* Relocation type */ |
| WORD offset; /* Offset in segment to fixup */ |
| WORD target1; /* Target specification */ |
| WORD target2; /* Target specification */ |
| }; |
| |
| /* |
| * Relocation address types |
| */ |
| #define NE_RADDR_LOWBYTE 0 |
| #define NE_RADDR_SELECTOR 2 |
| #define NE_RADDR_POINTER32 3 |
| #define NE_RADDR_OFFSET16 5 |
| #define NE_RADDR_POINTER48 11 |
| #define NE_RADDR_OFFSET32 13 |
| |
| /* |
| * Relocation types |
| */ |
| #define NE_RELTYPE_INTERNAL 0 |
| #define NE_RELTYPE_ORDINAL 1 |
| #define NE_RELTYPE_NAME 2 |
| #define NE_RELTYPE_OSFIXUP 3 |
| #define NE_RELFLAG_ADDITIVE 4 |
| |
| /* Self-loading modules contain this structure in their first segment */ |
| typedef struct |
| { |
| WORD version; /* Must be "A0" (0x3041) */ |
| WORD reserved; |
| FARPROC16 BootApp; /* startup procedure */ |
| FARPROC16 LoadAppSeg; /* procedure to load a segment */ |
| FARPROC16 reserved2; |
| FARPROC16 MyAlloc; /* memory allocation procedure, |
| * wine must write this field */ |
| FARPROC16 EntryAddrProc; |
| FARPROC16 ExitProc; /* exit procedure */ |
| WORD reserved3[4]; |
| FARPROC16 SetOwner; /* Set Owner procedure, exported by wine */ |
| } SELFLOADHEADER; |
| |
| #define SEL(x) ((x)|1) |
| |
| static void NE_FixupSegmentPrologs(NE_MODULE *pModule, WORD segnum); |
| |
| |
| /*********************************************************************** |
| * 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_OpenFile |
| */ |
| static HFILE16 NE_OpenFile( NE_MODULE *pModule ) |
| { |
| char *name = NE_MODULE_NAME( pModule ); |
| HANDLE handle = CreateFileA( name, GENERIC_READ, FILE_SHARE_READ, |
| NULL, OPEN_EXISTING, 0, 0 ); |
| |
| if (handle == INVALID_HANDLE_VALUE) |
| { |
| ERR( "Can't open file '%s' for module %04x\n", name, pModule->self ); |
| return HFILE_ERROR; |
| } |
| return Win32HandleToDosFileHandle( handle ); |
| } |
| |
| |
| /*********************************************************************** |
| * apply_relocations |
| * |
| * Apply relocations to a segment. Helper for NE_LoadSegment. |
| */ |
| static inline BOOL apply_relocations( NE_MODULE *pModule, const struct relocation_entry_s *rep, |
| int count, int segnum ) |
| { |
| BYTE *func_name; |
| char buffer[256]; |
| int i, ordinal; |
| WORD offset, *sp; |
| HMODULE16 module; |
| FARPROC16 address = 0; |
| HMODULE16 *pModuleTable = (HMODULE16 *)((char *)pModule + pModule->ne_modtab); |
| SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule ); |
| SEGTABLEENTRY *pSeg = pSegTable + segnum - 1; |
| |
| /* |
| * Go through the relocation table one entry at a time. |
| */ |
| 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 */ |
| int additive = rep->relocation_type & NE_RELFLAG_ADDITIVE; |
| switch (rep->relocation_type & 3) |
| { |
| 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->ne_restab), |
| *((BYTE *)pModule + pModule->ne_restab), |
| (char *)pModule + pModule->ne_restab + 1 ); |
| else |
| { |
| ERR("No implementation for %.*s.%d, setting to 0xdeadbeef\n", |
| *((BYTE *)pTarget + pTarget->ne_restab), |
| (char *)pTarget + pTarget->ne_restab + 1, |
| ordinal ); |
| address = (FARPROC16)0xdeadbeef; |
| } |
| } |
| if (TRACE_ON(fixup)) |
| { |
| NE_MODULE *pTarget = NE_GetPtr( module ); |
| TRACE("%d: %.*s.%d=%04x:%04x %s\n", i + 1, |
| *((BYTE *)pTarget + pTarget->ne_restab), |
| (char *)pTarget + pTarget->ne_restab + 1, |
| ordinal, HIWORD(address), LOWORD(address), |
| NE_GetRelocAddrName( rep->address_type, additive ) ); |
| } |
| break; |
| |
| case NE_RELTYPE_NAME: |
| module = pModuleTable[rep->target1-1]; |
| func_name = (BYTE *)pModule + pModule->ne_imptab + rep->target2; |
| memcpy( buffer, func_name+1, *func_name ); |
| buffer[*func_name] = '\0'; |
| ordinal = NE_GetOrdinal( module, buffer ); |
| address = NE_GetEntryPoint( module, ordinal ); |
| |
| if (ERR_ON(fixup) && !address) |
| { |
| NE_MODULE *pTarget = NE_GetPtr( module ); |
| ERR("No implementation for %.*s.%s, setting to 0xdeadbeef\n", |
| *((BYTE *)pTarget + pTarget->ne_restab), |
| (char *)pTarget + pTarget->ne_restab + 1, buffer ); |
| } |
| if (!address) address = (FARPROC16) 0xdeadbeef; |
| if (TRACE_ON(fixup)) |
| { |
| NE_MODULE *pTarget = NE_GetPtr( module ); |
| TRACE("%d: %.*s.%s=%04x:%04x %s\n", i + 1, |
| *((BYTE *)pTarget + pTarget->ne_restab), |
| (char *)pTarget + pTarget->ne_restab + 1, |
| buffer, 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)MAKESEGPTR( SEL(pSegTable[rep->target1-1].hSeg), rep->target2 ); |
| } |
| |
| TRACE("%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("%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("WARNING: module %s: unknown reloc addr type = 0x%02x. Please report.\n", |
| module, rep->address_type ); |
| } |
| |
| if (additive) |
| { |
| sp = MapSL( MAKESEGPTR( SEL(pSeg->hSeg), offset ) ); |
| TRACE(" %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("Additive selector to %04x.Please report\n",*sp); |
| else |
| *sp = HIWORD(address); |
| break; |
| default: |
| goto unknown; |
| } |
| } |
| else /* non-additive fixup */ |
| { |
| do |
| { |
| WORD next_offset; |
| |
| sp = MapSL( MAKESEGPTR( SEL(pSeg->hSeg), offset ) ); |
| next_offset = *sp; |
| TRACE(" %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); |
| } |
| } |
| return TRUE; |
| |
| unknown: |
| WARN("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); |
| return FALSE; |
| } |
| |
| |
| /*********************************************************************** |
| * NE_LoadSegment |
| */ |
| BOOL NE_LoadSegment( NE_MODULE *pModule, WORD segnum ) |
| { |
| WORD count; |
| DWORD pos; |
| const struct relocation_entry_s *rep; |
| int size; |
| SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule ); |
| SEGTABLEENTRY *pSeg = pSegTable + segnum - 1; |
| |
| if (pSeg->flags & NE_SEGFLAGS_LOADED) |
| { |
| /* self-loader ? -> already loaded it */ |
| if (pModule->ne_flags & NE_FFLAGS_SELFLOAD) |
| return TRUE; |
| |
| /* leave, except for DGROUP, as this may be the second instance */ |
| if (segnum != pModule->ne_autodata) |
| return TRUE; |
| } |
| |
| if (!pSeg->filepos) return TRUE; /* No file image, just return */ |
| |
| TRACE_(module)("Loading segment %d, hSeg=%04x, flags=%04x\n", |
| segnum, pSeg->hSeg, pSeg->flags ); |
| pos = pSeg->filepos << pModule->ne_align; |
| if (pSeg->size) size = pSeg->size; |
| else size = pSeg->minsize ? pSeg->minsize : 0x10000; |
| |
| if (pModule->ne_flags & NE_FFLAGS_SELFLOAD && segnum > 1) |
| { |
| /* Implement self-loading segments */ |
| SELFLOADHEADER *selfloadheader; |
| void *oldstack; |
| HFILE16 hFile16; |
| WORD args[3]; |
| DWORD ret; |
| |
| selfloadheader = MapSL( MAKESEGPTR(SEL(pSegTable->hSeg),0) ); |
| oldstack = NtCurrentTeb()->WOW32Reserved; |
| NtCurrentTeb()->WOW32Reserved = (void *)MAKESEGPTR(pModule->self_loading_sel, |
| 0xff00 - sizeof(STACK16FRAME)); |
| |
| hFile16 = NE_OpenFile( pModule ); |
| TRACE_(dll)("Call LoadAppSegProc(hmodule=0x%04x,hf=%x,segnum=%d)\n", |
| pModule->self,hFile16,segnum ); |
| args[2] = pModule->self; |
| args[1] = hFile16; |
| args[0] = segnum; |
| WOWCallback16Ex( (DWORD)selfloadheader->LoadAppSeg, WCB16_PASCAL, sizeof(args), args, &ret ); |
| pSeg->hSeg = LOWORD(ret); |
| TRACE_(dll)("Ret LoadAppSegProc: hSeg=0x%04x\n", pSeg->hSeg); |
| _lclose16( hFile16 ); |
| NtCurrentTeb()->WOW32Reserved = oldstack; |
| |
| pSeg->flags |= NE_SEGFLAGS_LOADED; |
| return TRUE; |
| } |
| else if (!(pSeg->flags & NE_SEGFLAGS_ITERATED)) |
| { |
| void *mem = GlobalLock16(pSeg->hSeg); |
| if (!NE_READ_DATA( pModule, mem, pos, size )) |
| return FALSE; |
| pos += size; |
| } |
| 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. |
| */ |
| const char *buff = NE_GET_DATA( pModule, pos, size ); |
| const char* curr = buff; |
| char *mem = GlobalLock16(pSeg->hSeg); |
| |
| pos += size; |
| if (buff == NULL) return FALSE; |
| |
| while(curr < buff + size) { |
| unsigned int rept = ((const short *)curr)[0]; |
| unsigned int len = ((const short *)curr)[1]; |
| |
| curr += 2*sizeof(short); |
| while (rept--) |
| { |
| memcpy( mem, curr, len ); |
| mem += len; |
| } |
| curr += len; |
| } |
| } |
| |
| pSeg->flags |= NE_SEGFLAGS_LOADED; |
| |
| /* Perform exported function prolog fixups */ |
| NE_FixupSegmentPrologs( pModule, segnum ); |
| |
| if (!(pSeg->flags & NE_SEGFLAGS_RELOC_DATA)) |
| return TRUE; /* No relocation data, we are done */ |
| |
| if (!NE_READ_DATA( pModule, &count, pos, sizeof(count) ) || !count) return TRUE; |
| pos += sizeof(count); |
| |
| TRACE("Fixups for %.*s, segment %d, hSeg %04x\n", |
| *((BYTE *)pModule + pModule->ne_restab), |
| (char *)pModule + pModule->ne_restab + 1, |
| segnum, pSeg->hSeg ); |
| |
| if (!(rep = NE_GET_DATA( pModule, pos, count * sizeof(struct relocation_entry_s) ))) |
| return FALSE; |
| |
| return apply_relocations( pModule, rep, count, segnum ); |
| } |
| |
| |
| /*********************************************************************** |
| * NE_LoadAllSegments |
| */ |
| BOOL NE_LoadAllSegments( NE_MODULE *pModule ) |
| { |
| int i; |
| SEGTABLEENTRY * pSegTable = NE_SEG_TABLE(pModule); |
| |
| if (pModule->ne_flags & NE_FFLAGS_SELFLOAD) |
| { |
| HFILE16 hFile16; |
| HGLOBAL16 sel; |
| /* Handle self-loading modules */ |
| SELFLOADHEADER *selfloadheader; |
| HMODULE16 mod = GetModuleHandle16("KERNEL"); |
| void *oldstack; |
| WORD args[2]; |
| |
| TRACE_(module)("%.*s is a self-loading module!\n", |
| *((BYTE*)pModule + pModule->ne_restab), |
| (char *)pModule + pModule->ne_restab + 1); |
| if (!NE_LoadSegment( pModule, 1 )) return FALSE; |
| selfloadheader = MapSL( MAKESEGPTR(SEL(pSegTable->hSeg), 0) ); |
| selfloadheader->EntryAddrProc = GetProcAddress16(mod,"EntryAddrProc"); |
| selfloadheader->MyAlloc = GetProcAddress16(mod,"MyAlloc"); |
| selfloadheader->SetOwner = GetProcAddress16(mod,"FarSetOwner"); |
| sel = GlobalAlloc16( GMEM_ZEROINIT, 0xFF00 ); |
| pModule->self_loading_sel = SEL(sel); |
| FarSetOwner16( sel, pModule->self ); |
| oldstack = NtCurrentTeb()->WOW32Reserved; |
| NtCurrentTeb()->WOW32Reserved = (void *)MAKESEGPTR(pModule->self_loading_sel, |
| 0xff00 - sizeof(STACK16FRAME) ); |
| |
| hFile16 = NE_OpenFile(pModule); |
| TRACE_(dll)("CallBootAppProc(hModule=0x%04x,hf=0x%04x)\n", |
| pModule->self,hFile16); |
| args[1] = pModule->self; |
| args[0] = hFile16; |
| WOWCallback16Ex( (DWORD)selfloadheader->BootApp, WCB16_PASCAL, sizeof(args), args, NULL ); |
| TRACE_(dll)("Return from CallBootAppProc\n"); |
| _lclose16(hFile16); |
| NtCurrentTeb()->WOW32Reserved = oldstack; |
| |
| for (i = 2; i <= pModule->ne_cseg; i++) |
| if (!NE_LoadSegment( pModule, i )) return FALSE; |
| } |
| else |
| { |
| for (i = 1; i <= pModule->ne_cseg; i++) |
| if (!NE_LoadSegment( pModule, i )) return FALSE; |
| } |
| return TRUE; |
| } |
| |
| |
| /*********************************************************************** |
| * NE_FixupSegmentPrologs |
| * |
| * Fixup exported functions prologs of one segment |
| */ |
| static 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("(%d);\n", segnum); |
| |
| if (pSegTable[segnum-1].flags & NE_SEGFLAGS_DATA) |
| { |
| pSegTable[segnum-1].flags |= NE_SEGFLAGS_LOADED; |
| return; |
| } |
| |
| if (!pModule->ne_autodata) return; |
| |
| if (!pSegTable[pModule->ne_autodata-1].hSeg) return; |
| dgroup = SEL(pSegTable[pModule->ne_autodata-1].hSeg); |
| |
| pSeg = MapSL( MAKESEGPTR(sel, 0) ); |
| |
| bundle = (ET_BUNDLE *)((BYTE *)pModule+pModule->ne_enttab); |
| |
| do { |
| TRACE("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("entry: %p, entry->segnum: %d, entry->offs: %04x\n", entry, entry->segnum, entry->offs);*/ |
| if (entry->segnum == segnum) |
| { |
| pFunc = pSeg+entry->offs; |
| TRACE("pFunc: %p, *(DWORD *)pFunc: %08x, num_entries: %d\n", pFunc, *(DWORD *)pFunc, num_entries); |
| if (*(pFunc+2) == 0x90) |
| { |
| if (*(WORD *)pFunc == 0x581e) /* push ds, pop ax */ |
| { |
| TRACE("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("patch %04x:%04x -> mov ax, dgroup [%04x]\n", sel, entry->offs, dgroup); |
| *pFunc = 0xb8; /* mov ax, */ |
| *(WORD *)(pFunc+1) = dgroup; |
| } |
| else if ((pModule->ne_flags & NE_FFLAGS_MULTIPLEDATA) |
| && (entry->flags & 1)) /* exported ? */ |
| { |
| TRACE("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 (KERNEL.110) |
| * |
| * 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->ne_cseg; segnum++) |
| { |
| if (SEL(pSegTable[segnum-1].hSeg) == sel) |
| { |
| NE_FixupSegmentPrologs(pModule, segnum); |
| break; |
| } |
| } |
| |
| return MAKELONG(hSeg, sel); |
| } |
| |
| |
| /*********************************************************************** |
| * NE_GetDLLInitParams |
| */ |
| static VOID NE_GetDLLInitParams( NE_MODULE *pModule, |
| WORD *hInst, WORD *ds, WORD *heap ) |
| { |
| SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule ); |
| |
| if (!(pModule->ne_flags & NE_FFLAGS_SINGLEDATA)) |
| { |
| if (pModule->ne_flags & NE_FFLAGS_MULTIPLEDATA || pModule->ne_autodata) |
| { |
| /* 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->ne_autodata) { |
| *ds = SEL(pSegTable[pModule->ne_autodata-1].hSeg); |
| *heap = pModule->ne_heap; |
| } |
| else /* hmm, DLL has no dgroup, |
| but why has it NE_FFLAGS_SINGLEDATA set ? |
| Buggy DLL compiler ? */ |
| { |
| *ds = 0; |
| *heap = 0; |
| } |
| } |
| |
| *hInst = *ds ? GlobalHandle16(*ds) : pModule->self; |
| } |
| |
| |
| /*********************************************************************** |
| * NE_InitDLL |
| * |
| * Call the DLL initialization code |
| */ |
| static BOOL NE_InitDLL( NE_MODULE *pModule ) |
| { |
| SEGTABLEENTRY *pSegTable; |
| WORD hInst, ds, heap; |
| CONTEXT context; |
| |
| pSegTable = NE_SEG_TABLE( pModule ); |
| |
| if (!(pModule->ne_flags & NE_FFLAGS_LIBMODULE) || |
| (pModule->ne_flags & NE_FFLAGS_WIN32)) return TRUE; /*not a library*/ |
| |
| /* Call USER signal handler for Win3.1 compatibility. */ |
| NE_CallUserSignalProc( pModule->self, USIG16_DLL_LOAD ); |
| |
| if (!SELECTOROF(pModule->ne_csip)) 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 ); |
| |
| context.Ecx = heap; |
| context.Edi = hInst; |
| context.SegDs = ds; |
| context.SegEs = ds; /* who knows ... */ |
| context.SegFs = wine_get_fs(); |
| context.SegGs = wine_get_gs(); |
| context.SegCs = SEL(pSegTable[SELECTOROF(pModule->ne_csip)-1].hSeg); |
| context.Eip = OFFSETOF(pModule->ne_csip); |
| context.Ebp = OFFSETOF(NtCurrentTeb()->WOW32Reserved) + FIELD_OFFSET(STACK16FRAME,bp); |
| |
| pModule->ne_csip = 0; /* Don't initialize it twice */ |
| TRACE_(dll)("Calling LibMain for %.*s, cs:ip=%04x:%04x ds=%04x di=%04x cx=%04x\n", |
| *((BYTE*)pModule + pModule->ne_restab), |
| (char *)pModule + pModule->ne_restab + 1, |
| context.SegCs, context.Eip, context.SegDs, |
| LOWORD(context.Edi), LOWORD(context.Ecx) ); |
| WOWCallback16Ex( 0, WCB16_REGS, 0, NULL, (DWORD *)&context ); |
| return TRUE; |
| } |
| |
| /*********************************************************************** |
| * NE_InitializeDLLs |
| * |
| * Recursively initialize all DLLs (according to the order in which |
| * they where loaded). |
| */ |
| void NE_InitializeDLLs( HMODULE16 hModule ) |
| { |
| NE_MODULE *pModule; |
| HMODULE16 *pDLL; |
| |
| if (!(pModule = NE_GetPtr( hModule ))) return; |
| assert( !(pModule->ne_flags & NE_FFLAGS_WIN32) ); |
| |
| if (pModule->dlls_to_init) |
| { |
| HGLOBAL16 to_init = pModule->dlls_to_init; |
| pModule->dlls_to_init = 0; |
| for (pDLL = GlobalLock16( to_init ); *pDLL; pDLL++) |
| { |
| NE_InitializeDLLs( *pDLL ); |
| } |
| GlobalFree16( to_init ); |
| } |
| NE_InitDLL( pModule ); |
| } |
| |
| |
| /********************************************************************** |
| * NE_CallUserSignalProc |
| * |
| * According to "Undocumented Windows", the task signal proc is |
| * bypassed for module load/unload notifications, and the USER signal |
| * proc is called directly instead. This is what this function does. |
| */ |
| typedef DWORD (WINAPI *pSignalProc)( HANDLE16 module, UINT16 code, UINT16 exit, |
| HINSTANCE16 inst, HQUEUE16 queue ); |
| |
| void NE_CallUserSignalProc( HMODULE16 hModule, UINT16 code ) |
| { |
| FARPROC16 proc; |
| HMODULE16 user = GetModuleHandle16("user.exe"); |
| |
| if (!user) return; |
| if ((proc = GetProcAddress16( user, "SignalProc" ))) |
| { |
| /* USER is always a builtin dll */ |
| pSignalProc sigproc = (pSignalProc)((ENTRYPOINT16 *)MapSL( (SEGPTR)proc ))->target; |
| sigproc( hModule, code, 0, 0, 0 ); |
| } |
| } |
| |
| |
| /*********************************************************************** |
| * NE_CallDllEntryPoint |
| * |
| * Call the DllEntryPoint of DLLs with subsystem >= 4.0 |
| */ |
| typedef DWORD (WINAPI *WinNEEntryProc)(DWORD,WORD,WORD,WORD,DWORD,WORD); |
| |
| static void NE_CallDllEntryPoint( NE_MODULE *pModule, DWORD dwReason ) |
| { |
| WORD hInst, ds, heap; |
| FARPROC16 entryPoint; |
| |
| if (!(pModule->ne_flags & NE_FFLAGS_LIBMODULE)) return; |
| if (!(pModule->ne_flags & NE_FFLAGS_BUILTIN) && pModule->ne_expver < 0x0400) return; |
| if (!(entryPoint = GetProcAddress16( pModule->self, "DllEntryPoint" ))) return; |
| |
| NE_GetDLLInitParams( pModule, &hInst, &ds, &heap ); |
| |
| TRACE_(dll)( "Calling %s DllEntryPoint, cs:ip=%04x:%04x\n", |
| NE_MODULE_NAME( pModule ), |
| SELECTOROF(entryPoint), OFFSETOF(entryPoint) ); |
| |
| if ( pModule->ne_flags & NE_FFLAGS_BUILTIN ) |
| { |
| WinNEEntryProc entryProc = (WinNEEntryProc)((ENTRYPOINT16 *)MapSL( (SEGPTR)entryPoint ))->target; |
| |
| entryProc( dwReason, hInst, ds, heap, 0, 0 ); |
| } |
| else |
| { |
| CONTEXT context; |
| WORD args[8]; |
| |
| memset( &context, 0, sizeof(context) ); |
| context.SegDs = ds; |
| context.SegEs = ds; /* who knows ... */ |
| context.SegFs = wine_get_fs(); |
| context.SegGs = wine_get_gs(); |
| context.SegCs = HIWORD(entryPoint); |
| context.Eip = LOWORD(entryPoint); |
| context.Ebp = OFFSETOF(NtCurrentTeb()->WOW32Reserved) + FIELD_OFFSET(STACK16FRAME,bp); |
| |
| args[7] = HIWORD(dwReason); |
| args[6] = LOWORD(dwReason); |
| args[5] = hInst; |
| args[4] = ds; |
| args[3] = heap; |
| args[2] = 0; /* HIWORD(dwReserved1) */ |
| args[1] = 0; /* LOWORD(dwReserved1) */ |
| args[0] = 0; /* wReserved2 */ |
| WOWCallback16Ex( 0, WCB16_REGS, sizeof(args), args, (DWORD *)&context ); |
| } |
| } |
| |
| /*********************************************************************** |
| * NE_DllProcessAttach |
| * |
| * Call the DllEntryPoint of all modules this one (recursively) |
| * depends on, according to the order in which they were loaded. |
| * |
| * Note that --as opposed to the PE module case-- there is no notion |
| * of 'module loaded into a process' for NE modules, and hence we |
| * have no place to store the fact that the DllEntryPoint of a |
| * given module was already called on behalf of this process (e.g. |
| * due to some earlier LoadLibrary16 call). |
| * |
| * Thus, we just call the DllEntryPoint twice in that case. Win9x |
| * appears to behave this way as well ... |
| * |
| * This routine must only be called with the Win16Lock held. |
| * |
| * FIXME: We should actually abort loading in case the DllEntryPoint |
| * returns FALSE ... |
| * |
| */ |
| |
| struct ne_init_list |
| { |
| int count; |
| int size; |
| NE_MODULE **module; |
| }; |
| |
| static void add_to_init_list( struct ne_init_list *list, NE_MODULE *hModule ) |
| { |
| NE_MODULE **newModule = NULL; |
| if ( list->count == list->size ) |
| { |
| int newSize = list->size + 128; |
| |
| if (list->module) |
| newModule = HeapReAlloc( GetProcessHeap(), 0, |
| list->module, newSize*sizeof(NE_MODULE *) ); |
| else |
| newModule = HeapAlloc( GetProcessHeap(), 0, |
| newSize*sizeof(NE_MODULE *) ); |
| if ( !newModule ) |
| { |
| FIXME_(dll)("Out of memory!\n"); |
| return; |
| } |
| |
| list->module = newModule; |
| list->size = newSize; |
| } |
| |
| list->module[list->count++] = hModule; |
| } |
| |
| static void free_init_list( struct ne_init_list *list ) |
| { |
| if ( list->module ) |
| { |
| HeapFree( GetProcessHeap(), 0, list->module ); |
| memset( list, 0, sizeof(*list) ); |
| } |
| } |
| |
| static void fill_init_list( struct ne_init_list *list, HMODULE16 hModule ) |
| { |
| NE_MODULE *pModule; |
| HMODULE16 *pModRef; |
| int i; |
| |
| if (!(pModule = NE_GetPtr( hModule ))) return; |
| assert( !(pModule->ne_flags & NE_FFLAGS_WIN32) ); |
| |
| /* Never add a module twice */ |
| for ( i = 0; i < list->count; i++ ) |
| if ( list->module[i] == pModule ) |
| return; |
| |
| /* Check for recursive call */ |
| if ( pModule->ne_flagsothers & 0x80 ) return; |
| |
| TRACE_(dll)("(%s) - START\n", NE_MODULE_NAME(pModule) ); |
| |
| /* Tag current module to prevent recursive loop */ |
| pModule->ne_flagsothers |= 0x80; |
| |
| /* Recursively attach all DLLs this one depends on */ |
| pModRef = (HMODULE16 *)((char *)pModule + pModule->ne_modtab); |
| for ( i = 0; i < pModule->ne_cmod; i++ ) |
| if ( pModRef[i] ) fill_init_list( list, pModRef[i] ); |
| |
| /* Add current module */ |
| add_to_init_list( list, pModule ); |
| |
| /* Remove recursion flag */ |
| pModule->ne_flagsothers &= ~0x80; |
| |
| TRACE_(dll)("(%s) - END\n", NE_MODULE_NAME(pModule) ); |
| } |
| |
| static void call_init_list( struct ne_init_list *list ) |
| { |
| int i; |
| for ( i = 0; i < list->count; i++ ) |
| NE_CallDllEntryPoint( list->module[i], DLL_PROCESS_ATTACH ); |
| } |
| |
| void NE_DllProcessAttach( HMODULE16 hModule ) |
| { |
| struct ne_init_list list; |
| memset( &list, 0, sizeof(list) ); |
| |
| fill_init_list( &list, hModule ); |
| call_init_list( &list ); |
| free_init_list( &list ); |
| } |
| |
| |
| /*********************************************************************** |
| * 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; |
| } |
| |
| /*********************************************************************** |
| * MyAlloc (KERNEL.668) Wine-specific export |
| * |
| * MyAlloc() function for self-loading apps. |
| */ |
| DWORD WINAPI MyAlloc16( 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_GetInstance |
| */ |
| HINSTANCE16 NE_GetInstance( NE_MODULE *pModule ) |
| { |
| if ( !pModule->ne_autodata ) |
| return pModule->self; |
| else |
| { |
| SEGTABLEENTRY *pSeg; |
| pSeg = NE_SEG_TABLE( pModule ) + pModule->ne_autodata - 1; |
| return pSeg->hSeg; |
| } |
| } |
| |
| /*********************************************************************** |
| * NE_CreateSegment |
| */ |
| BOOL NE_CreateSegment( NE_MODULE *pModule, int segnum ) |
| { |
| SEGTABLEENTRY *pSeg = NE_SEG_TABLE( pModule ) + segnum - 1; |
| int minsize; |
| unsigned char selflags; |
| |
| assert( !(pModule->ne_flags & NE_FFLAGS_WIN32) ); |
| |
| if ( segnum < 1 || segnum > pModule->ne_cseg ) |
| return FALSE; |
| |
| if ( (pModule->ne_flags & NE_FFLAGS_SELFLOAD) && segnum != 1 ) |
| return TRUE; /* selfloader allocates segment itself */ |
| |
| if ( (pSeg->flags & NE_SEGFLAGS_ALLOCATED) && segnum != pModule->ne_autodata ) |
| return TRUE; /* all but DGROUP only allocated once */ |
| |
| minsize = pSeg->minsize ? pSeg->minsize : 0x10000; |
| if ( segnum == SELECTOROF(pModule->ne_sssp) ) minsize += pModule->ne_stack; |
| if ( segnum == pModule->ne_autodata ) minsize += pModule->ne_heap; |
| |
| selflags = (pSeg->flags & NE_SEGFLAGS_DATA) ? WINE_LDT_FLAGS_DATA : WINE_LDT_FLAGS_CODE; |
| if (pSeg->flags & NE_SEGFLAGS_32BIT) selflags |= WINE_LDT_FLAGS_32BIT; |
| pSeg->hSeg = GLOBAL_Alloc( NE_Ne2MemFlags(pSeg->flags), minsize, pModule->self, selflags ); |
| if (!pSeg->hSeg) return FALSE; |
| |
| pSeg->flags |= NE_SEGFLAGS_ALLOCATED; |
| return TRUE; |
| } |
| |
| /*********************************************************************** |
| * NE_CreateAllSegments |
| */ |
| BOOL NE_CreateAllSegments( NE_MODULE *pModule ) |
| { |
| int i; |
| for ( i = 1; i <= pModule->ne_cseg; i++ ) |
| if ( !NE_CreateSegment( pModule, i ) ) |
| return FALSE; |
| |
| pModule->dgroup_entry = pModule->ne_autodata ? pModule->ne_segtab + |
| (pModule->ne_autodata - 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->ne_flags & NE_FFLAGS_LIBMODULE) != 0; |
| } |