4 * Copyright 1993 Robert J. Amstadt
5 * Copyright 1995 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "wine/port.h"
28 #include <sys/types.h>
36 #include "wine/winbase16.h"
38 #include "wine/library.h"
43 #include "stackframe.h"
44 #include "builtin16.h"
45 #include "wine/debug.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(fixup);
48 WINE_DECLARE_DEBUG_CHANNEL(dll);
49 WINE_DECLARE_DEBUG_CHANNEL(module);
52 * Relocation table entry
54 struct relocation_entry_s
56 BYTE address_type; /* Relocation address type */
57 BYTE relocation_type; /* Relocation type */
58 WORD offset; /* Offset in segment to fixup */
59 WORD target1; /* Target specification */
60 WORD target2; /* Target specification */
64 * Relocation address types
66 #define NE_RADDR_LOWBYTE 0
67 #define NE_RADDR_SELECTOR 2
68 #define NE_RADDR_POINTER32 3
69 #define NE_RADDR_OFFSET16 5
70 #define NE_RADDR_POINTER48 11
71 #define NE_RADDR_OFFSET32 13
76 #define NE_RELTYPE_INTERNAL 0
77 #define NE_RELTYPE_ORDINAL 1
78 #define NE_RELTYPE_NAME 2
79 #define NE_RELTYPE_OSFIXUP 3
80 #define NE_RELFLAG_ADDITIVE 4
82 /* Self-loading modules contain this structure in their first segment */
85 WORD version; /* Must be "A0" (0x3041) */
87 FARPROC16 BootApp; /* startup procedure */
88 FARPROC16 LoadAppSeg; /* procedure to load a segment */
90 FARPROC16 MyAlloc; /* memory allocation procedure,
91 * wine must write this field */
92 FARPROC16 EntryAddrProc;
93 FARPROC16 ExitProc; /* exit procedure */
95 FARPROC16 SetOwner; /* Set Owner procedure, exported by wine */
98 #define SEL(x) ((x)|1)
100 static void NE_FixupSegmentPrologs(NE_MODULE *pModule, WORD segnum);
103 /***********************************************************************
104 * NE_GetRelocAddrName
106 static const char *NE_GetRelocAddrName( BYTE addr_type, int additive )
108 switch(addr_type & 0x7f)
110 case NE_RADDR_LOWBYTE: return additive ? "BYTE add" : "BYTE";
111 case NE_RADDR_OFFSET16: return additive ? "OFFSET16 add" : "OFFSET16";
112 case NE_RADDR_POINTER32: return additive ? "POINTER32 add" : "POINTER32";
113 case NE_RADDR_SELECTOR: return additive ? "SELECTOR add" : "SELECTOR";
114 case NE_RADDR_POINTER48: return additive ? "POINTER48 add" : "POINTER48";
115 case NE_RADDR_OFFSET32: return additive ? "OFFSET32 add" : "OFFSET32";
121 /***********************************************************************
124 BOOL NE_LoadSegment( NE_MODULE *pModule, WORD segnum )
126 SEGTABLEENTRY *pSegTable, *pSeg;
127 HMODULE16 *pModuleTable;
128 WORD count, i, offset, next_offset;
130 FARPROC16 address = 0;
133 struct relocation_entry_s *rep, *reloc_entries;
139 int ordinal, additive;
142 pSegTable = NE_SEG_TABLE( pModule );
143 pSeg = pSegTable + segnum - 1;
145 if (pSeg->flags & NE_SEGFLAGS_LOADED)
147 /* self-loader ? -> already loaded it */
148 if (pModule->flags & NE_FFLAGS_SELFLOAD)
151 /* leave, except for DGROUP, as this may be the second instance */
152 if (segnum != pModule->dgroup)
156 if (!pSeg->filepos) return TRUE; /* No file image, just return */
158 pModuleTable = (HMODULE16 *)((char *)pModule + pModule->modref_table);
160 hf = NE_OpenFile( pModule );
161 TRACE_(module)("Loading segment %d, hSeg=%04x, flags=%04x\n",
162 segnum, pSeg->hSeg, pSeg->flags );
163 SetFilePointer( hf, pSeg->filepos << pModule->alignment, NULL, SEEK_SET );
164 if (pSeg->size) size = pSeg->size;
165 else size = pSeg->minsize ? pSeg->minsize : 0x10000;
166 mem = GlobalLock16(pSeg->hSeg);
167 if (pModule->flags & NE_FFLAGS_SELFLOAD && segnum > 1)
169 /* Implement self-loading segments */
170 SELFLOADHEADER *selfloadheader;
177 selfloadheader = MapSL( MAKESEGPTR(SEL(pSegTable->hSeg),0) );
178 oldstack = NtCurrentTeb()->cur_stack;
179 NtCurrentTeb()->cur_stack = MAKESEGPTR(pModule->self_loading_sel,
180 0xff00 - sizeof(STACK16FRAME));
182 TRACE_(dll)("CallLoadAppSegProc(hmodule=0x%04x,hf=%p,segnum=%d\n",
183 pModule->self,hf,segnum );
184 DuplicateHandle( GetCurrentProcess(), hf, GetCurrentProcess(), &hFile32,
185 0, FALSE, DUPLICATE_SAME_ACCESS );
186 hFile16 = Win32HandleToDosFileHandle( hFile32 );
187 args[2] = pModule->self;
190 WOWCallback16Ex( (DWORD)selfloadheader->LoadAppSeg, WCB16_PASCAL, sizeof(args), args, &ret );
191 pSeg->hSeg = LOWORD(ret);
192 TRACE_(dll)("Ret CallLoadAppSegProc: hSeg = 0x%04x\n", pSeg->hSeg);
193 _lclose16( hFile16 );
194 NtCurrentTeb()->cur_stack = oldstack;
196 else if (!(pSeg->flags & NE_SEGFLAGS_ITERATED))
197 ReadFile(hf, mem, size, &res, NULL);
200 The following bit of code for "iterated segments" was written without
201 any documentation on the format of these segments. It seems to work,
202 but may be missing something. If you have any doc please either send
203 it to me or fix the code yourself. gfm@werple.mira.net.au
205 char* buff = HeapAlloc(GetProcessHeap(), 0, size);
209 WARN_(dll)("Memory exausted!");
213 ReadFile(hf, buff, size, &res, NULL);
214 while(curr < buff + size) {
215 unsigned int rept = *((short*) curr)++;
216 unsigned int len = *((short*) curr)++;
217 for(; rept > 0; rept--) {
220 for(byte = 0; byte < len; byte++)
225 HeapFree(GetProcessHeap(), 0, buff);
228 pSeg->flags |= NE_SEGFLAGS_LOADED;
230 /* Perform exported function prolog fixups */
231 NE_FixupSegmentPrologs( pModule, segnum );
233 if (!(pSeg->flags & NE_SEGFLAGS_RELOC_DATA))
234 goto succeed; /* No relocation data, we are done */
236 ReadFile(hf, &count, sizeof(count), &res, NULL);
237 if (!count) goto succeed;
239 TRACE("Fixups for %.*s, segment %d, hSeg %04x\n",
240 *((BYTE *)pModule + pModule->name_table),
241 (char *)pModule + pModule->name_table + 1,
242 segnum, pSeg->hSeg );
244 reloc_entries = (struct relocation_entry_s *)HeapAlloc(GetProcessHeap(), 0, count * sizeof(struct relocation_entry_s));
245 if(reloc_entries == NULL) {
246 WARN("Not enough memory for relocation entries!");
249 if (!ReadFile( hf, reloc_entries, count * sizeof(struct relocation_entry_s), &res, NULL) ||
250 (res != count * sizeof(struct relocation_entry_s)))
252 WARN("Unable to read relocation information\n" );
257 * Go through the relocation table one entry at a time.
260 for (i = 0; i < count; i++, rep++)
263 * Get the target address corresponding to this entry.
266 /* If additive, there is no target chain list. Instead, add source
268 additive = rep->relocation_type & NE_RELFLAG_ADDITIVE;
269 rep->relocation_type &= 0x3;
271 switch (rep->relocation_type)
273 case NE_RELTYPE_ORDINAL:
274 module = pModuleTable[rep->target1-1];
275 ordinal = rep->target2;
276 address = NE_GetEntryPoint( module, ordinal );
279 NE_MODULE *pTarget = NE_GetPtr( module );
281 WARN_(module)("Module not found: %04x, reference %d of module %*.*s\n",
282 module, rep->target1,
283 *((BYTE *)pModule + pModule->name_table),
284 *((BYTE *)pModule + pModule->name_table),
285 (char *)pModule + pModule->name_table + 1 );
288 ERR("No implementation for %.*s.%d, setting to 0xdeadbeef\n",
289 *((BYTE *)pTarget + pTarget->name_table),
290 (char *)pTarget + pTarget->name_table + 1,
292 address = (FARPROC16)0xdeadbeef;
297 NE_MODULE *pTarget = NE_GetPtr( module );
298 TRACE("%d: %.*s.%d=%04x:%04x %s\n", i + 1,
299 *((BYTE *)pTarget + pTarget->name_table),
300 (char *)pTarget + pTarget->name_table + 1,
301 ordinal, HIWORD(address), LOWORD(address),
302 NE_GetRelocAddrName( rep->address_type, additive ) );
306 case NE_RELTYPE_NAME:
307 module = pModuleTable[rep->target1-1];
308 func_name = (char *)pModule + pModule->import_table + rep->target2;
309 memcpy( buffer, func_name+1, *func_name );
310 buffer[*func_name] = '\0';
312 ordinal = NE_GetOrdinal( module, func_name );
313 address = NE_GetEntryPoint( module, ordinal );
315 if (ERR_ON(fixup) && !address)
317 NE_MODULE *pTarget = NE_GetPtr( module );
318 ERR("No implementation for %.*s.%s, setting to 0xdeadbeef\n",
319 *((BYTE *)pTarget + pTarget->name_table),
320 (char *)pTarget + pTarget->name_table + 1, func_name );
322 if (!address) address = (FARPROC16) 0xdeadbeef;
325 NE_MODULE *pTarget = NE_GetPtr( module );
326 TRACE("%d: %.*s.%s=%04x:%04x %s\n", i + 1,
327 *((BYTE *)pTarget + pTarget->name_table),
328 (char *)pTarget + pTarget->name_table + 1,
329 func_name, HIWORD(address), LOWORD(address),
330 NE_GetRelocAddrName( rep->address_type, additive ) );
334 case NE_RELTYPE_INTERNAL:
335 if ((rep->target1 & 0xff) == 0xff)
337 address = NE_GetEntryPoint( pModule->self, rep->target2 );
341 address = (FARPROC16)MAKESEGPTR( SEL(pSegTable[rep->target1-1].hSeg), rep->target2 );
344 TRACE("%d: %04x:%04x %s\n",
345 i + 1, HIWORD(address), LOWORD(address),
346 NE_GetRelocAddrName( rep->address_type, additive ) );
349 case NE_RELTYPE_OSFIXUP:
350 /* Relocation type 7:
352 * These appear to be used as fixups for the Windows
353 * floating point emulator. Let's just ignore them and
354 * try to use the hardware floating point. Linux should
355 * successfully emulate the coprocessor if it doesn't
358 TRACE("%d: TYPE %d, OFFSET %04x, TARGET %04x %04x %s\n",
359 i + 1, rep->relocation_type, rep->offset,
360 rep->target1, rep->target2,
361 NE_GetRelocAddrName( rep->address_type, additive ) );
365 offset = rep->offset;
367 /* Apparently, high bit of address_type is sometimes set; */
368 /* we ignore it for now */
369 if (rep->address_type > NE_RADDR_OFFSET32)
372 GetModuleName16( pModule->self, module, sizeof(module) );
373 ERR("WARNING: module %s: unknown reloc addr type = 0x%02x. Please report.\n",
374 module, rep->address_type );
379 sp = MapSL( MAKESEGPTR( SEL(pSeg->hSeg), offset ) );
380 TRACE(" %04x:%04x\n", offset, *sp );
381 switch (rep->address_type & 0x7f)
383 case NE_RADDR_LOWBYTE:
384 *(BYTE *)sp += LOBYTE((int)address);
386 case NE_RADDR_OFFSET16:
387 *sp += LOWORD(address);
389 case NE_RADDR_POINTER32:
390 *sp += LOWORD(address);
391 *(sp+1) = HIWORD(address);
393 case NE_RADDR_SELECTOR:
394 /* Borland creates additive records with offset zero. Strange, but OK */
396 ERR("Additive selector to %04x.Please report\n",*sp);
398 *sp = HIWORD(address);
404 else /* non-additive fixup */
408 sp = MapSL( MAKESEGPTR( SEL(pSeg->hSeg), offset ) );
410 TRACE(" %04x:%04x\n", offset, *sp );
411 switch (rep->address_type & 0x7f)
413 case NE_RADDR_LOWBYTE:
414 *(BYTE *)sp = LOBYTE((int)address);
416 case NE_RADDR_OFFSET16:
417 *sp = LOWORD(address);
419 case NE_RADDR_POINTER32:
420 *(FARPROC16 *)sp = address;
422 case NE_RADDR_SELECTOR:
423 *sp = SELECTOROF(address);
428 if (next_offset == offset) break; /* avoid infinite loop */
429 if (next_offset >= GlobalSize16(pSeg->hSeg)) break;
430 offset = next_offset;
431 } while (offset != 0xffff);
435 HeapFree(GetProcessHeap(), 0, reloc_entries);
442 WARN("WARNING: %d: unknown ADDR TYPE %d, "
443 "TYPE %d, OFFSET %04x, TARGET %04x %04x\n",
444 i + 1, rep->address_type, rep->relocation_type,
445 rep->offset, rep->target1, rep->target2);
446 HeapFree(GetProcessHeap(), 0, reloc_entries);
454 /***********************************************************************
457 BOOL NE_LoadAllSegments( NE_MODULE *pModule )
460 SEGTABLEENTRY * pSegTable = (SEGTABLEENTRY *) NE_SEG_TABLE(pModule);
462 if (pModule->flags & NE_FFLAGS_SELFLOAD)
467 /* Handle self-loading modules */
468 SELFLOADHEADER *selfloadheader;
469 HMODULE16 mod = GetModuleHandle16("KERNEL");
473 TRACE_(module)("%.*s is a self-loading module!\n",
474 *((BYTE*)pModule + pModule->name_table),
475 (char *)pModule + pModule->name_table + 1);
476 if (!NE_LoadSegment( pModule, 1 )) return FALSE;
477 selfloadheader = MapSL( MAKESEGPTR(SEL(pSegTable->hSeg), 0) );
478 selfloadheader->EntryAddrProc = GetProcAddress16(mod,"EntryAddrProc");
479 selfloadheader->MyAlloc = GetProcAddress16(mod,"MyAlloc");
480 selfloadheader->SetOwner = GetProcAddress16(mod,"FarSetOwner");
481 sel = GlobalAlloc16( GMEM_ZEROINIT, 0xFF00 );
482 pModule->self_loading_sel = SEL(sel);
483 FarSetOwner16( sel, pModule->self );
484 oldstack = NtCurrentTeb()->cur_stack;
485 NtCurrentTeb()->cur_stack = MAKESEGPTR(pModule->self_loading_sel,
486 0xff00 - sizeof(STACK16FRAME) );
488 hf = NE_OpenFile(pModule);
489 hFile16 = Win32HandleToDosFileHandle( hf );
490 TRACE_(dll)("CallBootAppProc(hModule=0x%04x,hf=0x%04x)\n",
491 pModule->self,hFile16);
492 args[1] = pModule->self;
494 WOWCallback16Ex( (DWORD)selfloadheader->BootApp, WCB16_PASCAL, sizeof(args), args, NULL );
495 TRACE_(dll)("Return from CallBootAppProc\n");
497 NtCurrentTeb()->cur_stack = oldstack;
499 for (i = 2; i <= pModule->seg_count; i++)
500 if (!NE_LoadSegment( pModule, i )) return FALSE;
504 for (i = 1; i <= pModule->seg_count; i++)
505 if (!NE_LoadSegment( pModule, i )) return FALSE;
511 /***********************************************************************
512 * NE_FixupSegmentPrologs
514 * Fixup exported functions prologs of one segment
516 static void NE_FixupSegmentPrologs(NE_MODULE *pModule, WORD segnum)
518 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
521 WORD dgroup, num_entries, sel = SEL(pSegTable[segnum-1].hSeg);
524 TRACE("(%d);\n", segnum);
526 if (pSegTable[segnum-1].flags & NE_SEGFLAGS_DATA)
528 pSegTable[segnum-1].flags |= NE_SEGFLAGS_LOADED;
532 if (!pModule->dgroup) return;
534 if (!(dgroup = SEL(pSegTable[pModule->dgroup-1].hSeg))) return;
536 pSeg = MapSL( MAKESEGPTR(sel, 0) );
538 bundle = (ET_BUNDLE *)((BYTE *)pModule+pModule->entry_table);
541 TRACE("num_entries: %d, bundle: %p, next: %04x, pSeg: %p\n", bundle->last - bundle->first, bundle, bundle->next, pSeg);
542 if (!(num_entries = bundle->last - bundle->first))
544 entry = (ET_ENTRY *)((BYTE *)bundle+6);
545 while (num_entries--)
547 /*TRACE("entry: %p, entry->segnum: %d, entry->offs: %04x\n", entry, entry->segnum, entry->offs);*/
548 if (entry->segnum == segnum)
550 pFunc = ((BYTE *)pSeg+entry->offs);
551 TRACE("pFunc: %p, *(DWORD *)pFunc: %08lx, num_entries: %d\n", pFunc, *(DWORD *)pFunc, num_entries);
552 if (*(pFunc+2) == 0x90)
554 if (*(WORD *)pFunc == 0x581e) /* push ds, pop ax */
556 TRACE("patch %04x:%04x -> mov ax, ds\n", sel, entry->offs);
557 *(WORD *)pFunc = 0xd88c; /* mov ax, ds */
560 if (*(WORD *)pFunc == 0xd88c)
562 if ((entry->flags & 2)) /* public data ? */
564 TRACE("patch %04x:%04x -> mov ax, dgroup [%04x]\n", sel, entry->offs, dgroup);
565 *pFunc = 0xb8; /* mov ax, */
566 *(WORD *)(pFunc+1) = dgroup;
569 if ((pModule->flags & NE_FFLAGS_MULTIPLEDATA)
570 && (entry->flags & 1)) /* exported ? */
572 TRACE("patch %04x:%04x -> nop, nop\n", sel, entry->offs);
573 *(WORD *)pFunc = 0x9090; /* nop, nop */
580 } while ( (bundle->next)
581 && (bundle = ((ET_BUNDLE *)((BYTE *)pModule + bundle->next))) );
585 /***********************************************************************
586 * PatchCodeHandle (KERNEL.110)
588 * Needed for self-loading modules.
590 DWORD WINAPI PatchCodeHandle16(HANDLE16 hSeg)
593 WORD sel = SEL(hSeg);
594 NE_MODULE *pModule = NE_GetPtr(FarGetOwner16(sel));
595 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE(pModule);
597 TRACE_(module)("(%04x);\n", hSeg);
599 /* find the segment number of the module that belongs to hSeg */
600 for (segnum = 1; segnum <= pModule->seg_count; segnum++)
602 if (SEL(pSegTable[segnum-1].hSeg) == sel)
604 NE_FixupSegmentPrologs(pModule, segnum);
609 return MAKELONG(hSeg, sel);
613 /***********************************************************************
614 * NE_GetDLLInitParams
616 static VOID NE_GetDLLInitParams( NE_MODULE *pModule,
617 WORD *hInst, WORD *ds, WORD *heap )
619 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
621 if (!(pModule->flags & NE_FFLAGS_SINGLEDATA))
623 if (pModule->flags & NE_FFLAGS_MULTIPLEDATA || pModule->dgroup)
626 ERR_(dll)("Library is not marked SINGLEDATA\n");
629 else /* DATA NONE DLL */
635 else /* DATA SINGLE DLL */
637 if (pModule->dgroup) {
638 *ds = SEL(pSegTable[pModule->dgroup-1].hSeg);
639 *heap = pModule->heap_size;
641 else /* hmm, DLL has no dgroup,
642 but why has it NE_FFLAGS_SINGLEDATA set ?
643 Buggy DLL compiler ? */
650 *hInst = *ds ? GlobalHandle16(*ds) : pModule->self;
654 /***********************************************************************
657 * Call the DLL initialization code
659 static BOOL NE_InitDLL( NE_MODULE *pModule )
661 SEGTABLEENTRY *pSegTable;
662 WORD hInst, ds, heap;
665 pSegTable = NE_SEG_TABLE( pModule );
667 if (!(pModule->flags & NE_FFLAGS_LIBMODULE) ||
668 (pModule->flags & NE_FFLAGS_WIN32)) return TRUE; /*not a library*/
670 /* Call USER signal handler for Win3.1 compatibility. */
671 NE_CallUserSignalProc( pModule->self, USIG16_DLL_LOAD );
673 if (!pModule->cs) return TRUE; /* no initialization code */
676 /* Registers at initialization must be:
678 * di library instance
679 * ds data segment if any
680 * es:si command line (always 0)
683 memset( &context, 0, sizeof(context) );
685 NE_GetDLLInitParams( pModule, &hInst, &ds, &heap );
690 context.SegEs = ds; /* who knows ... */
691 context.SegFs = wine_get_fs();
692 context.SegGs = wine_get_gs();
693 context.SegCs = SEL(pSegTable[pModule->cs-1].hSeg);
694 context.Eip = pModule->ip;
695 context.Ebp = OFFSETOF(NtCurrentTeb()->cur_stack) + (WORD)&((STACK16FRAME*)0)->bp;
697 pModule->cs = 0; /* Don't initialize it twice */
698 TRACE_(dll)("Calling LibMain, cs:ip=%04lx:%04lx ds=%04lx di=%04x cx=%04x\n",
699 context.SegCs, context.Eip, context.SegDs,
700 LOWORD(context.Edi), LOWORD(context.Ecx) );
701 WOWCallback16Ex( 0, WCB16_REGS, 0, NULL, (DWORD *)&context );
705 /***********************************************************************
708 * Recursively initialize all DLLs (according to the order in which
709 * they where loaded).
711 void NE_InitializeDLLs( HMODULE16 hModule )
716 if (!(pModule = NE_GetPtr( hModule ))) return;
717 assert( !(pModule->flags & NE_FFLAGS_WIN32) );
719 if (pModule->dlls_to_init)
721 HGLOBAL16 to_init = pModule->dlls_to_init;
722 pModule->dlls_to_init = 0;
723 for (pDLL = (HMODULE16 *)GlobalLock16( to_init ); *pDLL; pDLL++)
725 NE_InitializeDLLs( *pDLL );
727 GlobalFree16( to_init );
729 NE_InitDLL( pModule );
733 /**********************************************************************
734 * NE_CallUserSignalProc
736 * According to "Undocumented Windows", the task signal proc is
737 * bypassed for module load/unload notifications, and the USER signal
738 * proc is called directly instead. This is what this function does.
740 typedef DWORD (WINAPI *pSignalProc)( HANDLE16 module, UINT16 code, UINT16 exit,
741 HINSTANCE16 inst, HQUEUE16 queue );
743 void NE_CallUserSignalProc( HMODULE16 hModule, UINT16 code )
746 HMODULE16 user = GetModuleHandle16("user.exe");
749 if ((proc = GetProcAddress16( user, "SignalProc" )))
751 /* USER is always a builtin dll */
752 pSignalProc sigproc = (pSignalProc)((ENTRYPOINT16 *)MapSL( (SEGPTR)proc ))->target;
753 sigproc( hModule, code, 0, 0, 0 );
758 /***********************************************************************
759 * NE_CallDllEntryPoint
761 * Call the DllEntryPoint of DLLs with subsystem >= 4.0
763 typedef DWORD (WINAPI *WinNEEntryProc)(DWORD,WORD,WORD,WORD,DWORD,WORD);
765 static void NE_CallDllEntryPoint( NE_MODULE *pModule, DWORD dwReason )
767 WORD hInst, ds, heap;
768 FARPROC16 entryPoint;
770 if (!(pModule->flags & NE_FFLAGS_LIBMODULE)) return;
771 if (!(pModule->flags & NE_FFLAGS_BUILTIN) && pModule->expected_version < 0x0400) return;
772 if (!(entryPoint = GetProcAddress16( pModule->self, "DllEntryPoint" ))) return;
774 NE_GetDLLInitParams( pModule, &hInst, &ds, &heap );
776 TRACE_(dll)( "Calling %s DllEntryPoint, cs:ip=%04x:%04x\n",
777 NE_MODULE_NAME( pModule ),
778 SELECTOROF(entryPoint), OFFSETOF(entryPoint) );
780 if ( pModule->flags & NE_FFLAGS_BUILTIN )
782 WinNEEntryProc entryProc = (WinNEEntryProc)((ENTRYPOINT16 *)MapSL( (SEGPTR)entryPoint ))->target;
784 entryProc( dwReason, hInst, ds, heap, 0, 0 );
791 memset( &context, 0, sizeof(context) );
793 context.SegEs = ds; /* who knows ... */
794 context.SegFs = wine_get_fs();
795 context.SegGs = wine_get_gs();
796 context.SegCs = HIWORD(entryPoint);
797 context.Eip = LOWORD(entryPoint);
798 context.Ebp = OFFSETOF( NtCurrentTeb()->cur_stack )
799 + (WORD)&((STACK16FRAME*)0)->bp;
801 args[7] = HIWORD(dwReason);
802 args[6] = LOWORD(dwReason);
806 args[2] = 0; /* HIWORD(dwReserved1) */
807 args[1] = 0; /* LOWORD(dwReserved1) */
808 args[0] = 0; /* wReserved2 */
809 WOWCallback16Ex( 0, WCB16_REGS, sizeof(args), args, (DWORD *)&context );
813 /***********************************************************************
814 * NE_DllProcessAttach
816 * Call the DllEntryPoint of all modules this one (recursively)
817 * depends on, according to the order in which they were loaded.
819 * Note that --as opposed to the PE module case-- there is no notion
820 * of 'module loaded into a process' for NE modules, and hence we
821 * have no place to store the fact that the DllEntryPoint of a
822 * given module was already called on behalf of this process (e.g.
823 * due to some earlier LoadLibrary16 call).
825 * Thus, we just call the DllEntryPoint twice in that case. Win9x
826 * appears to behave this way as well ...
828 * This routine must only be called with the Win16Lock held.
830 * FIXME: We should actually abort loading in case the DllEntryPoint
842 static void add_to_init_list( struct ne_init_list *list, NE_MODULE *hModule )
844 NE_MODULE **newModule = NULL;
845 if ( list->count == list->size )
847 int newSize = list->size + 128;
850 newModule = HeapReAlloc( GetProcessHeap(), 0,
851 list->module, newSize*sizeof(NE_MODULE *) );
853 newModule = HeapAlloc( GetProcessHeap(), 0,
854 newSize*sizeof(NE_MODULE *) );
857 FIXME_(dll)("Out of memory!");
861 list->module = newModule;
862 list->size = newSize;
865 list->module[list->count++] = hModule;
868 static void free_init_list( struct ne_init_list *list )
872 HeapFree( GetProcessHeap(), 0, list->module );
873 memset( list, 0, sizeof(*list) );
877 static void fill_init_list( struct ne_init_list *list, HMODULE16 hModule )
883 if (!(pModule = NE_GetPtr( hModule ))) return;
884 assert( !(pModule->flags & NE_FFLAGS_WIN32) );
886 /* Never add a module twice */
887 for ( i = 0; i < list->count; i++ )
888 if ( list->module[i] == pModule )
891 /* Check for recursive call */
892 if ( pModule->misc_flags & 0x80 ) return;
894 TRACE_(dll)("(%s) - START\n", NE_MODULE_NAME(pModule) );
896 /* Tag current module to prevent recursive loop */
897 pModule->misc_flags |= 0x80;
899 /* Recursively attach all DLLs this one depends on */
900 pModRef = (HMODULE16 *)((char *)pModule + pModule->modref_table);
901 for ( i = 0; i < pModule->modref_count; i++ )
902 if ( pModRef[i] ) fill_init_list( list, pModRef[i] );
904 /* Add current module */
905 add_to_init_list( list, pModule );
907 /* Remove recursion flag */
908 pModule->misc_flags &= ~0x80;
910 TRACE_(dll)("(%s) - END\n", NE_MODULE_NAME(pModule) );
913 static void call_init_list( struct ne_init_list *list )
916 for ( i = 0; i < list->count; i++ )
917 NE_CallDllEntryPoint( list->module[i], DLL_PROCESS_ATTACH );
920 void NE_DllProcessAttach( HMODULE16 hModule )
922 struct ne_init_list list;
923 memset( &list, 0, sizeof(list) );
925 fill_init_list( &list, hModule );
926 call_init_list( &list );
927 free_init_list( &list );
931 /***********************************************************************
934 * This function translates NE segment flags to GlobalAlloc flags
936 static WORD NE_Ne2MemFlags(WORD flags)
940 if (flags & NE_SEGFLAGS_DISCARDABLE)
941 memflags |= GMEM_DISCARDABLE;
942 if (flags & NE_SEGFLAGS_MOVEABLE ||
943 ( ! (flags & NE_SEGFLAGS_DATA) &&
944 ! (flags & NE_SEGFLAGS_LOADED) &&
945 ! (flags & NE_SEGFLAGS_ALLOCATED)
948 memflags |= GMEM_MOVEABLE;
949 memflags |= GMEM_ZEROINIT;
951 memflags = GMEM_ZEROINIT | GMEM_FIXED;
956 /***********************************************************************
957 * MyAlloc (KERNEL.668) Wine-specific export
959 * MyAlloc() function for self-loading apps.
961 DWORD WINAPI MyAlloc16( WORD wFlags, WORD wSize, WORD wElem )
963 WORD size = wSize << wElem;
966 if (wSize || (wFlags & NE_SEGFLAGS_MOVEABLE))
967 hMem = GlobalAlloc16( NE_Ne2MemFlags(wFlags), size);
969 if ( ((wFlags & 0x7) != 0x1) && /* DATA */
970 ((wFlags & 0x7) != 0x7) ) /* DATA|ALLOCATED|LOADED */
972 WORD hSel = SEL(hMem);
973 WORD access = SelectorAccessRights16(hSel,0,0);
975 access |= 2<<2; /* SEGMENT_CODE */
976 SelectorAccessRights16(hSel,1,access);
979 return MAKELONG( hMem, SEL(hMem) );
981 return MAKELONG( 0, hMem );
984 /***********************************************************************
987 HINSTANCE16 NE_GetInstance( NE_MODULE *pModule )
989 if ( !pModule->dgroup )
990 return pModule->self;
994 pSeg = NE_SEG_TABLE( pModule ) + pModule->dgroup - 1;
999 /***********************************************************************
1002 BOOL NE_CreateSegment( NE_MODULE *pModule, int segnum )
1004 SEGTABLEENTRY *pSeg = NE_SEG_TABLE( pModule ) + segnum - 1;
1006 unsigned char selflags;
1008 assert( !(pModule->flags & NE_FFLAGS_WIN32) );
1010 if ( segnum < 1 || segnum > pModule->seg_count )
1013 if ( (pModule->flags & NE_FFLAGS_SELFLOAD) && segnum != 1 )
1014 return TRUE; /* selfloader allocates segment itself */
1016 if ( (pSeg->flags & NE_SEGFLAGS_ALLOCATED) && segnum != pModule->dgroup )
1017 return TRUE; /* all but DGROUP only allocated once */
1019 minsize = pSeg->minsize ? pSeg->minsize : 0x10000;
1020 if ( segnum == pModule->ss ) minsize += pModule->stack_size;
1021 if ( segnum == pModule->dgroup ) minsize += pModule->heap_size;
1023 selflags = (pSeg->flags & NE_SEGFLAGS_DATA) ? WINE_LDT_FLAGS_DATA : WINE_LDT_FLAGS_CODE;
1024 if (pSeg->flags & NE_SEGFLAGS_32BIT) selflags |= WINE_LDT_FLAGS_32BIT;
1025 pSeg->hSeg = GLOBAL_Alloc( NE_Ne2MemFlags(pSeg->flags), minsize, pModule->self, selflags );
1026 if (!pSeg->hSeg) return FALSE;
1028 pSeg->flags |= NE_SEGFLAGS_ALLOCATED;
1032 /***********************************************************************
1033 * NE_CreateAllSegments
1035 BOOL NE_CreateAllSegments( NE_MODULE *pModule )
1038 for ( i = 1; i <= pModule->seg_count; i++ )
1039 if ( !NE_CreateSegment( pModule, i ) )
1042 pModule->dgroup_entry = pModule->dgroup ? pModule->seg_table +
1043 (pModule->dgroup - 1) * sizeof(SEGTABLEENTRY) : 0;
1048 /**********************************************************************
1049 * IsSharedSelector (KERNEL.345)
1051 BOOL16 WINAPI IsSharedSelector16( HANDLE16 selector )
1053 /* Check whether the selector belongs to a DLL */
1054 NE_MODULE *pModule = NE_GetPtr( selector );
1055 if (!pModule) return FALSE;
1056 return (pModule->flags & NE_FFLAGS_LIBMODULE) != 0;